summaryrefslogtreecommitdiffstats
path: root/src/3rdparty
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty')
-rw-r--r--src/3rdparty/VulkanMemoryAllocator.pri1
-rw-r--r--src/3rdparty/VulkanMemoryAllocator/LICENSE.txt19
-rw-r--r--src/3rdparty/VulkanMemoryAllocator/patches/0001-Avoid-compiler-warnings.patch402
-rw-r--r--src/3rdparty/VulkanMemoryAllocator/patches/0002-Fix-gcc8-warning.patch14
-rw-r--r--src/3rdparty/VulkanMemoryAllocator/patches/0003-Disable-srwlock-for-mingw.patch13
-rw-r--r--src/3rdparty/VulkanMemoryAllocator/qt_attribution.json16
-rw-r--r--src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h16790
-rw-r--r--src/3rdparty/double-conversion/README8
-rw-r--r--src/3rdparty/double-conversion/double-conversion.cc30
-rw-r--r--src/3rdparty/double-conversion/double-conversion.pri4
-rw-r--r--src/3rdparty/double-conversion/ieee.h4
-rw-r--r--src/3rdparty/double-conversion/include/double-conversion/utils.h15
-rw-r--r--src/3rdparty/double-conversion/qt_attribution.json4
-rw-r--r--src/3rdparty/forkfd/forkfd.c188
-rw-r--r--src/3rdparty/forkfd/forkfd.h5
-rw-r--r--src/3rdparty/forkfd/forkfd_atomic.h39
-rw-r--r--src/3rdparty/forkfd/forkfd_c11.h64
-rw-r--r--src/3rdparty/forkfd/forkfd_freebsd.c101
-rw-r--r--src/3rdparty/gradle/gradle/wrapper/gradle-wrapper.properties2
-rw-r--r--src/3rdparty/harfbuzz/src/harfbuzz-indic.cpp2
-rw-r--r--src/3rdparty/harfbuzz/tests/shaping/main.cpp4
-rw-r--r--src/3rdparty/libjpeg/jconfig.h2
-rw-r--r--src/3rdparty/libjpeg/jconfigint.h2
-rw-r--r--src/3rdparty/libjpeg/qt_attribution.json2
-rw-r--r--src/3rdparty/libjpeg/src/ChangeLog.md38
-rw-r--r--src/3rdparty/libjpeg/src/README.md28
-rw-r--r--src/3rdparty/libjpeg/src/jchuff.c6
-rw-r--r--src/3rdparty/libjpeg/src/jcmaster.c2
-rw-r--r--src/3rdparty/libjpeg/src/jdhuff.c10
-rw-r--r--src/3rdparty/libjpeg/src/jdmerge.c2
-rw-r--r--src/3rdparty/libjpeg/src/jdsample.c14
-rw-r--r--src/3rdparty/md4c.pri3
-rw-r--r--src/3rdparty/md4c/LICENSE.md22
-rw-r--r--src/3rdparty/md4c/md4c.c6173
-rw-r--r--src/3rdparty/md4c/md4c.h373
-rw-r--r--src/3rdparty/md4c/qt_attribution.json15
-rw-r--r--src/3rdparty/pcre2/patches/0001-fix-rtems-build-undefine-madvise.patch28
-rw-r--r--src/3rdparty/pcre2/src/sljit/sljitUtils.c2
-rw-r--r--src/3rdparty/sha3/brg_endian.h2
-rw-r--r--src/3rdparty/sqlite.pri8
-rw-r--r--src/3rdparty/sqlite/patches/0001-Fix-CVE-2019-16168-in-SQLite.patch42
-rw-r--r--src/3rdparty/sqlite/qt_attribution.json4
-rw-r--r--src/3rdparty/sqlite/sqlite3.c9186
-rw-r--r--src/3rdparty/sqlite/sqlite3.h81
44 files changed, 29588 insertions, 4182 deletions
diff --git a/src/3rdparty/VulkanMemoryAllocator.pri b/src/3rdparty/VulkanMemoryAllocator.pri
new file mode 100644
index 0000000000..7466200dfc
--- /dev/null
+++ b/src/3rdparty/VulkanMemoryAllocator.pri
@@ -0,0 +1 @@
+INCLUDEPATH += $$PWD/VulkanMemoryAllocator
diff --git a/src/3rdparty/VulkanMemoryAllocator/LICENSE.txt b/src/3rdparty/VulkanMemoryAllocator/LICENSE.txt
new file mode 100644
index 0000000000..dbfe253391
--- /dev/null
+++ b/src/3rdparty/VulkanMemoryAllocator/LICENSE.txt
@@ -0,0 +1,19 @@
+Copyright (c) 2017-2018 Advanced Micro Devices, Inc. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/src/3rdparty/VulkanMemoryAllocator/patches/0001-Avoid-compiler-warnings.patch b/src/3rdparty/VulkanMemoryAllocator/patches/0001-Avoid-compiler-warnings.patch
new file mode 100644
index 0000000000..f459db6c7a
--- /dev/null
+++ b/src/3rdparty/VulkanMemoryAllocator/patches/0001-Avoid-compiler-warnings.patch
@@ -0,0 +1,402 @@
+diff --git a/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h b/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h
+index a2f7a1b..fbe6f9e 100644
+--- a/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h
++++ b/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h
+@@ -3661,7 +3661,7 @@ static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
+ {
+ uint32_t* pDst = (uint32_t*)((char*)pData + offset);
+ const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
+- for(size_t i = 0; i < numberCount; ++i, ++pDst)
++ for(size_t i = 0; i != numberCount; ++i, ++pDst)
+ {
+ *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
+ }
+@@ -3671,7 +3671,7 @@ static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
+ {
+ const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
+ const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
+- for(size_t i = 0; i < numberCount; ++i, ++pSrc)
++ for(size_t i = 0; i != numberCount; ++i, ++pSrc)
+ {
+ if(*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
+ {
+@@ -3866,7 +3866,7 @@ public:
+ template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
+
+ T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
+- void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
++ void deallocate(T* p, size_t /*n*/) { VmaFree(m_pCallbacks, p); }
+
+ template<typename U>
+ bool operator==(const VmaStlAllocator<U>& rhs) const
+@@ -5214,7 +5214,7 @@ public:
+ virtual void FreeAtOffset(VkDeviceSize offset) = 0;
+
+ // Tries to resize (grow or shrink) space for given allocation, in place.
+- virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize) { return false; }
++ virtual bool ResizeAllocation(const VmaAllocation /*alloc*/, VkDeviceSize /*newSize*/) { return false; }
+
+ protected:
+ const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
+@@ -5574,7 +5574,7 @@ public:
+
+ virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
+
+- virtual VkResult CheckCorruption(const void* pBlockData) { return VK_ERROR_FEATURE_NOT_PRESENT; }
++ virtual VkResult CheckCorruption(const void* /*pBlockData*/) { return VK_ERROR_FEATURE_NOT_PRESENT; }
+
+ virtual void Alloc(
+ const VmaAllocationRequest& request,
+@@ -6133,7 +6133,7 @@ public:
+ bool overlappingMoveSupported);
+ virtual ~VmaDefragmentationAlgorithm_Fast();
+
+- virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) { ++m_AllocationCount; }
++ virtual void AddAllocation(VmaAllocation /*hAlloc*/, VkBool32* /*pChanged*/) { ++m_AllocationCount; }
+ virtual void AddAll() { m_AllAllocations = true; }
+
+ virtual VkResult Defragment(
+@@ -6318,7 +6318,7 @@ private:
+ // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors.
+ VmaBlockVector* const m_pBlockVector;
+ const uint32_t m_CurrFrameIndex;
+- const uint32_t m_AlgorithmFlags;
++ /*const uint32_t m_AlgorithmFlags;*/
+ // Owner of this object.
+ VmaDefragmentationAlgorithm* m_pAlgorithm;
+
+@@ -7073,6 +7073,7 @@ void VmaJsonWriter::BeginValue(bool isString)
+ if(currItem.type == COLLECTION_TYPE_OBJECT &&
+ currItem.valueCount % 2 == 0)
+ {
++ (void) isString;
+ VMA_ASSERT(isString);
+ }
+
+@@ -7660,7 +7661,9 @@ bool VmaBlockMetadata_Generic::Validate() const
+ }
+
+ // Margin required between allocations - every free space must be at least that large.
++#if VMA_DEBUG_MARGIN
+ VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN);
++#endif
+ }
+ else
+ {
+@@ -7806,6 +7809,7 @@ bool VmaBlockMetadata_Generic::CreateAllocationRequest(
+ {
+ VMA_ASSERT(allocSize > 0);
+ VMA_ASSERT(!upperAddress);
++ (void) upperAddress;
+ VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
+ VMA_ASSERT(pAllocationRequest != VMA_NULL);
+ VMA_HEAVY_ASSERT(Validate());
+@@ -8033,6 +8037,7 @@ void VmaBlockMetadata_Generic::Alloc(
+ VmaAllocation hAllocation)
+ {
+ VMA_ASSERT(!upperAddress);
++ (void) upperAddress;
+ VMA_ASSERT(request.item != m_Suballocations.end());
+ VmaSuballocation& suballoc = *request.item;
+ // Given suballocation is a free block.
+@@ -9609,7 +9614,7 @@ bool VmaBlockMetadata_Linear::CreateAllocationRequest(
+ bool upperAddress,
+ VmaSuballocationType allocType,
+ bool canMakeOtherLost,
+- uint32_t strategy,
++ uint32_t /*strategy*/,
+ VmaAllocationRequest* pAllocationRequest)
+ {
+ VMA_ASSERT(allocSize > 0);
+@@ -9651,10 +9656,12 @@ bool VmaBlockMetadata_Linear::CreateAllocationRequest(
+ // Apply VMA_DEBUG_MARGIN at the end.
+ if(VMA_DEBUG_MARGIN > 0)
+ {
++#if VMA_DEBUG_MARGIN
+ if(resultOffset < VMA_DEBUG_MARGIN)
+ {
+ return false;
+ }
++#endif
+ resultOffset -= VMA_DEBUG_MARGIN;
+ }
+
+@@ -10542,18 +10549,19 @@ void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const
+ #endif // #if VMA_STATS_STRING_ENABLED
+
+ bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
+- uint32_t currentFrameIndex,
+- uint32_t frameInUseCount,
++ uint32_t /*currentFrameIndex*/,
++ uint32_t /*frameInUseCount*/,
+ VkDeviceSize bufferImageGranularity,
+ VkDeviceSize allocSize,
+ VkDeviceSize allocAlignment,
+ bool upperAddress,
+ VmaSuballocationType allocType,
+- bool canMakeOtherLost,
+- uint32_t strategy,
++ bool /*canMakeOtherLost*/,
++ uint32_t /*strategy*/,
+ VmaAllocationRequest* pAllocationRequest)
+ {
+ VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
++ (void) upperAddress;
+
+ // Simple way to respect bufferImageGranularity. May be optimized some day.
+ // Whenever it might be an OPTIMAL image...
+@@ -10593,8 +10601,8 @@ bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
+ }
+
+ bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost(
+- uint32_t currentFrameIndex,
+- uint32_t frameInUseCount,
++ uint32_t /*currentFrameIndex*/,
++ uint32_t /*frameInUseCount*/,
+ VmaAllocationRequest* pAllocationRequest)
+ {
+ /*
+@@ -10604,7 +10612,7 @@ bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost(
+ return pAllocationRequest->itemsToMakeLostCount == 0;
+ }
+
+-uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
++uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t /*currentFrameIndex*/, uint32_t /*frameInUseCount*/)
+ {
+ /*
+ Lost allocations are not supported in buddy allocator at the moment.
+@@ -10615,9 +10623,9 @@ uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t currentFrameIndex,
+
+ void VmaBlockMetadata_Buddy::Alloc(
+ const VmaAllocationRequest& request,
+- VmaSuballocationType type,
++ VmaSuballocationType /*type*/,
+ VkDeviceSize allocSize,
+- bool upperAddress,
++ bool /*upperAddress*/,
+ VmaAllocation hAllocation)
+ {
+ const uint32_t targetLevel = AllocSizeToLevel(allocSize);
+@@ -10941,7 +10949,7 @@ void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, con
+ ////////////////////////////////////////////////////////////////////////////////
+ // class VmaDeviceMemoryBlock
+
+-VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) :
++VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator /*hAllocator*/) :
+ m_pMetadata(VMA_NULL),
+ m_MemoryTypeIndex(UINT32_MAX),
+ m_Id(0),
+@@ -11691,6 +11699,7 @@ VkResult VmaBlockVector::AllocatePage(
+ if(IsCorruptionDetectionEnabled())
+ {
+ VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size);
++ (void) res;
+ VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
+ }
+ return VK_SUCCESS;
+@@ -11729,6 +11738,7 @@ void VmaBlockVector::Free(
+ if(IsCorruptionDetectionEnabled())
+ {
+ VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
++ (void) res;
+ VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
+ }
+
+@@ -11894,6 +11904,7 @@ VkResult VmaBlockVector::AllocateFromBlock(
+ if(IsCorruptionDetectionEnabled())
+ {
+ VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
++ (void) res;
+ VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
+ }
+ return VK_SUCCESS;
+@@ -11903,7 +11914,8 @@ VkResult VmaBlockVector::AllocateFromBlock(
+
+ VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
+ {
+- VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
++ VkMemoryAllocateInfo allocInfo = {};
++ allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
+ allocInfo.allocationSize = blockSize;
+ VkDeviceMemory mem = VK_NULL_HANDLE;
+@@ -11991,7 +12003,8 @@ void VmaBlockVector::ApplyDefragmentationMovesCpu(
+ if(pDefragCtx->res == VK_SUCCESS)
+ {
+ const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
+- VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
++ VkMappedMemoryRange memRange = {};
++ memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
+
+ for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
+ {
+@@ -12076,7 +12089,8 @@ void VmaBlockVector::ApplyDefragmentationMovesGpu(
+
+ // Go over all blocks. Create and bind buffer for whole block if necessary.
+ {
+- VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
++ VkBufferCreateInfo bufCreateInfo = {};
++ bufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
+ VK_BUFFER_USAGE_TRANSFER_DST_BIT;
+
+@@ -12101,8 +12115,9 @@ void VmaBlockVector::ApplyDefragmentationMovesGpu(
+ // Go over all moves. Post data transfer commands to command buffer.
+ if(pDefragCtx->res == VK_SUCCESS)
+ {
+- const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
+- VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
++ /*const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
++ VkMappedMemoryRange memRange = {};
++ memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;*/
+
+ for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
+ {
+@@ -12435,10 +12450,10 @@ VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
+ VmaAllocator hAllocator,
+ VmaBlockVector* pBlockVector,
+ uint32_t currentFrameIndex,
+- bool overlappingMoveSupported) :
++ bool /*overlappingMoveSupported*/) :
+ VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
+- m_AllAllocations(false),
+ m_AllocationCount(0),
++ m_AllAllocations(false),
+ m_BytesMoved(0),
+ m_AllocationsMoved(0),
+ m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
+@@ -12813,7 +12828,7 @@ VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
+ size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
+ VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
+ VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
+- VkDeviceSize freeSpaceBlockSize = pFreeSpaceMetadata->GetSize();
++ /*VkDeviceSize freeSpaceBlockSize = pFreeSpaceMetadata->GetSize();*/
+
+ // Same block
+ if(freeSpaceInfoIndex == srcBlockInfoIndex)
+@@ -13098,7 +13113,7 @@ VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
+ VmaPool hCustomPool,
+ VmaBlockVector* pBlockVector,
+ uint32_t currFrameIndex,
+- uint32_t algorithmFlags) :
++ uint32_t /*algorithmFlags*/) :
+ res(VK_SUCCESS),
+ mutexLocked(false),
+ blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
+@@ -13106,7 +13121,7 @@ VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
+ m_hCustomPool(hCustomPool),
+ m_pBlockVector(pBlockVector),
+ m_CurrFrameIndex(currFrameIndex),
+- m_AlgorithmFlags(algorithmFlags),
++ /*m_AlgorithmFlags(algorithmFlags),*/
+ m_pAlgorithm(VMA_NULL),
+ m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
+ m_AllAllocations(false)
+@@ -14311,19 +14326,21 @@ VkResult VmaAllocator_T::AllocateDedicatedMemory(
+ bool map,
+ bool isUserDataString,
+ void* pUserData,
+- VkBuffer dedicatedBuffer,
+- VkImage dedicatedImage,
++ VkBuffer /*dedicatedBuffer*/,
++ VkImage /*dedicatedImage*/,
+ size_t allocationCount,
+ VmaAllocation* pAllocations)
+ {
+ VMA_ASSERT(allocationCount > 0 && pAllocations);
+
+- VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
++ VkMemoryAllocateInfo allocInfo = {};
++ allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ allocInfo.memoryTypeIndex = memTypeIndex;
+ allocInfo.allocationSize = size;
+
+ #if VMA_DEDICATED_ALLOCATION
+- VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };
++ VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = {};
++ dedicatedAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR;
+ if(m_UseKhrDedicatedAllocation)
+ {
+ if(dedicatedBuffer != VK_NULL_HANDLE)
+@@ -14341,7 +14358,7 @@ VkResult VmaAllocator_T::AllocateDedicatedMemory(
+ #endif // #if VMA_DEDICATED_ALLOCATION
+
+ size_t allocIndex;
+- VkResult res;
++ VkResult res = VK_SUCCESS;
+ for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
+ {
+ res = AllocateDedicatedMemoryPage(
+@@ -14460,12 +14477,15 @@ void VmaAllocator_T::GetBufferMemoryRequirements(
+ #if VMA_DEDICATED_ALLOCATION
+ if(m_UseKhrDedicatedAllocation)
+ {
+- VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };
++ VkBufferMemoryRequirementsInfo2KHR memReqInfo = {};
++ memReqInfo.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR;
+ memReqInfo.buffer = hBuffer;
+
+- VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
++ VkMemoryDedicatedRequirementsKHR memDedicatedReq = {};
++ memDedicatedReq.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR;
+
+- VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
++ VkMemoryRequirements2KHR memReq2 = {};
++ memReq2.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR;
+ memReq2.pNext = &memDedicatedReq;
+
+ (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
+@@ -14492,12 +14512,15 @@ void VmaAllocator_T::GetImageMemoryRequirements(
+ #if VMA_DEDICATED_ALLOCATION
+ if(m_UseKhrDedicatedAllocation)
+ {
+- VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };
++ VkImageMemoryRequirementsInfo2KHR memReqInfo = {};
++ memReqInfo.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR;
+ memReqInfo.image = hImage;
+
+- VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };
++ VkMemoryDedicatedRequirementsKHR memDedicatedReq = {};
++ memDedicatedReq.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR;
+
+- VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };
++ VkMemoryRequirements2KHR memReq2 = {};
++ memReq2.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR;
+ memReq2.pNext = &memDedicatedReq;
+
+ (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
+@@ -14734,7 +14757,7 @@ VkResult VmaAllocator_T::ResizeAllocation(
+ }
+ else
+ {
+- return VK_ERROR_OUT_OF_POOL_MEMORY;
++ return VkResult(-1000069000); // VK_ERROR_OUT_OF_POOL_MEMORY
+ }
+ default:
+ VMA_ASSERT(0);
+@@ -15000,6 +15023,7 @@ void VmaAllocator_T::DestroyPool(VmaPool pool)
+ {
+ VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
+ bool success = VmaVectorRemoveSorted<VmaPointerLess>(m_Pools, pool);
++ (void) success;
+ VMA_ASSERT(success && "Pool not found in Allocator.");
+ }
+
+@@ -15248,7 +15272,8 @@ void VmaAllocator_T::FlushOrInvalidateAllocation(
+
+ const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
+
+- VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
++ VkMappedMemoryRange memRange = {};
++ memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
+ memRange.memory = hAllocation->GetMemory();
+
+ switch(hAllocation->GetType())
+@@ -15321,6 +15346,7 @@ void VmaAllocator_T::FreeDedicatedMemory(VmaAllocation allocation)
+ AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
+ VMA_ASSERT(pDedicatedAllocations);
+ bool success = VmaVectorRemoveSorted<VmaPointerLess>(*pDedicatedAllocations, allocation);
++ (void) success;
+ VMA_ASSERT(success);
+ }
+
diff --git a/src/3rdparty/VulkanMemoryAllocator/patches/0002-Fix-gcc8-warning.patch b/src/3rdparty/VulkanMemoryAllocator/patches/0002-Fix-gcc8-warning.patch
new file mode 100644
index 0000000000..57a2f1a0f1
--- /dev/null
+++ b/src/3rdparty/VulkanMemoryAllocator/patches/0002-Fix-gcc8-warning.patch
@@ -0,0 +1,14 @@
+diff --git a/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h b/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h
+index fbe6f9e3e8..f043bdc289 100644
+--- a/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h
++++ b/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h
+@@ -12074,7 +12074,8 @@ void VmaBlockVector::ApplyDefragmentationMovesGpu(
+ const size_t blockCount = m_Blocks.size();
+
+ pDefragCtx->blockContexts.resize(blockCount);
+- memset(pDefragCtx->blockContexts.data(), 0, blockCount * sizeof(VmaBlockDefragmentationContext));
++ for (size_t i = 0; i < blockCount; ++i)
++ pDefragCtx->blockContexts[i] = VmaBlockDefragmentationContext();
+
+ // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
+ const size_t moveCount = moves.size();
diff --git a/src/3rdparty/VulkanMemoryAllocator/patches/0003-Disable-srwlock-for-mingw.patch b/src/3rdparty/VulkanMemoryAllocator/patches/0003-Disable-srwlock-for-mingw.patch
new file mode 100644
index 0000000000..ab7acfe40b
--- /dev/null
+++ b/src/3rdparty/VulkanMemoryAllocator/patches/0003-Disable-srwlock-for-mingw.patch
@@ -0,0 +1,13 @@
+diff --git a/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h b/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h
+index f043bdc289..2355de091f 100644
+--- a/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h
++++ b/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h
+@@ -3298,7 +3298,7 @@ void *aligned_alloc(size_t alignment, size_t size)
+ std::shared_mutex m_Mutex;
+ };
+ #define VMA_RW_MUTEX VmaRWMutex
+- #elif defined(_WIN32)
++ #elif defined(_WIN32) && !defined(__MINGW32__)
+ // Use SRWLOCK from WinAPI.
+ class VmaRWMutex
+ {
diff --git a/src/3rdparty/VulkanMemoryAllocator/qt_attribution.json b/src/3rdparty/VulkanMemoryAllocator/qt_attribution.json
new file mode 100644
index 0000000000..2548856ca7
--- /dev/null
+++ b/src/3rdparty/VulkanMemoryAllocator/qt_attribution.json
@@ -0,0 +1,16 @@
+[
+ {
+ "Id": "VulkanMemoryAllocator",
+ "Name": "Vulkan Memory Allocator",
+ "QDocModule": "qtrhi",
+ "Description": "Vulkan Memory Allocator",
+ "QtUsage": "Memory management for the Vulkan backend of QRhi.",
+
+ "Homepage": "https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator",
+ "Version": "2.2.0",
+ "License": "MIT License",
+ "LicenseId": "MIT",
+ "LicenseFile": "LICENSE.txt",
+ "Copyright": "Copyright (c) 2017-2018 Advanced Micro Devices, Inc. All rights reserved."
+ }
+]
diff --git a/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h b/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h
new file mode 100644
index 0000000000..2355de091f
--- /dev/null
+++ b/src/3rdparty/VulkanMemoryAllocator/vk_mem_alloc.h
@@ -0,0 +1,16790 @@
+//
+// Copyright (c) 2017-2018 Advanced Micro Devices, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H
+#define AMD_VULKAN_MEMORY_ALLOCATOR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \mainpage Vulkan Memory Allocator
+
+<b>Version 2.2.0</b> (2018-12-13)
+
+Copyright (c) 2017-2018 Advanced Micro Devices, Inc. All rights reserved. \n
+License: MIT
+
+Documentation of all members: vk_mem_alloc.h
+
+\section main_table_of_contents Table of contents
+
+- <b>User guide</b>
+ - \subpage quick_start
+ - [Project setup](@ref quick_start_project_setup)
+ - [Initialization](@ref quick_start_initialization)
+ - [Resource allocation](@ref quick_start_resource_allocation)
+ - \subpage choosing_memory_type
+ - [Usage](@ref choosing_memory_type_usage)
+ - [Required and preferred flags](@ref choosing_memory_type_required_preferred_flags)
+ - [Explicit memory types](@ref choosing_memory_type_explicit_memory_types)
+ - [Custom memory pools](@ref choosing_memory_type_custom_memory_pools)
+ - \subpage memory_mapping
+ - [Mapping functions](@ref memory_mapping_mapping_functions)
+ - [Persistently mapped memory](@ref memory_mapping_persistently_mapped_memory)
+ - [Cache control](@ref memory_mapping_cache_control)
+ - [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable)
+ - \subpage custom_memory_pools
+ - [Choosing memory type index](@ref custom_memory_pools_MemTypeIndex)
+ - [Linear allocation algorithm](@ref linear_algorithm)
+ - [Free-at-once](@ref linear_algorithm_free_at_once)
+ - [Stack](@ref linear_algorithm_stack)
+ - [Double stack](@ref linear_algorithm_double_stack)
+ - [Ring buffer](@ref linear_algorithm_ring_buffer)
+ - [Buddy allocation algorithm](@ref buddy_algorithm)
+ - \subpage defragmentation
+ - [Defragmenting CPU memory](@ref defragmentation_cpu)
+ - [Defragmenting GPU memory](@ref defragmentation_gpu)
+ - [Additional notes](@ref defragmentation_additional_notes)
+ - [Writing custom allocation algorithm](@ref defragmentation_custom_algorithm)
+ - \subpage lost_allocations
+ - \subpage statistics
+ - [Numeric statistics](@ref statistics_numeric_statistics)
+ - [JSON dump](@ref statistics_json_dump)
+ - \subpage allocation_annotation
+ - [Allocation user data](@ref allocation_user_data)
+ - [Allocation names](@ref allocation_names)
+ - \subpage debugging_memory_usage
+ - [Memory initialization](@ref debugging_memory_usage_initialization)
+ - [Margins](@ref debugging_memory_usage_margins)
+ - [Corruption detection](@ref debugging_memory_usage_corruption_detection)
+ - \subpage record_and_replay
+- \subpage usage_patterns
+ - [Simple patterns](@ref usage_patterns_simple)
+ - [Advanced patterns](@ref usage_patterns_advanced)
+- \subpage configuration
+ - [Pointers to Vulkan functions](@ref config_Vulkan_functions)
+ - [Custom host memory allocator](@ref custom_memory_allocator)
+ - [Device memory allocation callbacks](@ref allocation_callbacks)
+ - [Device heap memory limit](@ref heap_memory_limit)
+ - \subpage vk_khr_dedicated_allocation
+- \subpage general_considerations
+ - [Thread safety](@ref general_considerations_thread_safety)
+ - [Validation layer warnings](@ref general_considerations_validation_layer_warnings)
+ - [Allocation algorithm](@ref general_considerations_allocation_algorithm)
+ - [Features not supported](@ref general_considerations_features_not_supported)
+
+\section main_see_also See also
+
+- [Product page on GPUOpen](https://gpuopen.com/gaming-product/vulkan-memory-allocator/)
+- [Source repository on GitHub](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator)
+
+
+
+
+\page quick_start Quick start
+
+\section quick_start_project_setup Project setup
+
+Vulkan Memory Allocator comes in form of a single header file.
+You don't need to build it as a separate library project.
+You can add this file directly to your project and submit it to code repository next to your other source files.
+
+"Single header" doesn't mean that everything is contained in C/C++ declarations,
+like it tends to be in case of inline functions or C++ templates.
+It means that implementation is bundled with interface in a single file and needs to be extracted using preprocessor macro.
+If you don't do it properly, you will get linker errors.
+
+To do it properly:
+
+-# Include "vk_mem_alloc.h" file in each CPP file where you want to use the library.
+ This includes declarations of all members of the library.
+-# In exacly one CPP file define following macro before this include.
+ It enables also internal definitions.
+
+\code
+#define VMA_IMPLEMENTATION
+#include "vk_mem_alloc.h"
+\endcode
+
+It may be a good idea to create dedicated CPP file just for this purpose.
+
+Note on language: This library is written in C++, but has C-compatible interface.
+Thus you can include and use vk_mem_alloc.h in C or C++ code, but full
+implementation with `VMA_IMPLEMENTATION` macro must be compiled as C++, NOT as C.
+
+Please note that this library includes header `<vulkan/vulkan.h>`, which in turn
+includes `<windows.h>` on Windows. If you need some specific macros defined
+before including these headers (like `WIN32_LEAN_AND_MEAN` or
+`WINVER` for Windows, `VK_USE_PLATFORM_WIN32_KHR` for Vulkan), you must define
+them before every `#include` of this library.
+
+
+\section quick_start_initialization Initialization
+
+At program startup:
+
+-# Initialize Vulkan to have `VkPhysicalDevice` and `VkDevice` object.
+-# Fill VmaAllocatorCreateInfo structure and create #VmaAllocator object by
+ calling vmaCreateAllocator().
+
+\code
+VmaAllocatorCreateInfo allocatorInfo = {};
+allocatorInfo.physicalDevice = physicalDevice;
+allocatorInfo.device = device;
+
+VmaAllocator allocator;
+vmaCreateAllocator(&allocatorInfo, &allocator);
+\endcode
+
+\section quick_start_resource_allocation Resource allocation
+
+When you want to create a buffer or image:
+
+-# Fill `VkBufferCreateInfo` / `VkImageCreateInfo` structure.
+-# Fill VmaAllocationCreateInfo structure.
+-# Call vmaCreateBuffer() / vmaCreateImage() to get `VkBuffer`/`VkImage` with memory
+ already allocated and bound to it.
+
+\code
+VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
+bufferInfo.size = 65536;
+bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
+
+VmaAllocationCreateInfo allocInfo = {};
+allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
+
+VkBuffer buffer;
+VmaAllocation allocation;
+vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
+\endcode
+
+Don't forget to destroy your objects when no longer needed:
+
+\code
+vmaDestroyBuffer(allocator, buffer, allocation);
+vmaDestroyAllocator(allocator);
+\endcode
+
+
+\page choosing_memory_type Choosing memory type
+
+Physical devices in Vulkan support various combinations of memory heaps and
+types. Help with choosing correct and optimal memory type for your specific
+resource is one of the key features of this library. You can use it by filling
+appropriate members of VmaAllocationCreateInfo structure, as described below.
+You can also combine multiple methods.
+
+-# If you just want to find memory type index that meets your requirements, you
+ can use function vmaFindMemoryTypeIndex().
+-# If you want to allocate a region of device memory without association with any
+ specific image or buffer, you can use function vmaAllocateMemory(). Usage of
+ this function is not recommended and usually not needed.
+-# If you already have a buffer or an image created, you want to allocate memory
+ for it and then you will bind it yourself, you can use function
+ vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage().
+ For binding you should use functions: vmaBindBufferMemory(), vmaBindImageMemory().
+-# If you want to create a buffer or an image, allocate memory for it and bind
+ them together, all in one call, you can use function vmaCreateBuffer(),
+ vmaCreateImage(). This is the recommended way to use this library.
+
+When using 3. or 4., the library internally queries Vulkan for memory types
+supported for that buffer or image (function `vkGetBufferMemoryRequirements()`)
+and uses only one of these types.
+
+If no memory type can be found that meets all the requirements, these functions
+return `VK_ERROR_FEATURE_NOT_PRESENT`.
+
+You can leave VmaAllocationCreateInfo structure completely filled with zeros.
+It means no requirements are specified for memory type.
+It is valid, although not very useful.
+
+\section choosing_memory_type_usage Usage
+
+The easiest way to specify memory requirements is to fill member
+VmaAllocationCreateInfo::usage using one of the values of enum #VmaMemoryUsage.
+It defines high level, common usage types.
+For more details, see description of this enum.
+
+For example, if you want to create a uniform buffer that will be filled using
+transfer only once or infrequently and used for rendering every frame, you can
+do it using following code:
+
+\code
+VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
+bufferInfo.size = 65536;
+bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
+
+VmaAllocationCreateInfo allocInfo = {};
+allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
+
+VkBuffer buffer;
+VmaAllocation allocation;
+vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
+\endcode
+
+\section choosing_memory_type_required_preferred_flags Required and preferred flags
+
+You can specify more detailed requirements by filling members
+VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags
+with a combination of bits from enum `VkMemoryPropertyFlags`. For example,
+if you want to create a buffer that will be persistently mapped on host (so it
+must be `HOST_VISIBLE`) and preferably will also be `HOST_COHERENT` and `HOST_CACHED`,
+use following code:
+
+\code
+VmaAllocationCreateInfo allocInfo = {};
+allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
+allocInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
+allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
+
+VkBuffer buffer;
+VmaAllocation allocation;
+vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
+\endcode
+
+A memory type is chosen that has all the required flags and as many preferred
+flags set as possible.
+
+If you use VmaAllocationCreateInfo::usage, it is just internally converted to
+a set of required and preferred flags.
+
+\section choosing_memory_type_explicit_memory_types Explicit memory types
+
+If you inspected memory types available on the physical device and you have
+a preference for memory types that you want to use, you can fill member
+VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set
+means that a memory type with that index is allowed to be used for the
+allocation. Special value 0, just like `UINT32_MAX`, means there are no
+restrictions to memory type index.
+
+Please note that this member is NOT just a memory type index.
+Still you can use it to choose just one, specific memory type.
+For example, if you already determined that your buffer should be created in
+memory type 2, use following code:
+
+\code
+uint32_t memoryTypeIndex = 2;
+
+VmaAllocationCreateInfo allocInfo = {};
+allocInfo.memoryTypeBits = 1u << memoryTypeIndex;
+
+VkBuffer buffer;
+VmaAllocation allocation;
+vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);
+\endcode
+
+\section choosing_memory_type_custom_memory_pools Custom memory pools
+
+If you allocate from custom memory pool, all the ways of specifying memory
+requirements described above are not applicable and the aforementioned members
+of VmaAllocationCreateInfo structure are ignored. Memory type is selected
+explicitly when creating the pool and then used to make all the allocations from
+that pool. For further details, see \ref custom_memory_pools.
+
+
+\page memory_mapping Memory mapping
+
+To "map memory" in Vulkan means to obtain a CPU pointer to `VkDeviceMemory`,
+to be able to read from it or write to it in CPU code.
+Mapping is possible only of memory allocated from a memory type that has
+`VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag.
+Functions `vkMapMemory()`, `vkUnmapMemory()` are designed for this purpose.
+You can use them directly with memory allocated by this library,
+but it is not recommended because of following issue:
+Mapping the same `VkDeviceMemory` block multiple times is illegal - only one mapping at a time is allowed.
+This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan.
+Because of this, Vulkan Memory Allocator provides following facilities:
+
+\section memory_mapping_mapping_functions Mapping functions
+
+The library provides following functions for mapping of a specific #VmaAllocation: vmaMapMemory(), vmaUnmapMemory().
+They are safer and more convenient to use than standard Vulkan functions.
+You can map an allocation multiple times simultaneously - mapping is reference-counted internally.
+You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block.
+The way it's implemented is that the library always maps entire memory block, not just region of the allocation.
+For further details, see description of vmaMapMemory() function.
+Example:
+
+\code
+// Having these objects initialized:
+
+struct ConstantBuffer
+{
+ ...
+};
+ConstantBuffer constantBufferData;
+
+VmaAllocator allocator;
+VkBuffer constantBuffer;
+VmaAllocation constantBufferAllocation;
+
+// You can map and fill your buffer using following code:
+
+void* mappedData;
+vmaMapMemory(allocator, constantBufferAllocation, &mappedData);
+memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
+vmaUnmapMemory(allocator, constantBufferAllocation);
+\endcode
+
+When mapping, you may see a warning from Vulkan validation layer similar to this one:
+
+<i>Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used.</i>
+
+It happens because the library maps entire `VkDeviceMemory` block, where different
+types of images and buffers may end up together, especially on GPUs with unified memory like Intel.
+You can safely ignore it if you are sure you access only memory of the intended
+object that you wanted to map.
+
+
+\section memory_mapping_persistently_mapped_memory Persistently mapped memory
+
+Kepping your memory persistently mapped is generally OK in Vulkan.
+You don't need to unmap it before using its data on the GPU.
+The library provides a special feature designed for that:
+Allocations made with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in
+VmaAllocationCreateInfo::flags stay mapped all the time,
+so you can just access CPU pointer to it any time
+without a need to call any "map" or "unmap" function.
+Example:
+
+\code
+VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
+bufCreateInfo.size = sizeof(ConstantBuffer);
+bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
+
+VmaAllocationCreateInfo allocCreateInfo = {};
+allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
+allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
+
+VkBuffer buf;
+VmaAllocation alloc;
+VmaAllocationInfo allocInfo;
+vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
+
+// Buffer is already mapped. You can access its memory.
+memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
+\endcode
+
+There are some exceptions though, when you should consider mapping memory only for a short period of time:
+
+- When operating system is Windows 7 or 8.x (Windows 10 is not affected because it uses WDDM2),
+ device is discrete AMD GPU,
+ and memory type is the special 256 MiB pool of `DEVICE_LOCAL + HOST_VISIBLE` memory
+ (selected when you use #VMA_MEMORY_USAGE_CPU_TO_GPU),
+ then whenever a memory block allocated from this memory type stays mapped
+ for the time of any call to `vkQueueSubmit()` or `vkQueuePresentKHR()`, this
+ block is migrated by WDDM to system RAM, which degrades performance. It doesn't
+ matter if that particular memory block is actually used by the command buffer
+ being submitted.
+- On Mac/MoltenVK there is a known bug - [Issue #175](https://github.com/KhronosGroup/MoltenVK/issues/175)
+ which requires unmapping before GPU can see updated texture.
+- Keeping many large memory blocks mapped may impact performance or stability of some debugging tools.
+
+\section memory_mapping_cache_control Cache control
+
+Memory in Vulkan doesn't need to be unmapped before using it on GPU,
+but unless a memory types has `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set,
+you need to manually invalidate cache before reading of mapped pointer
+and flush cache after writing to mapped pointer.
+Vulkan provides following functions for this purpose `vkFlushMappedMemoryRanges()`,
+`vkInvalidateMappedMemoryRanges()`, but this library provides more convenient
+functions that refer to given allocation object: vmaFlushAllocation(),
+vmaInvalidateAllocation().
+
+Regions of memory specified for flush/invalidate must be aligned to
+`VkPhysicalDeviceLimits::nonCoherentAtomSize`. This is automatically ensured by the library.
+In any memory type that is `HOST_VISIBLE` but not `HOST_COHERENT`, all allocations
+within blocks are aligned to this value, so their offsets are always multiply of
+`nonCoherentAtomSize` and two different allocations never share same "line" of this size.
+
+Please note that memory allocated with #VMA_MEMORY_USAGE_CPU_ONLY is guaranteed to be `HOST_COHERENT`.
+
+Also, Windows drivers from all 3 PC GPU vendors (AMD, Intel, NVIDIA)
+currently provide `HOST_COHERENT` flag on all memory types that are
+`HOST_VISIBLE`, so on this platform you may not need to bother.
+
+\section memory_mapping_finding_if_memory_mappable Finding out if memory is mappable
+
+It may happen that your allocation ends up in memory that is `HOST_VISIBLE` (available for mapping)
+despite it wasn't explicitly requested.
+For example, application may work on integrated graphics with unified memory (like Intel) or
+allocation from video memory might have failed, so the library chose system memory as fallback.
+
+You can detect this case and map such allocation to access its memory on CPU directly,
+instead of launching a transfer operation.
+In order to do that: inspect `allocInfo.memoryType`, call vmaGetMemoryTypeProperties(),
+and look for `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag in properties of that memory type.
+
+\code
+VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
+bufCreateInfo.size = sizeof(ConstantBuffer);
+bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
+
+VmaAllocationCreateInfo allocCreateInfo = {};
+allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
+allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
+
+VkBuffer buf;
+VmaAllocation alloc;
+VmaAllocationInfo allocInfo;
+vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
+
+VkMemoryPropertyFlags memFlags;
+vmaGetMemoryTypeProperties(allocator, allocInfo.memoryType, &memFlags);
+if((memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
+{
+ // Allocation ended up in mappable memory. You can map it and access it directly.
+ void* mappedData;
+ vmaMapMemory(allocator, alloc, &mappedData);
+ memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));
+ vmaUnmapMemory(allocator, alloc);
+}
+else
+{
+ // Allocation ended up in non-mappable memory.
+ // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
+}
+\endcode
+
+You can even use #VMA_ALLOCATION_CREATE_MAPPED_BIT flag while creating allocations
+that are not necessarily `HOST_VISIBLE` (e.g. using #VMA_MEMORY_USAGE_GPU_ONLY).
+If the allocation ends up in memory type that is `HOST_VISIBLE`, it will be persistently mapped and you can use it directly.
+If not, the flag is just ignored.
+Example:
+
+\code
+VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
+bufCreateInfo.size = sizeof(ConstantBuffer);
+bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
+
+VmaAllocationCreateInfo allocCreateInfo = {};
+allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
+allocCreateInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
+
+VkBuffer buf;
+VmaAllocation alloc;
+VmaAllocationInfo allocInfo;
+vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
+
+if(allocInfo.pUserData != nullptr)
+{
+ // Allocation ended up in mappable memory.
+ // It's persistently mapped. You can access it directly.
+ memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));
+}
+else
+{
+ // Allocation ended up in non-mappable memory.
+ // You need to create CPU-side buffer in VMA_MEMORY_USAGE_CPU_ONLY and make a transfer.
+}
+\endcode
+
+
+\page custom_memory_pools Custom memory pools
+
+A memory pool contains a number of `VkDeviceMemory` blocks.
+The library automatically creates and manages default pool for each memory type available on the device.
+Default memory pool automatically grows in size.
+Size of allocated blocks is also variable and managed automatically.
+
+You can create custom pool and allocate memory out of it.
+It can be useful if you want to:
+
+- Keep certain kind of allocations separate from others.
+- Enforce particular, fixed size of Vulkan memory blocks.
+- Limit maximum amount of Vulkan memory allocated for that pool.
+- Reserve minimum or fixed amount of Vulkan memory always preallocated for that pool.
+
+To use custom memory pools:
+
+-# Fill VmaPoolCreateInfo structure.
+-# Call vmaCreatePool() to obtain #VmaPool handle.
+-# When making an allocation, set VmaAllocationCreateInfo::pool to this handle.
+ You don't need to specify any other parameters of this structure, like `usage`.
+
+Example:
+
+\code
+// Create a pool that can have at most 2 blocks, 128 MiB each.
+VmaPoolCreateInfo poolCreateInfo = {};
+poolCreateInfo.memoryTypeIndex = ...
+poolCreateInfo.blockSize = 128ull * 1024 * 1024;
+poolCreateInfo.maxBlockCount = 2;
+
+VmaPool pool;
+vmaCreatePool(allocator, &poolCreateInfo, &pool);
+
+// Allocate a buffer out of it.
+VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
+bufCreateInfo.size = 1024;
+bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
+
+VmaAllocationCreateInfo allocCreateInfo = {};
+allocCreateInfo.pool = pool;
+
+VkBuffer buf;
+VmaAllocation alloc;
+VmaAllocationInfo allocInfo;
+vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);
+\endcode
+
+You have to free all allocations made from this pool before destroying it.
+
+\code
+vmaDestroyBuffer(allocator, buf, alloc);
+vmaDestroyPool(allocator, pool);
+\endcode
+
+\section custom_memory_pools_MemTypeIndex Choosing memory type index
+
+When creating a pool, you must explicitly specify memory type index.
+To find the one suitable for your buffers or images, you can use helper functions
+vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo().
+You need to provide structures with example parameters of buffers or images
+that you are going to create in that pool.
+
+\code
+VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
+exampleBufCreateInfo.size = 1024; // Whatever.
+exampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; // Change if needed.
+
+VmaAllocationCreateInfo allocCreateInfo = {};
+allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; // Change if needed.
+
+uint32_t memTypeIndex;
+vmaFindMemoryTypeIndexForBufferInfo(allocator, &exampleBufCreateInfo, &allocCreateInfo, &memTypeIndex);
+
+VmaPoolCreateInfo poolCreateInfo = {};
+poolCreateInfo.memoryTypeIndex = memTypeIndex;
+// ...
+\endcode
+
+When creating buffers/images allocated in that pool, provide following parameters:
+
+- `VkBufferCreateInfo`: Prefer to pass same parameters as above.
+ Otherwise you risk creating resources in a memory type that is not suitable for them, which may result in undefined behavior.
+ Using different `VK_BUFFER_USAGE_` flags may work, but you shouldn't create images in a pool intended for buffers
+ or the other way around.
+- VmaAllocationCreateInfo: You don't need to pass same parameters. Fill only `pool` member.
+ Other members are ignored anyway.
+
+\section linear_algorithm Linear allocation algorithm
+
+Each Vulkan memory block managed by this library has accompanying metadata that
+keeps track of used and unused regions. By default, the metadata structure and
+algorithm tries to find best place for new allocations among free regions to
+optimize memory usage. This way you can allocate and free objects in any order.
+
+![Default allocation algorithm](../gfx/Linear_allocator_1_algo_default.png)
+
+Sometimes there is a need to use simpler, linear allocation algorithm. You can
+create custom pool that uses such algorithm by adding flag
+#VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
+#VmaPool object. Then an alternative metadata management is used. It always
+creates new allocations after last one and doesn't reuse free regions after
+allocations freed in the middle. It results in better allocation performance and
+less memory consumed by metadata.
+
+![Linear allocation algorithm](../gfx/Linear_allocator_2_algo_linear.png)
+
+With this one flag, you can create a custom pool that can be used in many ways:
+free-at-once, stack, double stack, and ring buffer. See below for details.
+
+\subsection linear_algorithm_free_at_once Free-at-once
+
+In a pool that uses linear algorithm, you still need to free all the allocations
+individually, e.g. by using vmaFreeMemory() or vmaDestroyBuffer(). You can free
+them in any order. New allocations are always made after last one - free space
+in the middle is not reused. However, when you release all the allocation and
+the pool becomes empty, allocation starts from the beginning again. This way you
+can use linear algorithm to speed up creation of allocations that you are going
+to release all at once.
+
+![Free-at-once](../gfx/Linear_allocator_3_free_at_once.png)
+
+This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
+value that allows multiple memory blocks.
+
+\subsection linear_algorithm_stack Stack
+
+When you free an allocation that was created last, its space can be reused.
+Thanks to this, if you always release allocations in the order opposite to their
+creation (LIFO - Last In First Out), you can achieve behavior of a stack.
+
+![Stack](../gfx/Linear_allocator_4_stack.png)
+
+This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
+value that allows multiple memory blocks.
+
+\subsection linear_algorithm_double_stack Double stack
+
+The space reserved by a custom pool with linear algorithm may be used by two
+stacks:
+
+- First, default one, growing up from offset 0.
+- Second, "upper" one, growing down from the end towards lower offsets.
+
+To make allocation from upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT
+to VmaAllocationCreateInfo::flags.
+
+![Double stack](../gfx/Linear_allocator_7_double_stack.png)
+
+Double stack is available only in pools with one memory block -
+VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
+
+When the two stacks' ends meet so there is not enough space between them for a
+new allocation, such allocation fails with usual
+`VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
+
+\subsection linear_algorithm_ring_buffer Ring buffer
+
+When you free some allocations from the beginning and there is not enough free space
+for a new one at the end of a pool, allocator's "cursor" wraps around to the
+beginning and starts allocation there. Thanks to this, if you always release
+allocations in the same order as you created them (FIFO - First In First Out),
+you can achieve behavior of a ring buffer / queue.
+
+![Ring buffer](../gfx/Linear_allocator_5_ring_buffer.png)
+
+Pools with linear algorithm support [lost allocations](@ref lost_allocations) when used as ring buffer.
+If there is not enough free space for a new allocation, but existing allocations
+from the front of the queue can become lost, they become lost and the allocation
+succeeds.
+
+![Ring buffer with lost allocations](../gfx/Linear_allocator_6_ring_buffer_lost.png)
+
+Ring buffer is available only in pools with one memory block -
+VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
+
+\section buddy_algorithm Buddy allocation algorithm
+
+There is another allocation algorithm that can be used with custom pools, called
+"buddy". Its internal data structure is based on a tree of blocks, each having
+size that is a power of two and a half of its parent's size. When you want to
+allocate memory of certain size, a free node in the tree is located. If it's too
+large, it is recursively split into two halves (called "buddies"). However, if
+requested allocation size is not a power of two, the size of a tree node is
+aligned up to the nearest power of two and the remaining space is wasted. When
+two buddy nodes become free, they are merged back into one larger node.
+
+![Buddy allocator](../gfx/Buddy_allocator.png)
+
+The advantage of buddy allocation algorithm over default algorithm is faster
+allocation and deallocation, as well as smaller external fragmentation. The
+disadvantage is more wasted space (internal fragmentation).
+
+For more information, please read ["Buddy memory allocation" on Wikipedia](https://en.wikipedia.org/wiki/Buddy_memory_allocation)
+or other sources that describe this concept in general.
+
+To use buddy allocation algorithm with a custom pool, add flag
+#VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating
+#VmaPool object.
+
+Several limitations apply to pools that use buddy algorithm:
+
+- It is recommended to use VmaPoolCreateInfo::blockSize that is a power of two.
+ Otherwise, only largest power of two smaller than the size is used for
+ allocations. The remaining space always stays unused.
+- [Margins](@ref debugging_memory_usage_margins) and
+ [corruption detection](@ref debugging_memory_usage_corruption_detection)
+ don't work in such pools.
+- [Lost allocations](@ref lost_allocations) don't work in such pools. You can
+ use them, but they never become lost. Support may be added in the future.
+- [Defragmentation](@ref defragmentation) doesn't work with allocations made from
+ such pool.
+
+\page defragmentation Defragmentation
+
+Interleaved allocations and deallocations of many objects of varying size can
+cause fragmentation over time, which can lead to a situation where the library is unable
+to find a continuous range of free memory for a new allocation despite there is
+enough free space, just scattered across many small free ranges between existing
+allocations.
+
+To mitigate this problem, you can use defragmentation feature:
+structure #VmaDefragmentationInfo2, function vmaDefragmentationBegin(), vmaDefragmentationEnd().
+Given set of allocations,
+this function can move them to compact used memory, ensure more continuous free
+space and possibly also free some `VkDeviceMemory` blocks.
+
+What the defragmentation does is:
+
+- Updates #VmaAllocation objects to point to new `VkDeviceMemory` and offset.
+ After allocation has been moved, its VmaAllocationInfo::deviceMemory and/or
+ VmaAllocationInfo::offset changes. You must query them again using
+ vmaGetAllocationInfo() if you need them.
+- Moves actual data in memory.
+
+What it doesn't do, so you need to do it yourself:
+
+- Recreate buffers and images that were bound to allocations that were defragmented and
+ bind them with their new places in memory.
+ You must use `vkDestroyBuffer()`, `vkDestroyImage()`,
+ `vkCreateBuffer()`, `vkCreateImage()` for that purpose and NOT vmaDestroyBuffer(),
+ vmaDestroyImage(), vmaCreateBuffer(), vmaCreateImage(), because you don't need to
+ destroy or create allocation objects!
+- Recreate views and update descriptors that point to these buffers and images.
+
+\section defragmentation_cpu Defragmenting CPU memory
+
+Following example demonstrates how you can run defragmentation on CPU.
+Only allocations created in memory types that are `HOST_VISIBLE` can be defragmented.
+Others are ignored.
+
+The way it works is:
+
+- It temporarily maps entire memory blocks when necessary.
+- It moves data using `memmove()` function.
+
+\code
+// Given following variables already initialized:
+VkDevice device;
+VmaAllocator allocator;
+std::vector<VkBuffer> buffers;
+std::vector<VmaAllocation> allocations;
+
+
+const uint32_t allocCount = (uint32_t)allocations.size();
+std::vector<VkBool32> allocationsChanged(allocCount);
+
+VmaDefragmentationInfo2 defragInfo = {};
+defragInfo.allocationCount = allocCount;
+defragInfo.pAllocations = allocations.data();
+defragInfo.pAllocationsChanged = allocationsChanged.data();
+defragInfo.maxCpuBytesToMove = VK_WHOLE_SIZE; // No limit.
+defragInfo.maxCpuAllocationsToMove = UINT32_MAX; // No limit.
+
+VmaDefragmentationContext defragCtx;
+vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx);
+vmaDefragmentationEnd(allocator, defragCtx);
+
+for(uint32_t i = 0; i < allocCount; ++i)
+{
+ if(allocationsChanged[i])
+ {
+ // Destroy buffer that is immutably bound to memory region which is no longer valid.
+ vkDestroyBuffer(device, buffers[i], nullptr);
+
+ // Create new buffer with same parameters.
+ VkBufferCreateInfo bufferInfo = ...;
+ vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
+
+ // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
+
+ // Bind new buffer to new memory region. Data contained in it is already moved.
+ VmaAllocationInfo allocInfo;
+ vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
+ vkBindBufferMemory(device, buffers[i], allocInfo.deviceMemory, allocInfo.offset);
+ }
+}
+\endcode
+
+Setting VmaDefragmentationInfo2::pAllocationsChanged is optional.
+This output array tells whether particular allocation in VmaDefragmentationInfo2::pAllocations at the same index
+has been modified during defragmentation.
+You can pass null, but you then need to query every allocation passed to defragmentation
+for new parameters using vmaGetAllocationInfo() if you might need to recreate and rebind a buffer or image associated with it.
+
+If you use [Custom memory pools](@ref choosing_memory_type_custom_memory_pools),
+you can fill VmaDefragmentationInfo2::poolCount and VmaDefragmentationInfo2::pPools
+instead of VmaDefragmentationInfo2::allocationCount and VmaDefragmentationInfo2::pAllocations
+to defragment all allocations in given pools.
+You cannot use VmaDefragmentationInfo2::pAllocationsChanged in that case.
+You can also combine both methods.
+
+\section defragmentation_gpu Defragmenting GPU memory
+
+It is also possible to defragment allocations created in memory types that are not `HOST_VISIBLE`.
+To do that, you need to pass a command buffer that meets requirements as described in
+VmaDefragmentationInfo2::commandBuffer. The way it works is:
+
+- It creates temporary buffers and binds them to entire memory blocks when necessary.
+- It issues `vkCmdCopyBuffer()` to passed command buffer.
+
+Example:
+
+\code
+// Given following variables already initialized:
+VkDevice device;
+VmaAllocator allocator;
+VkCommandBuffer commandBuffer;
+std::vector<VkBuffer> buffers;
+std::vector<VmaAllocation> allocations;
+
+
+const uint32_t allocCount = (uint32_t)allocations.size();
+std::vector<VkBool32> allocationsChanged(allocCount);
+
+VkCommandBufferBeginInfo cmdBufBeginInfo = ...;
+vkBeginCommandBuffer(commandBuffer, &cmdBufBeginInfo);
+
+VmaDefragmentationInfo2 defragInfo = {};
+defragInfo.allocationCount = allocCount;
+defragInfo.pAllocations = allocations.data();
+defragInfo.pAllocationsChanged = allocationsChanged.data();
+defragInfo.maxGpuBytesToMove = VK_WHOLE_SIZE; // Notice it's "GPU" this time.
+defragInfo.maxGpuAllocationsToMove = UINT32_MAX; // Notice it's "GPU" this time.
+defragInfo.commandBuffer = commandBuffer;
+
+VmaDefragmentationContext defragCtx;
+vmaDefragmentationBegin(allocator, &defragInfo, nullptr, &defragCtx);
+
+vkEndCommandBuffer(commandBuffer);
+
+// Submit commandBuffer.
+// Wait for a fence that ensures commandBuffer execution finished.
+
+vmaDefragmentationEnd(allocator, defragCtx);
+
+for(uint32_t i = 0; i < allocCount; ++i)
+{
+ if(allocationsChanged[i])
+ {
+ // Destroy buffer that is immutably bound to memory region which is no longer valid.
+ vkDestroyBuffer(device, buffers[i], nullptr);
+
+ // Create new buffer with same parameters.
+ VkBufferCreateInfo bufferInfo = ...;
+ vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
+
+ // You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
+
+ // Bind new buffer to new memory region. Data contained in it is already moved.
+ VmaAllocationInfo allocInfo;
+ vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
+ vkBindBufferMemory(device, buffers[i], allocInfo.deviceMemory, allocInfo.offset);
+ }
+}
+\endcode
+
+You can combine these two methods by specifying non-zero `maxGpu*` as well as `maxCpu*` parameters.
+The library automatically chooses best method to defragment each memory pool.
+
+You may try not to block your entire program to wait until defragmentation finishes,
+but do it in the background, as long as you carefully fullfill requirements described
+in function vmaDefragmentationBegin().
+
+\section defragmentation_additional_notes Additional notes
+
+While using defragmentation, you may experience validation layer warnings, which you just need to ignore.
+See [Validation layer warnings](@ref general_considerations_validation_layer_warnings).
+
+If you defragment allocations bound to images, these images should be created with
+`VK_IMAGE_CREATE_ALIAS_BIT` flag, to make sure that new image created with same
+parameters and pointing to data copied to another memory region will interpret
+its contents consistently. Otherwise you may experience corrupted data on some
+implementations, e.g. due to different pixel swizzling used internally by the graphics driver.
+
+If you defragment allocations bound to images, new images to be bound to new
+memory region after defragmentation should be created with `VK_IMAGE_LAYOUT_PREINITIALIZED`
+and then transitioned to their original layout from before defragmentation using
+an image memory barrier.
+
+Please don't expect memory to be fully compacted after defragmentation.
+Algorithms inside are based on some heuristics that try to maximize number of Vulkan
+memory blocks to make totally empty to release them, as well as to maximimze continuous
+empty space inside remaining blocks, while minimizing the number and size of allocations that
+need to be moved. Some fragmentation may still remain - this is normal.
+
+\section defragmentation_custom_algorithm Writing custom defragmentation algorithm
+
+If you want to implement your own, custom defragmentation algorithm,
+there is infrastructure prepared for that,
+but it is not exposed through the library API - you need to hack its source code.
+Here are steps needed to do this:
+
+-# Main thing you need to do is to define your own class derived from base abstract
+ class `VmaDefragmentationAlgorithm` and implement your version of its pure virtual methods.
+ See definition and comments of this class for details.
+-# Your code needs to interact with device memory block metadata.
+ If you need more access to its data than it's provided by its public interface,
+ declare your new class as a friend class e.g. in class `VmaBlockMetadata_Generic`.
+-# If you want to create a flag that would enable your algorithm or pass some additional
+ flags to configure it, add them to `VmaDefragmentationFlagBits` and use them in
+ VmaDefragmentationInfo2::flags.
+-# Modify function `VmaBlockVectorDefragmentationContext::Begin` to create object
+ of your new class whenever needed.
+
+
+\page lost_allocations Lost allocations
+
+If your game oversubscribes video memory, if may work OK in previous-generation
+graphics APIs (DirectX 9, 10, 11, OpenGL) because resources are automatically
+paged to system RAM. In Vulkan you can't do it because when you run out of
+memory, an allocation just fails. If you have more data (e.g. textures) that can
+fit into VRAM and you don't need it all at once, you may want to upload them to
+GPU on demand and "push out" ones that are not used for a long time to make room
+for the new ones, effectively using VRAM (or a cartain memory pool) as a form of
+cache. Vulkan Memory Allocator can help you with that by supporting a concept of
+"lost allocations".
+
+To create an allocation that can become lost, include #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT
+flag in VmaAllocationCreateInfo::flags. Before using a buffer or image bound to
+such allocation in every new frame, you need to query it if it's not lost.
+To check it, call vmaTouchAllocation().
+If the allocation is lost, you should not use it or buffer/image bound to it.
+You mustn't forget to destroy this allocation and this buffer/image.
+vmaGetAllocationInfo() can also be used for checking status of the allocation.
+Allocation is lost when returned VmaAllocationInfo::deviceMemory == `VK_NULL_HANDLE`.
+
+To create an allocation that can make some other allocations lost to make room
+for it, use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag. You will
+usually use both flags #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT and
+#VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT at the same time.
+
+Warning! Current implementation uses quite naive, brute force algorithm,
+which can make allocation calls that use #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT
+flag quite slow. A new, more optimal algorithm and data structure to speed this
+up is planned for the future.
+
+<b>Q: When interleaving creation of new allocations with usage of existing ones,
+how do you make sure that an allocation won't become lost while it's used in the
+current frame?</b>
+
+It is ensured because vmaTouchAllocation() / vmaGetAllocationInfo() not only returns allocation
+status/parameters and checks whether it's not lost, but when it's not, it also
+atomically marks it as used in the current frame, which makes it impossible to
+become lost in that frame. It uses lockless algorithm, so it works fast and
+doesn't involve locking any internal mutex.
+
+<b>Q: What if my allocation may still be in use by the GPU when it's rendering a
+previous frame while I already submit new frame on the CPU?</b>
+
+You can make sure that allocations "touched" by vmaTouchAllocation() / vmaGetAllocationInfo() will not
+become lost for a number of additional frames back from the current one by
+specifying this number as VmaAllocatorCreateInfo::frameInUseCount (for default
+memory pool) and VmaPoolCreateInfo::frameInUseCount (for custom pool).
+
+<b>Q: How do you inform the library when new frame starts?</b>
+
+You need to call function vmaSetCurrentFrameIndex().
+
+Example code:
+
+\code
+struct MyBuffer
+{
+ VkBuffer m_Buf = nullptr;
+ VmaAllocation m_Alloc = nullptr;
+
+ // Called when the buffer is really needed in the current frame.
+ void EnsureBuffer();
+};
+
+void MyBuffer::EnsureBuffer()
+{
+ // Buffer has been created.
+ if(m_Buf != VK_NULL_HANDLE)
+ {
+ // Check if its allocation is not lost + mark it as used in current frame.
+ if(vmaTouchAllocation(allocator, m_Alloc))
+ {
+ // It's all OK - safe to use m_Buf.
+ return;
+ }
+ }
+
+ // Buffer not yet exists or lost - destroy and recreate it.
+
+ vmaDestroyBuffer(allocator, m_Buf, m_Alloc);
+
+ VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
+ bufCreateInfo.size = 1024;
+ bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
+
+ VmaAllocationCreateInfo allocCreateInfo = {};
+ allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
+ allocCreateInfo.flags = VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT |
+ VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
+
+ vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &m_Buf, &m_Alloc, nullptr);
+}
+\endcode
+
+When using lost allocations, you may see some Vulkan validation layer warnings
+about overlapping regions of memory bound to different kinds of buffers and
+images. This is still valid as long as you implement proper handling of lost
+allocations (like in the example above) and don't use them.
+
+You can create an allocation that is already in lost state from the beginning using function
+vmaCreateLostAllocation(). It may be useful if you need a "dummy" allocation that is not null.
+
+You can call function vmaMakePoolAllocationsLost() to set all eligible allocations
+in a specified custom pool to lost state.
+Allocations that have been "touched" in current frame or VmaPoolCreateInfo::frameInUseCount frames back
+cannot become lost.
+
+<b>Q: Can I touch allocation that cannot become lost?</b>
+
+Yes, although it has no visible effect.
+Calls to vmaGetAllocationInfo() and vmaTouchAllocation() update last use frame index
+also for allocations that cannot become lost, but the only way to observe it is to dump
+internal allocator state using vmaBuildStatsString().
+You can use this feature for debugging purposes to explicitly mark allocations that you use
+in current frame and then analyze JSON dump to see for how long each allocation stays unused.
+
+
+\page statistics Statistics
+
+This library contains functions that return information about its internal state,
+especially the amount of memory allocated from Vulkan.
+Please keep in mind that these functions need to traverse all internal data structures
+to gather these information, so they may be quite time-consuming.
+Don't call them too often.
+
+\section statistics_numeric_statistics Numeric statistics
+
+You can query for overall statistics of the allocator using function vmaCalculateStats().
+Information are returned using structure #VmaStats.
+It contains #VmaStatInfo - number of allocated blocks, number of allocations
+(occupied ranges in these blocks), number of unused (free) ranges in these blocks,
+number of bytes used and unused (but still allocated from Vulkan) and other information.
+They are summed across memory heaps, memory types and total for whole allocator.
+
+You can query for statistics of a custom pool using function vmaGetPoolStats().
+Information are returned using structure #VmaPoolStats.
+
+You can query for information about specific allocation using function vmaGetAllocationInfo().
+It fill structure #VmaAllocationInfo.
+
+\section statistics_json_dump JSON dump
+
+You can dump internal state of the allocator to a string in JSON format using function vmaBuildStatsString().
+The result is guaranteed to be correct JSON.
+It uses ANSI encoding.
+Any strings provided by user (see [Allocation names](@ref allocation_names))
+are copied as-is and properly escaped for JSON, so if they use UTF-8, ISO-8859-2 or any other encoding,
+this JSON string can be treated as using this encoding.
+It must be freed using function vmaFreeStatsString().
+
+The format of this JSON string is not part of official documentation of the library,
+but it will not change in backward-incompatible way without increasing library major version number
+and appropriate mention in changelog.
+
+The JSON string contains all the data that can be obtained using vmaCalculateStats().
+It can also contain detailed map of allocated memory blocks and their regions -
+free and occupied by allocations.
+This allows e.g. to visualize the memory or assess fragmentation.
+
+
+\page allocation_annotation Allocation names and user data
+
+\section allocation_user_data Allocation user data
+
+You can annotate allocations with your own information, e.g. for debugging purposes.
+To do that, fill VmaAllocationCreateInfo::pUserData field when creating
+an allocation. It's an opaque `void*` pointer. You can use it e.g. as a pointer,
+some handle, index, key, ordinal number or any other value that would associate
+the allocation with your custom metadata.
+
+\code
+VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
+// Fill bufferInfo...
+
+MyBufferMetadata* pMetadata = CreateBufferMetadata();
+
+VmaAllocationCreateInfo allocCreateInfo = {};
+allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
+allocCreateInfo.pUserData = pMetadata;
+
+VkBuffer buffer;
+VmaAllocation allocation;
+vmaCreateBuffer(allocator, &bufferInfo, &allocCreateInfo, &buffer, &allocation, nullptr);
+\endcode
+
+The pointer may be later retrieved as VmaAllocationInfo::pUserData:
+
+\code
+VmaAllocationInfo allocInfo;
+vmaGetAllocationInfo(allocator, allocation, &allocInfo);
+MyBufferMetadata* pMetadata = (MyBufferMetadata*)allocInfo.pUserData;
+\endcode
+
+It can also be changed using function vmaSetAllocationUserData().
+
+Values of (non-zero) allocations' `pUserData` are printed in JSON report created by
+vmaBuildStatsString(), in hexadecimal form.
+
+\section allocation_names Allocation names
+
+There is alternative mode available where `pUserData` pointer is used to point to
+a null-terminated string, giving a name to the allocation. To use this mode,
+set #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT flag in VmaAllocationCreateInfo::flags.
+Then `pUserData` passed as VmaAllocationCreateInfo::pUserData or argument to
+vmaSetAllocationUserData() must be either null or pointer to a null-terminated string.
+The library creates internal copy of the string, so the pointer you pass doesn't need
+to be valid for whole lifetime of the allocation. You can free it after the call.
+
+\code
+VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
+// Fill imageInfo...
+
+std::string imageName = "Texture: ";
+imageName += fileName;
+
+VmaAllocationCreateInfo allocCreateInfo = {};
+allocCreateInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
+allocCreateInfo.flags = VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT;
+allocCreateInfo.pUserData = imageName.c_str();
+
+VkImage image;
+VmaAllocation allocation;
+vmaCreateImage(allocator, &imageInfo, &allocCreateInfo, &image, &allocation, nullptr);
+\endcode
+
+The value of `pUserData` pointer of the allocation will be different than the one
+you passed when setting allocation's name - pointing to a buffer managed
+internally that holds copy of the string.
+
+\code
+VmaAllocationInfo allocInfo;
+vmaGetAllocationInfo(allocator, allocation, &allocInfo);
+const char* imageName = (const char*)allocInfo.pUserData;
+printf("Image name: %s\n", imageName);
+\endcode
+
+That string is also printed in JSON report created by vmaBuildStatsString().
+
+
+\page debugging_memory_usage Debugging incorrect memory usage
+
+If you suspect a bug with memory usage, like usage of uninitialized memory or
+memory being overwritten out of bounds of an allocation,
+you can use debug features of this library to verify this.
+
+\section debugging_memory_usage_initialization Memory initialization
+
+If you experience a bug with incorrect and nondeterministic data in your program and you suspect uninitialized memory to be used,
+you can enable automatic memory initialization to verify this.
+To do it, define macro `VMA_DEBUG_INITIALIZE_ALLOCATIONS` to 1.
+
+\code
+#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1
+#include "vk_mem_alloc.h"
+\endcode
+
+It makes memory of all new allocations initialized to bit pattern `0xDCDCDCDC`.
+Before an allocation is destroyed, its memory is filled with bit pattern `0xEFEFEFEF`.
+Memory is automatically mapped and unmapped if necessary.
+
+If you find these values while debugging your program, good chances are that you incorrectly
+read Vulkan memory that is allocated but not initialized, or already freed, respectively.
+
+Memory initialization works only with memory types that are `HOST_VISIBLE`.
+It works also with dedicated allocations.
+It doesn't work with allocations created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
+as they cannot be mapped.
+
+\section debugging_memory_usage_margins Margins
+
+By default, allocations are laid out in memory blocks next to each other if possible
+(considering required alignment, `bufferImageGranularity`, and `nonCoherentAtomSize`).
+
+![Allocations without margin](../gfx/Margins_1.png)
+
+Define macro `VMA_DEBUG_MARGIN` to some non-zero value (e.g. 16) to enforce specified
+number of bytes as a margin before and after every allocation.
+
+\code
+#define VMA_DEBUG_MARGIN 16
+#include "vk_mem_alloc.h"
+\endcode
+
+![Allocations with margin](../gfx/Margins_2.png)
+
+If your bug goes away after enabling margins, it means it may be caused by memory
+being overwritten outside of allocation boundaries. It is not 100% certain though.
+Change in application behavior may also be caused by different order and distribution
+of allocations across memory blocks after margins are applied.
+
+The margin is applied also before first and after last allocation in a block.
+It may occur only once between two adjacent allocations.
+
+Margins work with all types of memory.
+
+Margin is applied only to allocations made out of memory blocks and not to dedicated
+allocations, which have their own memory block of specific size.
+It is thus not applied to allocations made using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag
+or those automatically decided to put into dedicated allocations, e.g. due to its
+large size or recommended by VK_KHR_dedicated_allocation extension.
+Margins are also not active in custom pools created with #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag.
+
+Margins appear in [JSON dump](@ref statistics_json_dump) as part of free space.
+
+Note that enabling margins increases memory usage and fragmentation.
+
+\section debugging_memory_usage_corruption_detection Corruption detection
+
+You can additionally define macro `VMA_DEBUG_DETECT_CORRUPTION` to 1 to enable validation
+of contents of the margins.
+
+\code
+#define VMA_DEBUG_MARGIN 16
+#define VMA_DEBUG_DETECT_CORRUPTION 1
+#include "vk_mem_alloc.h"
+\endcode
+
+When this feature is enabled, number of bytes specified as `VMA_DEBUG_MARGIN`
+(it must be multiply of 4) before and after every allocation is filled with a magic number.
+This idea is also know as "canary".
+Memory is automatically mapped and unmapped if necessary.
+
+This number is validated automatically when the allocation is destroyed.
+If it's not equal to the expected value, `VMA_ASSERT()` is executed.
+It clearly means that either CPU or GPU overwritten the memory outside of boundaries of the allocation,
+which indicates a serious bug.
+
+You can also explicitly request checking margins of all allocations in all memory blocks
+that belong to specified memory types by using function vmaCheckCorruption(),
+or in memory blocks that belong to specified custom pool, by using function
+vmaCheckPoolCorruption().
+
+Margin validation (corruption detection) works only for memory types that are
+`HOST_VISIBLE` and `HOST_COHERENT`.
+
+
+\page record_and_replay Record and replay
+
+\section record_and_replay_introduction Introduction
+
+While using the library, sequence of calls to its functions together with their
+parameters can be recorded to a file and later replayed using standalone player
+application. It can be useful to:
+
+- Test correctness - check if same sequence of calls will not cause crash or
+ failures on a target platform.
+- Gather statistics - see number of allocations, peak memory usage, number of
+ calls etc.
+- Benchmark performance - see how much time it takes to replay the whole
+ sequence.
+
+\section record_and_replay_usage Usage
+
+<b>To record sequence of calls to a file:</b> Fill in
+VmaAllocatorCreateInfo::pRecordSettings member while creating #VmaAllocator
+object. File is opened and written during whole lifetime of the allocator.
+
+<b>To replay file:</b> Use VmaReplay - standalone command-line program.
+Precompiled binary can be found in "bin" directory.
+Its source can be found in "src/VmaReplay" directory.
+Its project is generated by Premake.
+Command line syntax is printed when the program is launched without parameters.
+Basic usage:
+
+ VmaReplay.exe MyRecording.csv
+
+<b>Documentation of file format</b> can be found in file: "docs/Recording file format.md".
+It's a human-readable, text file in CSV format (Comma Separated Values).
+
+\section record_and_replay_additional_considerations Additional considerations
+
+- Replaying file that was recorded on a different GPU (with different parameters
+ like `bufferImageGranularity`, `nonCoherentAtomSize`, and especially different
+ set of memory heaps and types) may give different performance and memory usage
+ results, as well as issue some warnings and errors.
+- Current implementation of recording in VMA, as well as VmaReplay application, is
+ coded and tested only on Windows. Inclusion of recording code is driven by
+ `VMA_RECORDING_ENABLED` macro. Support for other platforms should be easy to
+ add. Contributions are welcomed.
+- Currently calls to vmaDefragment() function are not recorded.
+
+
+\page usage_patterns Recommended usage patterns
+
+See also slides from talk:
+[Sawicki, Adam. Advanced Graphics Techniques Tutorial: Memory management in Vulkan and DX12. Game Developers Conference, 2018](https://www.gdcvault.com/play/1025458/Advanced-Graphics-Techniques-Tutorial-New)
+
+
+\section usage_patterns_simple Simple patterns
+
+\subsection usage_patterns_simple_render_targets Render targets
+
+<b>When:</b>
+Any resources that you frequently write and read on GPU,
+e.g. images used as color attachments (aka "render targets"), depth-stencil attachments,
+images/buffers used as storage image/buffer (aka "Unordered Access View (UAV)").
+
+<b>What to do:</b>
+Create them in video memory that is fastest to access from GPU using
+#VMA_MEMORY_USAGE_GPU_ONLY.
+
+Consider using [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension
+and/or manually creating them as dedicated allocations using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
+especially if they are large or if you plan to destroy and recreate them e.g. when
+display resolution changes.
+Prefer to create such resources first and all other GPU resources (like textures and vertex buffers) later.
+
+\subsection usage_patterns_simple_immutable_resources Immutable resources
+
+<b>When:</b>
+Any resources that you fill on CPU only once (aka "immutable") or infrequently
+and then read frequently on GPU,
+e.g. textures, vertex and index buffers, constant buffers that don't change often.
+
+<b>What to do:</b>
+Create them in video memory that is fastest to access from GPU using
+#VMA_MEMORY_USAGE_GPU_ONLY.
+
+To initialize content of such resource, create a CPU-side (aka "staging") copy of it
+in system memory - #VMA_MEMORY_USAGE_CPU_ONLY, map it, fill it,
+and submit a transfer from it to the GPU resource.
+You can keep the staging copy if you need it for another upload transfer in the future.
+If you don't, you can destroy it or reuse this buffer for uploading different resource
+after the transfer finishes.
+
+Prefer to create just buffers in system memory rather than images, even for uploading textures.
+Use `vkCmdCopyBufferToImage()`.
+Dont use images with `VK_IMAGE_TILING_LINEAR`.
+
+\subsection usage_patterns_dynamic_resources Dynamic resources
+
+<b>When:</b>
+Any resources that change frequently (aka "dynamic"), e.g. every frame or every draw call,
+written on CPU, read on GPU.
+
+<b>What to do:</b>
+Create them using #VMA_MEMORY_USAGE_CPU_TO_GPU.
+You can map it and write to it directly on CPU, as well as read from it on GPU.
+
+This is a more complex situation. Different solutions are possible,
+and the best one depends on specific GPU type, but you can use this simple approach for the start.
+Prefer to write to such resource sequentially (e.g. using `memcpy`).
+Don't perform random access or any reads from it on CPU, as it may be very slow.
+
+\subsection usage_patterns_readback Readback
+
+<b>When:</b>
+Resources that contain data written by GPU that you want to read back on CPU,
+e.g. results of some computations.
+
+<b>What to do:</b>
+Create them using #VMA_MEMORY_USAGE_GPU_TO_CPU.
+You can write to them directly on GPU, as well as map and read them on CPU.
+
+\section usage_patterns_advanced Advanced patterns
+
+\subsection usage_patterns_integrated_graphics Detecting integrated graphics
+
+You can support integrated graphics (like Intel HD Graphics, AMD APU) better
+by detecting it in Vulkan.
+To do it, call `vkGetPhysicalDeviceProperties()`, inspect
+`VkPhysicalDeviceProperties::deviceType` and look for `VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU`.
+When you find it, you can assume that memory is unified and all memory types are comparably fast
+to access from GPU, regardless of `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.
+
+You can then sum up sizes of all available memory heaps and treat them as useful for
+your GPU resources, instead of only `DEVICE_LOCAL` ones.
+You can also prefer to create your resources in memory types that are `HOST_VISIBLE` to map them
+directly instead of submitting explicit transfer (see below).
+
+\subsection usage_patterns_direct_vs_transfer Direct access versus transfer
+
+For resources that you frequently write on CPU and read on GPU, many solutions are possible:
+
+-# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
+ second copy in system memory using #VMA_MEMORY_USAGE_CPU_ONLY and submit explicit tranfer each time.
+-# Create just single copy using #VMA_MEMORY_USAGE_CPU_TO_GPU, map it and fill it on CPU,
+ read it directly on GPU.
+-# Create just single copy using #VMA_MEMORY_USAGE_CPU_ONLY, map it and fill it on CPU,
+ read it directly on GPU.
+
+Which solution is the most efficient depends on your resource and especially on the GPU.
+It is best to measure it and then make the decision.
+Some general recommendations:
+
+- On integrated graphics use (2) or (3) to avoid unnecesary time and memory overhead
+ related to using a second copy and making transfer.
+- For small resources (e.g. constant buffers) use (2).
+ Discrete AMD cards have special 256 MiB pool of video memory that is directly mappable.
+ Even if the resource ends up in system memory, its data may be cached on GPU after first
+ fetch over PCIe bus.
+- For larger resources (e.g. textures), decide between (1) and (2).
+ You may want to differentiate NVIDIA and AMD, e.g. by looking for memory type that is
+ both `DEVICE_LOCAL` and `HOST_VISIBLE`. When you find it, use (2), otherwise use (1).
+
+Similarly, for resources that you frequently write on GPU and read on CPU, multiple
+solutions are possible:
+
+-# Create one copy in video memory using #VMA_MEMORY_USAGE_GPU_ONLY,
+ second copy in system memory using #VMA_MEMORY_USAGE_GPU_TO_CPU and submit explicit tranfer each time.
+-# Create just single copy using #VMA_MEMORY_USAGE_GPU_TO_CPU, write to it directly on GPU,
+ map it and read it on CPU.
+
+You should take some measurements to decide which option is faster in case of your specific
+resource.
+
+If you don't want to specialize your code for specific types of GPUs, you can still make
+an simple optimization for cases when your resource ends up in mappable memory to use it
+directly in this case instead of creating CPU-side staging copy.
+For details see [Finding out if memory is mappable](@ref memory_mapping_finding_if_memory_mappable).
+
+
+\page configuration Configuration
+
+Please check "CONFIGURATION SECTION" in the code to find macros that you can define
+before each include of this file or change directly in this file to provide
+your own implementation of basic facilities like assert, `min()` and `max()` functions,
+mutex, atomic etc.
+The library uses its own implementation of containers by default, but you can switch to using
+STL containers instead.
+
+\section config_Vulkan_functions Pointers to Vulkan functions
+
+The library uses Vulkan functions straight from the `vulkan.h` header by default.
+If you want to provide your own pointers to these functions, e.g. fetched using
+`vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`:
+
+-# Define `VMA_STATIC_VULKAN_FUNCTIONS 0`.
+-# Provide valid pointers through VmaAllocatorCreateInfo::pVulkanFunctions.
+
+\section custom_memory_allocator Custom host memory allocator
+
+If you use custom allocator for CPU memory rather than default operator `new`
+and `delete` from C++, you can make this library using your allocator as well
+by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These
+functions will be passed to Vulkan, as well as used by the library itself to
+make any CPU-side allocations.
+
+\section allocation_callbacks Device memory allocation callbacks
+
+The library makes calls to `vkAllocateMemory()` and `vkFreeMemory()` internally.
+You can setup callbacks to be informed about these calls, e.g. for the purpose
+of gathering some statistics. To do it, fill optional member
+VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
+
+\section heap_memory_limit Device heap memory limit
+
+If you want to test how your program behaves with limited amount of Vulkan device
+memory available without switching your graphics card to one that really has
+smaller VRAM, you can use a feature of this library intended for this purpose.
+To do it, fill optional member VmaAllocatorCreateInfo::pHeapSizeLimit.
+
+
+
+\page vk_khr_dedicated_allocation VK_KHR_dedicated_allocation
+
+VK_KHR_dedicated_allocation is a Vulkan extension which can be used to improve
+performance on some GPUs. It augments Vulkan API with possibility to query
+driver whether it prefers particular buffer or image to have its own, dedicated
+allocation (separate `VkDeviceMemory` block) for better efficiency - to be able
+to do some internal optimizations.
+
+The extension is supported by this library. It will be used automatically when
+enabled. To enable it:
+
+1 . When creating Vulkan device, check if following 2 device extensions are
+supported (call `vkEnumerateDeviceExtensionProperties()`).
+If yes, enable them (fill `VkDeviceCreateInfo::ppEnabledExtensionNames`).
+
+- VK_KHR_get_memory_requirements2
+- VK_KHR_dedicated_allocation
+
+If you enabled these extensions:
+
+2 . Use #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag when creating
+your #VmaAllocator`to inform the library that you enabled required extensions
+and you want the library to use them.
+
+\code
+allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
+
+vmaCreateAllocator(&allocatorInfo, &allocator);
+\endcode
+
+That's all. The extension will be automatically used whenever you create a
+buffer using vmaCreateBuffer() or image using vmaCreateImage().
+
+When using the extension together with Vulkan Validation Layer, you will receive
+warnings like this:
+
+ vkBindBufferMemory(): Binding memory to buffer 0x33 but vkGetBufferMemoryRequirements() has not been called on that buffer.
+
+It is OK, you should just ignore it. It happens because you use function
+`vkGetBufferMemoryRequirements2KHR()` instead of standard
+`vkGetBufferMemoryRequirements()`, while the validation layer seems to be
+unaware of it.
+
+To learn more about this extension, see:
+
+- [VK_KHR_dedicated_allocation in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html#VK_KHR_dedicated_allocation)
+- [VK_KHR_dedicated_allocation unofficial manual](http://asawicki.info/articles/VK_KHR_dedicated_allocation.php5)
+
+
+
+\page general_considerations General considerations
+
+\section general_considerations_thread_safety Thread safety
+
+- The library has no global state, so separate #VmaAllocator objects can be used
+ independently.
+ There should be no need to create multiple such objects though - one per `VkDevice` is enough.
+- By default, all calls to functions that take #VmaAllocator as first parameter
+ are safe to call from multiple threads simultaneously because they are
+ synchronized internally when needed.
+- When the allocator is created with #VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT
+ flag, calls to functions that take such #VmaAllocator object must be
+ synchronized externally.
+- Access to a #VmaAllocation object must be externally synchronized. For example,
+ you must not call vmaGetAllocationInfo() and vmaMapMemory() from different
+ threads at the same time if you pass the same #VmaAllocation object to these
+ functions.
+
+\section general_considerations_validation_layer_warnings Validation layer warnings
+
+When using this library, you can meet following types of warnings issued by
+Vulkan validation layer. They don't necessarily indicate a bug, so you may need
+to just ignore them.
+
+- *vkBindBufferMemory(): Binding memory to buffer 0xeb8e4 but vkGetBufferMemoryRequirements() has not been called on that buffer.*
+ - It happens when VK_KHR_dedicated_allocation extension is enabled.
+ `vkGetBufferMemoryRequirements2KHR` function is used instead, while validation layer seems to be unaware of it.
+- *Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used.*
+ - It happens when you map a buffer or image, because the library maps entire
+ `VkDeviceMemory` block, where different types of images and buffers may end
+ up together, especially on GPUs with unified memory like Intel.
+- *Non-linear image 0xebc91 is aliased with linear buffer 0xeb8e4 which may indicate a bug.*
+ - It happens when you use lost allocations, and a new image or buffer is
+ created in place of an existing object that bacame lost.
+ - It may happen also when you use [defragmentation](@ref defragmentation).
+
+\section general_considerations_allocation_algorithm Allocation algorithm
+
+The library uses following algorithm for allocation, in order:
+
+-# Try to find free range of memory in existing blocks.
+-# If failed, try to create a new block of `VkDeviceMemory`, with preferred block size.
+-# If failed, try to create such block with size/2, size/4, size/8.
+-# If failed and #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag was
+ specified, try to find space in existing blocks, possilby making some other
+ allocations lost.
+-# If failed, try to allocate separate `VkDeviceMemory` for this allocation,
+ just like when you use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
+-# If failed, choose other memory type that meets the requirements specified in
+ VmaAllocationCreateInfo and go to point 1.
+-# If failed, return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
+
+\section general_considerations_features_not_supported Features not supported
+
+Features deliberately excluded from the scope of this library:
+
+- Data transfer. Uploading (straming) and downloading data of buffers and images
+ between CPU and GPU memory and related synchronization is responsibility of the user.
+- Allocations for imported/exported external memory. They tend to require
+ explicit memory type index and dedicated allocation anyway, so they don't
+ interact with main features of this library. Such special purpose allocations
+ should be made manually, using `vkCreateBuffer()` and `vkAllocateMemory()`.
+- Recreation of buffers and images. Although the library has functions for
+ buffer and image creation (vmaCreateBuffer(), vmaCreateImage()), you need to
+ recreate these objects yourself after defragmentation. That's because the big
+ structures `VkBufferCreateInfo`, `VkImageCreateInfo` are not stored in
+ #VmaAllocation object.
+- Handling CPU memory allocation failures. When dynamically creating small C++
+ objects in CPU memory (not Vulkan memory), allocation failures are not checked
+ and handled gracefully, because that would complicate code significantly and
+ is usually not needed in desktop PC applications anyway.
+- Code free of any compiler warnings. Maintaining the library to compile and
+ work correctly on so many different platforms is hard enough. Being free of
+ any warnings, on any version of any compiler, is simply not feasible.
+- This is a C++ library with C interface.
+ Bindings or ports to any other programming languages are welcomed as external projects and
+ are not going to be included into this repository.
+
+*/
+
+/*
+Define this macro to 0/1 to disable/enable support for recording functionality,
+available through VmaAllocatorCreateInfo::pRecordSettings.
+*/
+#ifndef VMA_RECORDING_ENABLED
+ #ifdef _WIN32
+ #define VMA_RECORDING_ENABLED 1
+ #else
+ #define VMA_RECORDING_ENABLED 0
+ #endif
+#endif
+
+#ifndef NOMINMAX
+ #define NOMINMAX // For windows.h
+#endif
+
+#ifndef VULKAN_H_
+ #include <vulkan/vulkan.h>
+#endif
+
+#if VMA_RECORDING_ENABLED
+ #include <windows.h>
+#endif
+
+#if !defined(VMA_DEDICATED_ALLOCATION)
+ #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation
+ #define VMA_DEDICATED_ALLOCATION 1
+ #else
+ #define VMA_DEDICATED_ALLOCATION 0
+ #endif
+#endif
+
+/** \struct VmaAllocator
+\brief Represents main object of this library initialized.
+
+Fill structure #VmaAllocatorCreateInfo and call function vmaCreateAllocator() to create it.
+Call function vmaDestroyAllocator() to destroy it.
+
+It is recommended to create just one object of this type per `VkDevice` object,
+right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed.
+*/
+VK_DEFINE_HANDLE(VmaAllocator)
+
+/// Callback function called after successful vkAllocateMemory.
+typedef void (VKAPI_PTR *PFN_vmaAllocateDeviceMemoryFunction)(
+ VmaAllocator allocator,
+ uint32_t memoryType,
+ VkDeviceMemory memory,
+ VkDeviceSize size);
+/// Callback function called before vkFreeMemory.
+typedef void (VKAPI_PTR *PFN_vmaFreeDeviceMemoryFunction)(
+ VmaAllocator allocator,
+ uint32_t memoryType,
+ VkDeviceMemory memory,
+ VkDeviceSize size);
+
+/** \brief Set of callbacks that the library will call for `vkAllocateMemory` and `vkFreeMemory`.
+
+Provided for informative purpose, e.g. to gather statistics about number of
+allocations or total amount of memory allocated in Vulkan.
+
+Used in VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.
+*/
+typedef struct VmaDeviceMemoryCallbacks {
+ /// Optional, can be null.
+ PFN_vmaAllocateDeviceMemoryFunction pfnAllocate;
+ /// Optional, can be null.
+ PFN_vmaFreeDeviceMemoryFunction pfnFree;
+} VmaDeviceMemoryCallbacks;
+
+/// Flags for created #VmaAllocator.
+typedef enum VmaAllocatorCreateFlagBits {
+ /** \brief Allocator and all objects created from it will not be synchronized internally, so you must guarantee they are used from only one thread at a time or synchronized externally by you.
+
+ Using this flag may increase performance because internal mutexes are not used.
+ */
+ VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001,
+ /** \brief Enables usage of VK_KHR_dedicated_allocation extension.
+
+ Using this extenion will automatically allocate dedicated blocks of memory for
+ some buffers and images instead of suballocating place for them out of bigger
+ memory blocks (as if you explicitly used #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
+ flag) when it is recommended by the driver. It may improve performance on some
+ GPUs.
+
+ You may set this flag only if you found out that following device extensions are
+ supported, you enabled them while creating Vulkan device passed as
+ VmaAllocatorCreateInfo::device, and you want them to be used internally by this
+ library:
+
+ - VK_KHR_get_memory_requirements2
+ - VK_KHR_dedicated_allocation
+
+When this flag is set, you can experience following warnings reported by Vulkan
+validation layer. You can ignore them.
+
+> vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer.
+ */
+ VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002,
+
+ VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VmaAllocatorCreateFlagBits;
+typedef VkFlags VmaAllocatorCreateFlags;
+
+/** \brief Pointers to some Vulkan functions - a subset used by the library.
+
+Used in VmaAllocatorCreateInfo::pVulkanFunctions.
+*/
+typedef struct VmaVulkanFunctions {
+ PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
+ PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
+ PFN_vkAllocateMemory vkAllocateMemory;
+ PFN_vkFreeMemory vkFreeMemory;
+ PFN_vkMapMemory vkMapMemory;
+ PFN_vkUnmapMemory vkUnmapMemory;
+ PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;
+ PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;
+ PFN_vkBindBufferMemory vkBindBufferMemory;
+ PFN_vkBindImageMemory vkBindImageMemory;
+ PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
+ PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
+ PFN_vkCreateBuffer vkCreateBuffer;
+ PFN_vkDestroyBuffer vkDestroyBuffer;
+ PFN_vkCreateImage vkCreateImage;
+ PFN_vkDestroyImage vkDestroyImage;
+ PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
+#if VMA_DEDICATED_ALLOCATION
+ PFN_vkGetBufferMemoryRequirements2KHR vkGetBufferMemoryRequirements2KHR;
+ PFN_vkGetImageMemoryRequirements2KHR vkGetImageMemoryRequirements2KHR;
+#endif
+} VmaVulkanFunctions;
+
+/// Flags to be used in VmaRecordSettings::flags.
+typedef enum VmaRecordFlagBits {
+ /** \brief Enables flush after recording every function call.
+
+ Enable it if you expect your application to crash, which may leave recording file truncated.
+ It may degrade performance though.
+ */
+ VMA_RECORD_FLUSH_AFTER_CALL_BIT = 0x00000001,
+
+ VMA_RECORD_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VmaRecordFlagBits;
+typedef VkFlags VmaRecordFlags;
+
+/// Parameters for recording calls to VMA functions. To be used in VmaAllocatorCreateInfo::pRecordSettings.
+typedef struct VmaRecordSettings
+{
+ /// Flags for recording. Use #VmaRecordFlagBits enum.
+ VmaRecordFlags flags;
+ /** \brief Path to the file that should be written by the recording.
+
+ Suggested extension: "csv".
+ If the file already exists, it will be overwritten.
+ It will be opened for the whole time #VmaAllocator object is alive.
+ If opening this file fails, creation of the whole allocator object fails.
+ */
+ const char* pFilePath;
+} VmaRecordSettings;
+
+/// Description of a Allocator to be created.
+typedef struct VmaAllocatorCreateInfo
+{
+ /// Flags for created allocator. Use #VmaAllocatorCreateFlagBits enum.
+ VmaAllocatorCreateFlags flags;
+ /// Vulkan physical device.
+ /** It must be valid throughout whole lifetime of created allocator. */
+ VkPhysicalDevice physicalDevice;
+ /// Vulkan device.
+ /** It must be valid throughout whole lifetime of created allocator. */
+ VkDevice device;
+ /// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps > 1 GiB. Optional.
+ /** Set to 0 to use default, which is currently 256 MiB. */
+ VkDeviceSize preferredLargeHeapBlockSize;
+ /// Custom CPU memory allocation callbacks. Optional.
+ /** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */
+ const VkAllocationCallbacks* pAllocationCallbacks;
+ /// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional.
+ /** Optional, can be null. */
+ const VmaDeviceMemoryCallbacks* pDeviceMemoryCallbacks;
+ /** \brief Maximum number of additional frames that are in use at the same time as current frame.
+
+ This value is used only when you make allocations with
+ VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
+ lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
+
+ For example, if you double-buffer your command buffers, so resources used for
+ rendering in previous frame may still be in use by the GPU at the moment you
+ allocate resources needed for the current frame, set this value to 1.
+
+ If you want to allow any allocations other than used in the current frame to
+ become lost, set this value to 0.
+ */
+ uint32_t frameInUseCount;
+ /** \brief Either null or a pointer to an array of limits on maximum number of bytes that can be allocated out of particular Vulkan memory heap.
+
+ If not NULL, it must be a pointer to an array of
+ `VkPhysicalDeviceMemoryProperties::memoryHeapCount` elements, defining limit on
+ maximum number of bytes that can be allocated out of particular Vulkan memory
+ heap.
+
+ Any of the elements may be equal to `VK_WHOLE_SIZE`, which means no limit on that
+ heap. This is also the default in case of `pHeapSizeLimit` = NULL.
+
+ If there is a limit defined for a heap:
+
+ - If user tries to allocate more memory from that heap using this allocator,
+ the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.
+ - If the limit is smaller than heap size reported in `VkMemoryHeap::size`, the
+ value of this limit will be reported instead when using vmaGetMemoryProperties().
+
+ Warning! Using this feature may not be equivalent to installing a GPU with
+ smaller amount of memory, because graphics driver doesn't necessary fail new
+ allocations with `VK_ERROR_OUT_OF_DEVICE_MEMORY` result when memory capacity is
+ exceeded. It may return success and just silently migrate some device memory
+ blocks to system RAM. This driver behavior can also be controlled using
+ VK_AMD_memory_overallocation_behavior extension.
+ */
+ const VkDeviceSize* pHeapSizeLimit;
+ /** \brief Pointers to Vulkan functions. Can be null if you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1`.
+
+ If you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1` in configuration section,
+ you can pass null as this member, because the library will fetch pointers to
+ Vulkan functions internally in a static way, like:
+
+ vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
+
+ Fill this member if you want to provide your own pointers to Vulkan functions,
+ e.g. fetched using `vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`.
+ */
+ const VmaVulkanFunctions* pVulkanFunctions;
+ /** \brief Parameters for recording of VMA calls. Can be null.
+
+ If not null, it enables recording of calls to VMA functions to a file.
+ If support for recording is not enabled using `VMA_RECORDING_ENABLED` macro,
+ creation of the allocator object fails with `VK_ERROR_FEATURE_NOT_PRESENT`.
+ */
+ const VmaRecordSettings* pRecordSettings;
+} VmaAllocatorCreateInfo;
+
+/// Creates Allocator object.
+VkResult vmaCreateAllocator(
+ const VmaAllocatorCreateInfo* pCreateInfo,
+ VmaAllocator* pAllocator);
+
+/// Destroys allocator object.
+void vmaDestroyAllocator(
+ VmaAllocator allocator);
+
+/**
+PhysicalDeviceProperties are fetched from physicalDevice by the allocator.
+You can access it here, without fetching it again on your own.
+*/
+void vmaGetPhysicalDeviceProperties(
+ VmaAllocator allocator,
+ const VkPhysicalDeviceProperties** ppPhysicalDeviceProperties);
+
+/**
+PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator.
+You can access it here, without fetching it again on your own.
+*/
+void vmaGetMemoryProperties(
+ VmaAllocator allocator,
+ const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties);
+
+/**
+\brief Given Memory Type Index, returns Property Flags of this memory type.
+
+This is just a convenience function. Same information can be obtained using
+vmaGetMemoryProperties().
+*/
+void vmaGetMemoryTypeProperties(
+ VmaAllocator allocator,
+ uint32_t memoryTypeIndex,
+ VkMemoryPropertyFlags* pFlags);
+
+/** \brief Sets index of the current frame.
+
+This function must be used if you make allocations with
+#VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT and
+#VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flags to inform the allocator
+when a new frame begins. Allocations queried using vmaGetAllocationInfo() cannot
+become lost in the current frame.
+*/
+void vmaSetCurrentFrameIndex(
+ VmaAllocator allocator,
+ uint32_t frameIndex);
+
+/** \brief Calculated statistics of memory usage in entire allocator.
+*/
+typedef struct VmaStatInfo
+{
+ /// Number of `VkDeviceMemory` Vulkan memory blocks allocated.
+ uint32_t blockCount;
+ /// Number of #VmaAllocation allocation objects allocated.
+ uint32_t allocationCount;
+ /// Number of free ranges of memory between allocations.
+ uint32_t unusedRangeCount;
+ /// Total number of bytes occupied by all allocations.
+ VkDeviceSize usedBytes;
+ /// Total number of bytes occupied by unused ranges.
+ VkDeviceSize unusedBytes;
+ VkDeviceSize allocationSizeMin, allocationSizeAvg, allocationSizeMax;
+ VkDeviceSize unusedRangeSizeMin, unusedRangeSizeAvg, unusedRangeSizeMax;
+} VmaStatInfo;
+
+/// General statistics from current state of Allocator.
+typedef struct VmaStats
+{
+ VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES];
+ VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
+ VmaStatInfo total;
+} VmaStats;
+
+/// Retrieves statistics from current state of the Allocator.
+void vmaCalculateStats(
+ VmaAllocator allocator,
+ VmaStats* pStats);
+
+#define VMA_STATS_STRING_ENABLED 1
+
+#if VMA_STATS_STRING_ENABLED
+
+/// Builds and returns statistics as string in JSON format.
+/** @param[out] ppStatsString Must be freed using vmaFreeStatsString() function.
+*/
+void vmaBuildStatsString(
+ VmaAllocator allocator,
+ char** ppStatsString,
+ VkBool32 detailedMap);
+
+void vmaFreeStatsString(
+ VmaAllocator allocator,
+ char* pStatsString);
+
+#endif // #if VMA_STATS_STRING_ENABLED
+
+/** \struct VmaPool
+\brief Represents custom memory pool
+
+Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it.
+Call function vmaDestroyPool() to destroy it.
+
+For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools).
+*/
+VK_DEFINE_HANDLE(VmaPool)
+
+typedef enum VmaMemoryUsage
+{
+ /** No intended memory usage specified.
+ Use other members of VmaAllocationCreateInfo to specify your requirements.
+ */
+ VMA_MEMORY_USAGE_UNKNOWN = 0,
+ /** Memory will be used on device only, so fast access from the device is preferred.
+ It usually means device-local GPU (video) memory.
+ No need to be mappable on host.
+ It is roughly equivalent of `D3D12_HEAP_TYPE_DEFAULT`.
+
+ Usage:
+
+ - Resources written and read by device, e.g. images used as attachments.
+ - Resources transferred from host once (immutable) or infrequently and read by
+ device multiple times, e.g. textures to be sampled, vertex buffers, uniform
+ (constant) buffers, and majority of other types of resources used on GPU.
+
+ Allocation may still end up in `HOST_VISIBLE` memory on some implementations.
+ In such case, you are free to map it.
+ You can use #VMA_ALLOCATION_CREATE_MAPPED_BIT with this usage type.
+ */
+ VMA_MEMORY_USAGE_GPU_ONLY = 1,
+ /** Memory will be mappable on host.
+ It usually means CPU (system) memory.
+ Guarantees to be `HOST_VISIBLE` and `HOST_COHERENT`.
+ CPU access is typically uncached. Writes may be write-combined.
+ Resources created in this pool may still be accessible to the device, but access to them can be slow.
+ It is roughly equivalent of `D3D12_HEAP_TYPE_UPLOAD`.
+
+ Usage: Staging copy of resources used as transfer source.
+ */
+ VMA_MEMORY_USAGE_CPU_ONLY = 2,
+ /**
+ Memory that is both mappable on host (guarantees to be `HOST_VISIBLE`) and preferably fast to access by GPU.
+ CPU access is typically uncached. Writes may be write-combined.
+
+ Usage: Resources written frequently by host (dynamic), read by device. E.g. textures, vertex buffers, uniform buffers updated every frame or every draw call.
+ */
+ VMA_MEMORY_USAGE_CPU_TO_GPU = 3,
+ /** Memory mappable on host (guarantees to be `HOST_VISIBLE`) and cached.
+ It is roughly equivalent of `D3D12_HEAP_TYPE_READBACK`.
+
+ Usage:
+
+ - Resources written by device, read by host - results of some computations, e.g. screen capture, average scene luminance for HDR tone mapping.
+ - Any resources read or accessed randomly on host, e.g. CPU-side copy of vertex buffer used as source of transfer, but also used for collision detection.
+ */
+ VMA_MEMORY_USAGE_GPU_TO_CPU = 4,
+ VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF
+} VmaMemoryUsage;
+
+/// Flags to be passed as VmaAllocationCreateInfo::flags.
+typedef enum VmaAllocationCreateFlagBits {
+ /** \brief Set this flag if the allocation should have its own memory block.
+
+ Use it for special, big resources, like fullscreen images used as attachments.
+
+ This flag must also be used for host visible resources that you want to map
+ simultaneously because otherwise they might end up as regions of the same
+ `VkDeviceMemory`, while mapping same `VkDeviceMemory` multiple times
+ simultaneously is illegal.
+
+ You should not use this flag if VmaAllocationCreateInfo::pool is not null.
+ */
+ VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001,
+
+ /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block.
+
+ If new allocation cannot be placed in any of the existing blocks, allocation
+ fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
+
+ You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and
+ #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense.
+
+ If VmaAllocationCreateInfo::pool is not null, this flag is implied and ignored. */
+ VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002,
+ /** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it.
+
+ Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData.
+
+ Is it valid to use this flag for allocation made from memory type that is not
+ `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is
+ useful if you need an allocation that is efficient to use on GPU
+ (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that
+ support it (e.g. Intel GPU).
+
+ You should not use this flag together with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT.
+ */
+ VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004,
+ /** Allocation created with this flag can become lost as a result of another
+ allocation with #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag, so you
+ must check it before use.
+
+ To check if allocation is not lost, call vmaGetAllocationInfo() and check if
+ VmaAllocationInfo::deviceMemory is not `VK_NULL_HANDLE`.
+
+ For details about supporting lost allocations, see Lost Allocations
+ chapter of User Guide on Main Page.
+
+ You should not use this flag together with #VMA_ALLOCATION_CREATE_MAPPED_BIT.
+ */
+ VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT = 0x00000008,
+ /** While creating allocation using this flag, other allocations that were
+ created with flag #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT can become lost.
+
+ For details about supporting lost allocations, see Lost Allocations
+ chapter of User Guide on Main Page.
+ */
+ VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT = 0x00000010,
+ /** Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a
+ null-terminated string. Instead of copying pointer value, a local copy of the
+ string is made and stored in allocation's `pUserData`. The string is automatically
+ freed together with the allocation. It is also used in vmaBuildStatsString().
+ */
+ VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020,
+ /** Allocation will be created from upper stack in a double stack pool.
+
+ This flag is only allowed for custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT flag.
+ */
+ VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040,
+
+ /** Allocation strategy that chooses smallest possible free range for the
+ allocation.
+ */
+ VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT = 0x00010000,
+ /** Allocation strategy that chooses biggest possible free range for the
+ allocation.
+ */
+ VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT = 0x00020000,
+ /** Allocation strategy that chooses first suitable free range for the
+ allocation.
+
+ "First" doesn't necessarily means the one with smallest offset in memory,
+ but rather the one that is easiest and fastest to find.
+ */
+ VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = 0x00040000,
+
+ /** Allocation strategy that tries to minimize memory usage.
+ */
+ VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT,
+ /** Allocation strategy that tries to minimize allocation time.
+ */
+ VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
+ /** Allocation strategy that tries to minimize memory fragmentation.
+ */
+ VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT,
+
+ /** A bit mask to extract only `STRATEGY` bits from entire set of flags.
+ */
+ VMA_ALLOCATION_CREATE_STRATEGY_MASK =
+ VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT |
+ VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT |
+ VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
+
+ VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VmaAllocationCreateFlagBits;
+typedef VkFlags VmaAllocationCreateFlags;
+
+typedef struct VmaAllocationCreateInfo
+{
+ /// Use #VmaAllocationCreateFlagBits enum.
+ VmaAllocationCreateFlags flags;
+ /** \brief Intended usage of memory.
+
+ You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n
+ If `pool` is not null, this member is ignored.
+ */
+ VmaMemoryUsage usage;
+ /** \brief Flags that must be set in a Memory Type chosen for an allocation.
+
+ Leave 0 if you specify memory requirements in other way. \n
+ If `pool` is not null, this member is ignored.*/
+ VkMemoryPropertyFlags requiredFlags;
+ /** \brief Flags that preferably should be set in a memory type chosen for an allocation.
+
+ Set to 0 if no additional flags are prefered. \n
+ If `pool` is not null, this member is ignored. */
+ VkMemoryPropertyFlags preferredFlags;
+ /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation.
+
+ Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if
+ it meets other requirements specified by this structure, with no further
+ restrictions on memory type index. \n
+ If `pool` is not null, this member is ignored.
+ */
+ uint32_t memoryTypeBits;
+ /** \brief Pool that this allocation should be created in.
+
+ Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members:
+ `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored.
+ */
+ VmaPool pool;
+ /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData().
+
+ If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either
+ null or pointer to a null-terminated string. The string will be then copied to
+ internal buffer, so it doesn't need to be valid after allocation call.
+ */
+ void* pUserData;
+} VmaAllocationCreateInfo;
+
+/**
+\brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo.
+
+This algorithm tries to find a memory type that:
+
+- Is allowed by memoryTypeBits.
+- Contains all the flags from pAllocationCreateInfo->requiredFlags.
+- Matches intended usage.
+- Has as many flags from pAllocationCreateInfo->preferredFlags as possible.
+
+\return Returns VK_ERROR_FEATURE_NOT_PRESENT if not found. Receiving such result
+from this function or any other allocating function probably means that your
+device doesn't support any memory type with requested features for the specific
+type of resource you want to use it for. Please check parameters of your
+resource, like image layout (OPTIMAL versus LINEAR) or mip level count.
+*/
+VkResult vmaFindMemoryTypeIndex(
+ VmaAllocator allocator,
+ uint32_t memoryTypeBits,
+ const VmaAllocationCreateInfo* pAllocationCreateInfo,
+ uint32_t* pMemoryTypeIndex);
+
+/**
+\brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo.
+
+It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
+It internally creates a temporary, dummy buffer that never has memory bound.
+It is just a convenience function, equivalent to calling:
+
+- `vkCreateBuffer`
+- `vkGetBufferMemoryRequirements`
+- `vmaFindMemoryTypeIndex`
+- `vkDestroyBuffer`
+*/
+VkResult vmaFindMemoryTypeIndexForBufferInfo(
+ VmaAllocator allocator,
+ const VkBufferCreateInfo* pBufferCreateInfo,
+ const VmaAllocationCreateInfo* pAllocationCreateInfo,
+ uint32_t* pMemoryTypeIndex);
+
+/**
+\brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo.
+
+It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.
+It internally creates a temporary, dummy image that never has memory bound.
+It is just a convenience function, equivalent to calling:
+
+- `vkCreateImage`
+- `vkGetImageMemoryRequirements`
+- `vmaFindMemoryTypeIndex`
+- `vkDestroyImage`
+*/
+VkResult vmaFindMemoryTypeIndexForImageInfo(
+ VmaAllocator allocator,
+ const VkImageCreateInfo* pImageCreateInfo,
+ const VmaAllocationCreateInfo* pAllocationCreateInfo,
+ uint32_t* pMemoryTypeIndex);
+
+/// Flags to be passed as VmaPoolCreateInfo::flags.
+typedef enum VmaPoolCreateFlagBits {
+ /** \brief Use this flag if you always allocate only buffers and linear images or only optimal images out of this pool and so Buffer-Image Granularity can be ignored.
+
+ This is an optional optimization flag.
+
+ If you always allocate using vmaCreateBuffer(), vmaCreateImage(),
+ vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator
+ knows exact type of your allocations so it can handle Buffer-Image Granularity
+ in the optimal way.
+
+ If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(),
+ exact type of such allocations is not known, so allocator must be conservative
+ in handling Buffer-Image Granularity, which can lead to suboptimal allocation
+ (wasted memory). In that case, if you can make sure you always allocate only
+ buffers and linear images or only optimal images out of this pool, use this flag
+ to make allocator disregard Buffer-Image Granularity and so make allocations
+ faster and more optimal.
+ */
+ VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002,
+
+ /** \brief Enables alternative, linear allocation algorithm in this pool.
+
+ Specify this flag to enable linear allocation algorithm, which always creates
+ new allocations after last one and doesn't reuse space from allocations freed in
+ between. It trades memory consumption for simplified algorithm and data
+ structure, which has better performance and uses less memory for metadata.
+
+ By using this flag, you can achieve behavior of free-at-once, stack,
+ ring buffer, and double stack. For details, see documentation chapter
+ \ref linear_algorithm.
+
+ When using this flag, you must specify VmaPoolCreateInfo::maxBlockCount == 1 (or 0 for default).
+
+ For more details, see [Linear allocation algorithm](@ref linear_algorithm).
+ */
+ VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004,
+
+ /** \brief Enables alternative, buddy allocation algorithm in this pool.
+
+ It operates on a tree of blocks, each having size that is a power of two and
+ a half of its parent's size. Comparing to default algorithm, this one provides
+ faster allocation and deallocation and decreased external fragmentation,
+ at the expense of more memory wasted (internal fragmentation).
+
+ For more details, see [Buddy allocation algorithm](@ref buddy_algorithm).
+ */
+ VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT = 0x00000008,
+
+ /** Bit mask to extract only `ALGORITHM` bits from entire set of flags.
+ */
+ VMA_POOL_CREATE_ALGORITHM_MASK =
+ VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT |
+ VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT,
+
+ VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VmaPoolCreateFlagBits;
+typedef VkFlags VmaPoolCreateFlags;
+
+/** \brief Describes parameter of created #VmaPool.
+*/
+typedef struct VmaPoolCreateInfo {
+ /** \brief Vulkan memory type index to allocate this pool from.
+ */
+ uint32_t memoryTypeIndex;
+ /** \brief Use combination of #VmaPoolCreateFlagBits.
+ */
+ VmaPoolCreateFlags flags;
+ /** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes. Optional.
+
+ Specify nonzero to set explicit, constant size of memory blocks used by this
+ pool.
+
+ Leave 0 to use default and let the library manage block sizes automatically.
+ Sizes of particular blocks may vary.
+ */
+ VkDeviceSize blockSize;
+ /** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty.
+
+ Set to 0 to have no preallocated blocks and allow the pool be completely empty.
+ */
+ size_t minBlockCount;
+ /** \brief Maximum number of blocks that can be allocated in this pool. Optional.
+
+ Set to 0 to use default, which is `SIZE_MAX`, which means no limit.
+
+ Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated
+ throughout whole lifetime of this pool.
+ */
+ size_t maxBlockCount;
+ /** \brief Maximum number of additional frames that are in use at the same time as current frame.
+
+ This value is used only when you make allocations with
+ #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
+ lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
+
+ For example, if you double-buffer your command buffers, so resources used for
+ rendering in previous frame may still be in use by the GPU at the moment you
+ allocate resources needed for the current frame, set this value to 1.
+
+ If you want to allow any allocations other than used in the current frame to
+ become lost, set this value to 0.
+ */
+ uint32_t frameInUseCount;
+} VmaPoolCreateInfo;
+
+/** \brief Describes parameter of existing #VmaPool.
+*/
+typedef struct VmaPoolStats {
+ /** \brief Total amount of `VkDeviceMemory` allocated from Vulkan for this pool, in bytes.
+ */
+ VkDeviceSize size;
+ /** \brief Total number of bytes in the pool not used by any #VmaAllocation.
+ */
+ VkDeviceSize unusedSize;
+ /** \brief Number of #VmaAllocation objects created from this pool that were not destroyed or lost.
+ */
+ size_t allocationCount;
+ /** \brief Number of continuous memory ranges in the pool not used by any #VmaAllocation.
+ */
+ size_t unusedRangeCount;
+ /** \brief Size of the largest continuous free memory region available for new allocation.
+
+ Making a new allocation of that size is not guaranteed to succeed because of
+ possible additional margin required to respect alignment and buffer/image
+ granularity.
+ */
+ VkDeviceSize unusedRangeSizeMax;
+ /** \brief Number of `VkDeviceMemory` blocks allocated for this pool.
+ */
+ size_t blockCount;
+} VmaPoolStats;
+
+/** \brief Allocates Vulkan device memory and creates #VmaPool object.
+
+@param allocator Allocator object.
+@param pCreateInfo Parameters of pool to create.
+@param[out] pPool Handle to created pool.
+*/
+VkResult vmaCreatePool(
+ VmaAllocator allocator,
+ const VmaPoolCreateInfo* pCreateInfo,
+ VmaPool* pPool);
+
+/** \brief Destroys #VmaPool object and frees Vulkan device memory.
+*/
+void vmaDestroyPool(
+ VmaAllocator allocator,
+ VmaPool pool);
+
+/** \brief Retrieves statistics of existing #VmaPool object.
+
+@param allocator Allocator object.
+@param pool Pool object.
+@param[out] pPoolStats Statistics of specified pool.
+*/
+void vmaGetPoolStats(
+ VmaAllocator allocator,
+ VmaPool pool,
+ VmaPoolStats* pPoolStats);
+
+/** \brief Marks all allocations in given pool as lost if they are not used in current frame or VmaPoolCreateInfo::frameInUseCount back from now.
+
+@param allocator Allocator object.
+@param pool Pool.
+@param[out] pLostAllocationCount Number of allocations marked as lost. Optional - pass null if you don't need this information.
+*/
+void vmaMakePoolAllocationsLost(
+ VmaAllocator allocator,
+ VmaPool pool,
+ size_t* pLostAllocationCount);
+
+/** \brief Checks magic number in margins around all allocations in given memory pool in search for corruptions.
+
+Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
+`VMA_DEBUG_MARGIN` is defined to nonzero and the pool is created in memory type that is
+`HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
+
+Possible return values:
+
+- `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool.
+- `VK_SUCCESS` - corruption detection has been performed and succeeded.
+- `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
+ `VMA_ASSERT` is also fired in that case.
+- Other value: Error returned by Vulkan, e.g. memory mapping failure.
+*/
+VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool);
+
+/** \struct VmaAllocation
+\brief Represents single memory allocation.
+
+It may be either dedicated block of `VkDeviceMemory` or a specific region of a bigger block of this type
+plus unique offset.
+
+There are multiple ways to create such object.
+You need to fill structure VmaAllocationCreateInfo.
+For more information see [Choosing memory type](@ref choosing_memory_type).
+
+Although the library provides convenience functions that create Vulkan buffer or image,
+allocate memory for it and bind them together,
+binding of the allocation to a buffer or an image is out of scope of the allocation itself.
+Allocation object can exist without buffer/image bound,
+binding can be done manually by the user, and destruction of it can be done
+independently of destruction of the allocation.
+
+The object also remembers its size and some other information.
+To retrieve this information, use function vmaGetAllocationInfo() and inspect
+returned structure VmaAllocationInfo.
+
+Some kinds allocations can be in lost state.
+For more information, see [Lost allocations](@ref lost_allocations).
+*/
+VK_DEFINE_HANDLE(VmaAllocation)
+
+/** \brief Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
+*/
+typedef struct VmaAllocationInfo {
+ /** \brief Memory type index that this allocation was allocated from.
+
+ It never changes.
+ */
+ uint32_t memoryType;
+ /** \brief Handle to Vulkan memory object.
+
+ Same memory object can be shared by multiple allocations.
+
+ It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
+
+ If the allocation is lost, it is equal to `VK_NULL_HANDLE`.
+ */
+ VkDeviceMemory deviceMemory;
+ /** \brief Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory, offset) pair is unique to this allocation.
+
+ It can change after call to vmaDefragment() if this allocation is passed to the function, or if allocation is lost.
+ */
+ VkDeviceSize offset;
+ /** \brief Size of this allocation, in bytes.
+
+ It never changes, unless allocation is lost.
+ */
+ VkDeviceSize size;
+ /** \brief Pointer to the beginning of this allocation as mapped data.
+
+ If the allocation hasn't been mapped using vmaMapMemory() and hasn't been
+ created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value null.
+
+ It can change after call to vmaMapMemory(), vmaUnmapMemory().
+ It can also change after call to vmaDefragment() if this allocation is passed to the function.
+ */
+ void* pMappedData;
+ /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData().
+
+ It can change after call to vmaSetAllocationUserData() for this allocation.
+ */
+ void* pUserData;
+} VmaAllocationInfo;
+
+/** \brief General purpose memory allocation.
+
+@param[out] pAllocation Handle to allocated memory.
+@param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
+
+You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
+
+It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(),
+vmaCreateBuffer(), vmaCreateImage() instead whenever possible.
+*/
+VkResult vmaAllocateMemory(
+ VmaAllocator allocator,
+ const VkMemoryRequirements* pVkMemoryRequirements,
+ const VmaAllocationCreateInfo* pCreateInfo,
+ VmaAllocation* pAllocation,
+ VmaAllocationInfo* pAllocationInfo);
+
+/** \brief General purpose memory allocation for multiple allocation objects at once.
+
+@param allocator Allocator object.
+@param pVkMemoryRequirements Memory requirements for each allocation.
+@param pCreateInfo Creation parameters for each alloction.
+@param allocationCount Number of allocations to make.
+@param[out] pAllocations Pointer to array that will be filled with handles to created allocations.
+@param[out] pAllocationInfo Optional. Pointer to array that will be filled with parameters of created allocations.
+
+You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().
+
+Word "pages" is just a suggestion to use this function to allocate pieces of memory needed for sparse binding.
+It is just a general purpose allocation function able to make multiple allocations at once.
+It may be internally optimized to be more efficient than calling vmaAllocateMemory() `allocationCount` times.
+
+All allocations are made using same parameters. All of them are created out of the same memory pool and type.
+If any allocation fails, all allocations already made within this function call are also freed, so that when
+returned result is not `VK_SUCCESS`, `pAllocation` array is always entirely filled with `VK_NULL_HANDLE`.
+*/
+VkResult vmaAllocateMemoryPages(
+ VmaAllocator allocator,
+ const VkMemoryRequirements* pVkMemoryRequirements,
+ const VmaAllocationCreateInfo* pCreateInfo,
+ size_t allocationCount,
+ VmaAllocation* pAllocations,
+ VmaAllocationInfo* pAllocationInfo);
+
+/**
+@param[out] pAllocation Handle to allocated memory.
+@param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
+
+You should free the memory using vmaFreeMemory().
+*/
+VkResult vmaAllocateMemoryForBuffer(
+ VmaAllocator allocator,
+ VkBuffer buffer,
+ const VmaAllocationCreateInfo* pCreateInfo,
+ VmaAllocation* pAllocation,
+ VmaAllocationInfo* pAllocationInfo);
+
+/// Function similar to vmaAllocateMemoryForBuffer().
+VkResult vmaAllocateMemoryForImage(
+ VmaAllocator allocator,
+ VkImage image,
+ const VmaAllocationCreateInfo* pCreateInfo,
+ VmaAllocation* pAllocation,
+ VmaAllocationInfo* pAllocationInfo);
+
+/** \brief Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage().
+
+Passing `VK_NULL_HANDLE` as `allocation` is valid. Such function call is just skipped.
+*/
+void vmaFreeMemory(
+ VmaAllocator allocator,
+ VmaAllocation allocation);
+
+/** \brief Frees memory and destroys multiple allocations.
+
+Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding.
+It is just a general purpose function to free memory and destroy allocations made using e.g. vmaAllocateMemory(),
+vmaAllocateMemoryPages() and other functions.
+It may be internally optimized to be more efficient than calling vmaFreeMemory() `allocationCount` times.
+
+Allocations in `pAllocations` array can come from any memory pools and types.
+Passing `VK_NULL_HANDLE` as elements of `pAllocations` array is valid. Such entries are just skipped.
+*/
+void vmaFreeMemoryPages(
+ VmaAllocator allocator,
+ size_t allocationCount,
+ VmaAllocation* pAllocations);
+
+/** \brief Tries to resize an allocation in place, if there is enough free memory after it.
+
+Tries to change allocation's size without moving or reallocating it.
+You can both shrink and grow allocation size.
+When growing, it succeeds only when the allocation belongs to a memory block with enough
+free space after it.
+
+Returns `VK_SUCCESS` if allocation's size has been successfully changed.
+Returns `VK_ERROR_OUT_OF_POOL_MEMORY` if allocation's size could not be changed.
+
+After successful call to this function, VmaAllocationInfo::size of this allocation changes.
+All other parameters stay the same: memory pool and type, alignment, offset, mapped pointer.
+
+- Calling this function on allocation that is in lost state fails with result `VK_ERROR_VALIDATION_FAILED_EXT`.
+- Calling this function with `newSize` same as current allocation size does nothing and returns `VK_SUCCESS`.
+- Resizing dedicated allocations, as well as allocations created in pools that use linear
+ or buddy algorithm, is not supported.
+ The function returns `VK_ERROR_FEATURE_NOT_PRESENT` in such cases.
+ Support may be added in the future.
+*/
+VkResult vmaResizeAllocation(
+ VmaAllocator allocator,
+ VmaAllocation allocation,
+ VkDeviceSize newSize);
+
+/** \brief Returns current information about specified allocation and atomically marks it as used in current frame.
+
+Current paramters of given allocation are returned in `pAllocationInfo`.
+
+This function also atomically "touches" allocation - marks it as used in current frame,
+just like vmaTouchAllocation().
+If the allocation is in lost state, `pAllocationInfo->deviceMemory == VK_NULL_HANDLE`.
+
+Although this function uses atomics and doesn't lock any mutex, so it should be quite efficient,
+you can avoid calling it too often.
+
+- You can retrieve same VmaAllocationInfo structure while creating your resource, from function
+ vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change
+ (e.g. due to defragmentation or allocation becoming lost).
+- If you just want to check if allocation is not lost, vmaTouchAllocation() will work faster.
+*/
+void vmaGetAllocationInfo(
+ VmaAllocator allocator,
+ VmaAllocation allocation,
+ VmaAllocationInfo* pAllocationInfo);
+
+/** \brief Returns `VK_TRUE` if allocation is not lost and atomically marks it as used in current frame.
+
+If the allocation has been created with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
+this function returns `VK_TRUE` if it's not in lost state, so it can still be used.
+It then also atomically "touches" the allocation - marks it as used in current frame,
+so that you can be sure it won't become lost in current frame or next `frameInUseCount` frames.
+
+If the allocation is in lost state, the function returns `VK_FALSE`.
+Memory of such allocation, as well as buffer or image bound to it, should not be used.
+Lost allocation and the buffer/image still need to be destroyed.
+
+If the allocation has been created without #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag,
+this function always returns `VK_TRUE`.
+*/
+VkBool32 vmaTouchAllocation(
+ VmaAllocator allocator,
+ VmaAllocation allocation);
+
+/** \brief Sets pUserData in given allocation to new value.
+
+If the allocation was created with VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT,
+pUserData must be either null, or pointer to a null-terminated string. The function
+makes local copy of the string and sets it as allocation's `pUserData`. String
+passed as pUserData doesn't need to be valid for whole lifetime of the allocation -
+you can free it after this call. String previously pointed by allocation's
+pUserData is freed from memory.
+
+If the flag was not used, the value of pointer `pUserData` is just copied to
+allocation's `pUserData`. It is opaque, so you can use it however you want - e.g.
+as a pointer, ordinal number or some handle to you own data.
+*/
+void vmaSetAllocationUserData(
+ VmaAllocator allocator,
+ VmaAllocation allocation,
+ void* pUserData);
+
+/** \brief Creates new allocation that is in lost state from the beginning.
+
+It can be useful if you need a dummy, non-null allocation.
+
+You still need to destroy created object using vmaFreeMemory().
+
+Returned allocation is not tied to any specific memory pool or memory type and
+not bound to any image or buffer. It has size = 0. It cannot be turned into
+a real, non-empty allocation.
+*/
+void vmaCreateLostAllocation(
+ VmaAllocator allocator,
+ VmaAllocation* pAllocation);
+
+/** \brief Maps memory represented by given allocation and returns pointer to it.
+
+Maps memory represented by given allocation to make it accessible to CPU code.
+When succeeded, `*ppData` contains pointer to first byte of this memory.
+If the allocation is part of bigger `VkDeviceMemory` block, the pointer is
+correctly offseted to the beginning of region assigned to this particular
+allocation.
+
+Mapping is internally reference-counted and synchronized, so despite raw Vulkan
+function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory`
+multiple times simultaneously, it is safe to call this function on allocations
+assigned to the same memory block. Actual Vulkan memory will be mapped on first
+mapping and unmapped on last unmapping.
+
+If the function succeeded, you must call vmaUnmapMemory() to unmap the
+allocation when mapping is no longer needed or before freeing the allocation, at
+the latest.
+
+It also safe to call this function multiple times on the same allocation. You
+must call vmaUnmapMemory() same number of times as you called vmaMapMemory().
+
+It is also safe to call this function on allocation created with
+#VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time.
+You must still call vmaUnmapMemory() same number of times as you called
+vmaMapMemory(). You must not call vmaUnmapMemory() additional time to free the
+"0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag.
+
+This function fails when used on allocation made in memory type that is not
+`HOST_VISIBLE`.
+
+This function always fails when called for allocation that was created with
+#VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocations cannot be
+mapped.
+*/
+VkResult vmaMapMemory(
+ VmaAllocator allocator,
+ VmaAllocation allocation,
+ void** ppData);
+
+/** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().
+
+For details, see description of vmaMapMemory().
+*/
+void vmaUnmapMemory(
+ VmaAllocator allocator,
+ VmaAllocation allocation);
+
+/** \brief Flushes memory of given allocation.
+
+Calls `vkFlushMappedMemoryRanges()` for memory associated with given range of given allocation.
+
+- `offset` must be relative to the beginning of allocation.
+- `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
+- `offset` and `size` don't have to be aligned.
+ They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
+- If `size` is 0, this call is ignored.
+- If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
+ this call is ignored.
+*/
+void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
+
+/** \brief Invalidates memory of given allocation.
+
+Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given range of given allocation.
+
+- `offset` must be relative to the beginning of allocation.
+- `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
+- `offset` and `size` don't have to be aligned.
+ They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
+- If `size` is 0, this call is ignored.
+- If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
+ this call is ignored.
+*/
+void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
+
+/** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions.
+
+@param memoryTypeBits Bit mask, where each bit set means that a memory type with that index should be checked.
+
+Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
+`VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are
+`HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
+
+Possible return values:
+
+- `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types.
+- `VK_SUCCESS` - corruption detection has been performed and succeeded.
+- `VK_ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
+ `VMA_ASSERT` is also fired in that case.
+- Other value: Error returned by Vulkan, e.g. memory mapping failure.
+*/
+VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits);
+
+/** \struct VmaDefragmentationContext
+\brief Represents Opaque object that represents started defragmentation process.
+
+Fill structure #VmaDefragmentationInfo2 and call function vmaDefragmentationBegin() to create it.
+Call function vmaDefragmentationEnd() to destroy it.
+*/
+VK_DEFINE_HANDLE(VmaDefragmentationContext)
+
+/// Flags to be used in vmaDefragmentationBegin(). None at the moment. Reserved for future use.
+typedef enum VmaDefragmentationFlagBits {
+ VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
+} VmaDefragmentationFlagBits;
+typedef VkFlags VmaDefragmentationFlags;
+
+/** \brief Parameters for defragmentation.
+
+To be used with function vmaDefragmentationBegin().
+*/
+typedef struct VmaDefragmentationInfo2 {
+ /** \brief Reserved for future use. Should be 0.
+ */
+ VmaDefragmentationFlags flags;
+ /** \brief Number of allocations in `pAllocations` array.
+ */
+ uint32_t allocationCount;
+ /** \brief Pointer to array of allocations that can be defragmented.
+
+ The array should have `allocationCount` elements.
+ The array should not contain nulls.
+ Elements in the array should be unique - same allocation cannot occur twice.
+ It is safe to pass allocations that are in the lost state - they are ignored.
+ All allocations not present in this array are considered non-moveable during this defragmentation.
+ */
+ VmaAllocation* pAllocations;
+ /** \brief Optional, output. Pointer to array that will be filled with information whether the allocation at certain index has been changed during defragmentation.
+
+ The array should have `allocationCount` elements.
+ You can pass null if you are not interested in this information.
+ */
+ VkBool32* pAllocationsChanged;
+ /** \brief Numer of pools in `pPools` array.
+ */
+ uint32_t poolCount;
+ /** \brief Either null or pointer to array of pools to be defragmented.
+
+ All the allocations in the specified pools can be moved during defragmentation
+ and there is no way to check if they were really moved as in `pAllocationsChanged`,
+ so you must query all the allocations in all these pools for new `VkDeviceMemory`
+ and offset using vmaGetAllocationInfo() if you might need to recreate buffers
+ and images bound to them.
+
+ The array should have `poolCount` elements.
+ The array should not contain nulls.
+ Elements in the array should be unique - same pool cannot occur twice.
+
+ Using this array is equivalent to specifying all allocations from the pools in `pAllocations`.
+ It might be more efficient.
+ */
+ VmaPool* pPools;
+ /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on CPU side, like `memcpy()`, `memmove()`.
+
+ `VK_WHOLE_SIZE` means no limit.
+ */
+ VkDeviceSize maxCpuBytesToMove;
+ /** \brief Maximum number of allocations that can be moved to a different place using transfers on CPU side, like `memcpy()`, `memmove()`.
+
+ `UINT32_MAX` means no limit.
+ */
+ uint32_t maxCpuAllocationsToMove;
+ /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on GPU side, posted to `commandBuffer`.
+
+ `VK_WHOLE_SIZE` means no limit.
+ */
+ VkDeviceSize maxGpuBytesToMove;
+ /** \brief Maximum number of allocations that can be moved to a different place using transfers on GPU side, posted to `commandBuffer`.
+
+ `UINT32_MAX` means no limit.
+ */
+ uint32_t maxGpuAllocationsToMove;
+ /** \brief Optional. Command buffer where GPU copy commands will be posted.
+
+ If not null, it must be a valid command buffer handle that supports Transfer queue type.
+ It must be in the recording state and outside of a render pass instance.
+ You need to submit it and make sure it finished execution before calling vmaDefragmentationEnd().
+
+ Passing null means that only CPU defragmentation will be performed.
+ */
+ VkCommandBuffer commandBuffer;
+} VmaDefragmentationInfo2;
+
+/** \brief Deprecated. Optional configuration parameters to be passed to function vmaDefragment().
+
+\deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
+*/
+typedef struct VmaDefragmentationInfo {
+ /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places.
+
+ Default is `VK_WHOLE_SIZE`, which means no limit.
+ */
+ VkDeviceSize maxBytesToMove;
+ /** \brief Maximum number of allocations that can be moved to different place.
+
+ Default is `UINT32_MAX`, which means no limit.
+ */
+ uint32_t maxAllocationsToMove;
+} VmaDefragmentationInfo;
+
+/** \brief Statistics returned by function vmaDefragment(). */
+typedef struct VmaDefragmentationStats {
+ /// Total number of bytes that have been copied while moving allocations to different places.
+ VkDeviceSize bytesMoved;
+ /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects.
+ VkDeviceSize bytesFreed;
+ /// Number of allocations that have been moved to different places.
+ uint32_t allocationsMoved;
+ /// Number of empty `VkDeviceMemory` objects that have been released to the system.
+ uint32_t deviceMemoryBlocksFreed;
+} VmaDefragmentationStats;
+
+/** \brief Begins defragmentation process.
+
+@param allocator Allocator object.
+@param pInfo Structure filled with parameters of defragmentation.
+@param[out] pStats Optional. Statistics of defragmentation. You can pass null if you are not interested in this information.
+@param[out] pContext Context object that must be passed to vmaDefragmentationEnd() to finish defragmentation.
+@return `VK_SUCCESS` and `*pContext == null` if defragmentation finished within this function call. `VK_NOT_READY` and `*pContext != null` if defragmentation has been started and you need to call vmaDefragmentationEnd() to finish it. Negative value in case of error.
+
+Use this function instead of old, deprecated vmaDefragment().
+
+Warning! Between the call to vmaDefragmentationBegin() and vmaDefragmentationEnd():
+
+- You should not use any of allocations passed as `pInfo->pAllocations` or
+ any allocations that belong to pools passed as `pInfo->pPools`,
+ including calling vmaGetAllocationInfo(), vmaTouchAllocation(), or access
+ their data.
+- Some mutexes protecting internal data structures may be locked, so trying to
+ make or free any allocations, bind buffers or images, map memory, or launch
+ another simultaneous defragmentation in between may cause stall (when done on
+ another thread) or deadlock (when done on the same thread), unless you are
+ 100% sure that defragmented allocations are in different pools.
+- Information returned via `pStats` and `pInfo->pAllocationsChanged` are undefined.
+ They become valid after call to vmaDefragmentationEnd().
+- If `pInfo->commandBuffer` is not null, you must submit that command buffer
+ and make sure it finished execution before calling vmaDefragmentationEnd().
+*/
+VkResult vmaDefragmentationBegin(
+ VmaAllocator allocator,
+ const VmaDefragmentationInfo2* pInfo,
+ VmaDefragmentationStats* pStats,
+ VmaDefragmentationContext *pContext);
+
+/** \brief Ends defragmentation process.
+
+Use this function to finish defragmentation started by vmaDefragmentationBegin().
+It is safe to pass `context == null`. The function then does nothing.
+*/
+VkResult vmaDefragmentationEnd(
+ VmaAllocator allocator,
+ VmaDefragmentationContext context);
+
+/** \brief Deprecated. Compacts memory by moving allocations.
+
+@param pAllocations Array of allocations that can be moved during this compation.
+@param allocationCount Number of elements in pAllocations and pAllocationsChanged arrays.
+@param[out] pAllocationsChanged Array of boolean values that will indicate whether matching allocation in pAllocations array has been moved. This parameter is optional. Pass null if you don't need this information.
+@param pDefragmentationInfo Configuration parameters. Optional - pass null to use default values.
+@param[out] pDefragmentationStats Statistics returned by the function. Optional - pass null if you don't need this information.
+@return `VK_SUCCESS` if completed, negative error code in case of error.
+
+\deprecated This is a part of the old interface. It is recommended to use structure #VmaDefragmentationInfo2 and function vmaDefragmentationBegin() instead.
+
+This function works by moving allocations to different places (different
+`VkDeviceMemory` objects and/or different offsets) in order to optimize memory
+usage. Only allocations that are in `pAllocations` array can be moved. All other
+allocations are considered nonmovable in this call. Basic rules:
+
+- Only allocations made in memory types that have
+ `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`
+ flags can be compacted. You may pass other allocations but it makes no sense -
+ these will never be moved.
+- Custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT or
+ #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag are not defragmented. Allocations
+ passed to this function that come from such pools are ignored.
+- Allocations created with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT or
+ created as dedicated allocations for any other reason are also ignored.
+- Both allocations made with or without #VMA_ALLOCATION_CREATE_MAPPED_BIT
+ flag can be compacted. If not persistently mapped, memory will be mapped
+ temporarily inside this function if needed.
+- You must not pass same #VmaAllocation object multiple times in `pAllocations` array.
+
+The function also frees empty `VkDeviceMemory` blocks.
+
+Warning: This function may be time-consuming, so you shouldn't call it too often
+(like after every resource creation/destruction).
+You can call it on special occasions (like when reloading a game level or
+when you just destroyed a lot of objects). Calling it every frame may be OK, but
+you should measure that on your platform.
+
+For more information, see [Defragmentation](@ref defragmentation) chapter.
+*/
+VkResult vmaDefragment(
+ VmaAllocator allocator,
+ VmaAllocation* pAllocations,
+ size_t allocationCount,
+ VkBool32* pAllocationsChanged,
+ const VmaDefragmentationInfo *pDefragmentationInfo,
+ VmaDefragmentationStats* pDefragmentationStats);
+
+/** \brief Binds buffer to allocation.
+
+Binds specified buffer to region of memory represented by specified allocation.
+Gets `VkDeviceMemory` handle and offset from the allocation.
+If you want to create a buffer, allocate memory for it and bind them together separately,
+you should use this function for binding instead of standard `vkBindBufferMemory()`,
+because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
+allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
+(which is illegal in Vulkan).
+
+It is recommended to use function vmaCreateBuffer() instead of this one.
+*/
+VkResult vmaBindBufferMemory(
+ VmaAllocator allocator,
+ VmaAllocation allocation,
+ VkBuffer buffer);
+
+/** \brief Binds image to allocation.
+
+Binds specified image to region of memory represented by specified allocation.
+Gets `VkDeviceMemory` handle and offset from the allocation.
+If you want to create an image, allocate memory for it and bind them together separately,
+you should use this function for binding instead of standard `vkBindImageMemory()`,
+because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple
+allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
+(which is illegal in Vulkan).
+
+It is recommended to use function vmaCreateImage() instead of this one.
+*/
+VkResult vmaBindImageMemory(
+ VmaAllocator allocator,
+ VmaAllocation allocation,
+ VkImage image);
+
+/**
+@param[out] pBuffer Buffer that was created.
+@param[out] pAllocation Allocation that was created.
+@param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().
+
+This function automatically:
+
+-# Creates buffer.
+-# Allocates appropriate memory for it.
+-# Binds the buffer with the memory.
+
+If any of these operations fail, buffer and allocation are not created,
+returned value is negative error code, *pBuffer and *pAllocation are null.
+
+If the function succeeded, you must destroy both buffer and allocation when you
+no longer need them using either convenience function vmaDestroyBuffer() or
+separately, using `vkDestroyBuffer()` and vmaFreeMemory().
+
+If VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used,
+VK_KHR_dedicated_allocation extension is used internally to query driver whether
+it requires or prefers the new buffer to have dedicated allocation. If yes,
+and if dedicated allocation is possible (VmaAllocationCreateInfo::pool is null
+and VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated
+allocation for this buffer, just like when using
+VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
+*/
+VkResult vmaCreateBuffer(
+ VmaAllocator allocator,
+ const VkBufferCreateInfo* pBufferCreateInfo,
+ const VmaAllocationCreateInfo* pAllocationCreateInfo,
+ VkBuffer* pBuffer,
+ VmaAllocation* pAllocation,
+ VmaAllocationInfo* pAllocationInfo);
+
+/** \brief Destroys Vulkan buffer and frees allocated memory.
+
+This is just a convenience function equivalent to:
+
+\code
+vkDestroyBuffer(device, buffer, allocationCallbacks);
+vmaFreeMemory(allocator, allocation);
+\endcode
+
+It it safe to pass null as buffer and/or allocation.
+*/
+void vmaDestroyBuffer(
+ VmaAllocator allocator,
+ VkBuffer buffer,
+ VmaAllocation allocation);
+
+/// Function similar to vmaCreateBuffer().
+VkResult vmaCreateImage(
+ VmaAllocator allocator,
+ const VkImageCreateInfo* pImageCreateInfo,
+ const VmaAllocationCreateInfo* pAllocationCreateInfo,
+ VkImage* pImage,
+ VmaAllocation* pAllocation,
+ VmaAllocationInfo* pAllocationInfo);
+
+/** \brief Destroys Vulkan image and frees allocated memory.
+
+This is just a convenience function equivalent to:
+
+\code
+vkDestroyImage(device, image, allocationCallbacks);
+vmaFreeMemory(allocator, allocation);
+\endcode
+
+It it safe to pass null as image and/or allocation.
+*/
+void vmaDestroyImage(
+ VmaAllocator allocator,
+ VkImage image,
+ VmaAllocation allocation);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
+
+// For Visual Studio IntelliSense.
+#if defined(__cplusplus) && defined(__INTELLISENSE__)
+#define VMA_IMPLEMENTATION
+#endif
+
+#ifdef VMA_IMPLEMENTATION
+#undef VMA_IMPLEMENTATION
+
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+
+/*******************************************************************************
+CONFIGURATION SECTION
+
+Define some of these macros before each #include of this header or change them
+here if you need other then default behavior depending on your environment.
+*/
+
+/*
+Define this macro to 1 to make the library fetch pointers to Vulkan functions
+internally, like:
+
+ vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
+
+Define to 0 if you are going to provide you own pointers to Vulkan functions via
+VmaAllocatorCreateInfo::pVulkanFunctions.
+*/
+#if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)
+#define VMA_STATIC_VULKAN_FUNCTIONS 1
+#endif
+
+// Define this macro to 1 to make the library use STL containers instead of its own implementation.
+//#define VMA_USE_STL_CONTAINERS 1
+
+/* Set this macro to 1 to make the library including and using STL containers:
+std::pair, std::vector, std::list, std::unordered_map.
+
+Set it to 0 or undefined to make the library using its own implementation of
+the containers.
+*/
+#if VMA_USE_STL_CONTAINERS
+ #define VMA_USE_STL_VECTOR 1
+ #define VMA_USE_STL_UNORDERED_MAP 1
+ #define VMA_USE_STL_LIST 1
+#endif
+
+#ifndef VMA_USE_STL_SHARED_MUTEX
+ // Minimum Visual Studio 2015 Update 2
+ #if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918
+ #define VMA_USE_STL_SHARED_MUTEX 1
+ #endif
+#endif
+
+#if VMA_USE_STL_VECTOR
+ #include <vector>
+#endif
+
+#if VMA_USE_STL_UNORDERED_MAP
+ #include <unordered_map>
+#endif
+
+#if VMA_USE_STL_LIST
+ #include <list>
+#endif
+
+/*
+Following headers are used in this CONFIGURATION section only, so feel free to
+remove them if not needed.
+*/
+#include <cassert> // for assert
+#include <algorithm> // for min, max
+#include <mutex>
+#include <atomic> // for std::atomic
+
+#ifndef VMA_NULL
+ // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
+ #define VMA_NULL nullptr
+#endif
+
+#if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)
+#include <cstdlib>
+void *aligned_alloc(size_t alignment, size_t size)
+{
+ // alignment must be >= sizeof(void*)
+ if(alignment < sizeof(void*))
+ {
+ alignment = sizeof(void*);
+ }
+
+ return memalign(alignment, size);
+}
+#elif defined(__APPLE__) || defined(__ANDROID__)
+#include <cstdlib>
+void *aligned_alloc(size_t alignment, size_t size)
+{
+ // alignment must be >= sizeof(void*)
+ if(alignment < sizeof(void*))
+ {
+ alignment = sizeof(void*);
+ }
+
+ void *pointer;
+ if(posix_memalign(&pointer, alignment, size) == 0)
+ return pointer;
+ return VMA_NULL;
+}
+#endif
+
+// If your compiler is not compatible with C++11 and definition of
+// aligned_alloc() function is missing, uncommeting following line may help:
+
+//#include <malloc.h>
+
+// Normal assert to check for programmer's errors, especially in Debug configuration.
+#ifndef VMA_ASSERT
+ #ifdef _DEBUG
+ #define VMA_ASSERT(expr) assert(expr)
+ #else
+ #define VMA_ASSERT(expr)
+ #endif
+#endif
+
+// Assert that will be called very often, like inside data structures e.g. operator[].
+// Making it non-empty can make program slow.
+#ifndef VMA_HEAVY_ASSERT
+ #ifdef _DEBUG
+ #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr)
+ #else
+ #define VMA_HEAVY_ASSERT(expr)
+ #endif
+#endif
+
+#ifndef VMA_ALIGN_OF
+ #define VMA_ALIGN_OF(type) (__alignof(type))
+#endif
+
+#ifndef VMA_SYSTEM_ALIGNED_MALLOC
+ #if defined(_WIN32)
+ #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (_aligned_malloc((size), (alignment)))
+ #else
+ #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (aligned_alloc((alignment), (size) ))
+ #endif
+#endif
+
+#ifndef VMA_SYSTEM_FREE
+ #if defined(_WIN32)
+ #define VMA_SYSTEM_FREE(ptr) _aligned_free(ptr)
+ #else
+ #define VMA_SYSTEM_FREE(ptr) free(ptr)
+ #endif
+#endif
+
+#ifndef VMA_MIN
+ #define VMA_MIN(v1, v2) (std::min((v1), (v2)))
+#endif
+
+#ifndef VMA_MAX
+ #define VMA_MAX(v1, v2) (std::max((v1), (v2)))
+#endif
+
+#ifndef VMA_SWAP
+ #define VMA_SWAP(v1, v2) std::swap((v1), (v2))
+#endif
+
+#ifndef VMA_SORT
+ #define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp)
+#endif
+
+#ifndef VMA_DEBUG_LOG
+ #define VMA_DEBUG_LOG(format, ...)
+ /*
+ #define VMA_DEBUG_LOG(format, ...) do { \
+ printf(format, __VA_ARGS__); \
+ printf("\n"); \
+ } while(false)
+ */
+#endif
+
+// Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
+#if VMA_STATS_STRING_ENABLED
+ static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num)
+ {
+ snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));
+ }
+ static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num)
+ {
+ snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));
+ }
+ static inline void VmaPtrToStr(char* outStr, size_t strLen, const void* ptr)
+ {
+ snprintf(outStr, strLen, "%p", ptr);
+ }
+#endif
+
+#ifndef VMA_MUTEX
+ class VmaMutex
+ {
+ public:
+ void Lock() { m_Mutex.lock(); }
+ void Unlock() { m_Mutex.unlock(); }
+ private:
+ std::mutex m_Mutex;
+ };
+ #define VMA_MUTEX VmaMutex
+#endif
+
+// Read-write mutex, where "read" is shared access, "write" is exclusive access.
+#ifndef VMA_RW_MUTEX
+ #if VMA_USE_STL_SHARED_MUTEX
+ // Use std::shared_mutex from C++17.
+ #include <shared_mutex>
+ class VmaRWMutex
+ {
+ public:
+ void LockRead() { m_Mutex.lock_shared(); }
+ void UnlockRead() { m_Mutex.unlock_shared(); }
+ void LockWrite() { m_Mutex.lock(); }
+ void UnlockWrite() { m_Mutex.unlock(); }
+ private:
+ std::shared_mutex m_Mutex;
+ };
+ #define VMA_RW_MUTEX VmaRWMutex
+ #elif defined(_WIN32) && !defined(__MINGW32__)
+ // Use SRWLOCK from WinAPI.
+ class VmaRWMutex
+ {
+ public:
+ VmaRWMutex() { InitializeSRWLock(&m_Lock); }
+ void LockRead() { AcquireSRWLockShared(&m_Lock); }
+ void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }
+ void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }
+ void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }
+ private:
+ SRWLOCK m_Lock;
+ };
+ #define VMA_RW_MUTEX VmaRWMutex
+ #else
+ // Less efficient fallback: Use normal mutex.
+ class VmaRWMutex
+ {
+ public:
+ void LockRead() { m_Mutex.Lock(); }
+ void UnlockRead() { m_Mutex.Unlock(); }
+ void LockWrite() { m_Mutex.Lock(); }
+ void UnlockWrite() { m_Mutex.Unlock(); }
+ private:
+ VMA_MUTEX m_Mutex;
+ };
+ #define VMA_RW_MUTEX VmaRWMutex
+ #endif // #if VMA_USE_STL_SHARED_MUTEX
+#endif // #ifndef VMA_RW_MUTEX
+
+/*
+If providing your own implementation, you need to implement a subset of std::atomic:
+
+- Constructor(uint32_t desired)
+- uint32_t load() const
+- void store(uint32_t desired)
+- bool compare_exchange_weak(uint32_t& expected, uint32_t desired)
+*/
+#ifndef VMA_ATOMIC_UINT32
+ #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
+#endif
+
+#ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
+ /**
+ Every allocation will have its own memory block.
+ Define to 1 for debugging purposes only.
+ */
+ #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)
+#endif
+
+#ifndef VMA_DEBUG_ALIGNMENT
+ /**
+ Minimum alignment of all allocations, in bytes.
+ Set to more than 1 for debugging purposes only. Must be power of two.
+ */
+ #define VMA_DEBUG_ALIGNMENT (1)
+#endif
+
+#ifndef VMA_DEBUG_MARGIN
+ /**
+ Minimum margin before and after every allocation, in bytes.
+ Set nonzero for debugging purposes only.
+ */
+ #define VMA_DEBUG_MARGIN (0)
+#endif
+
+#ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS
+ /**
+ Define this macro to 1 to automatically fill new allocations and destroyed
+ allocations with some bit pattern.
+ */
+ #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0)
+#endif
+
+#ifndef VMA_DEBUG_DETECT_CORRUPTION
+ /**
+ Define this macro to 1 together with non-zero value of VMA_DEBUG_MARGIN to
+ enable writing magic value to the margin before and after every allocation and
+ validating it, so that memory corruptions (out-of-bounds writes) are detected.
+ */
+ #define VMA_DEBUG_DETECT_CORRUPTION (0)
+#endif
+
+#ifndef VMA_DEBUG_GLOBAL_MUTEX
+ /**
+ Set this to 1 for debugging purposes only, to enable single mutex protecting all
+ entry calls to the library. Can be useful for debugging multithreading issues.
+ */
+ #define VMA_DEBUG_GLOBAL_MUTEX (0)
+#endif
+
+#ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
+ /**
+ Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity.
+ Set to more than 1 for debugging purposes only. Must be power of two.
+ */
+ #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
+#endif
+
+#ifndef VMA_SMALL_HEAP_MAX_SIZE
+ /// Maximum size of a memory heap in Vulkan to consider it "small".
+ #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)
+#endif
+
+#ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
+ /// Default size of a block allocated as single VkDeviceMemory from a "large" heap.
+ #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)
+#endif
+
+#ifndef VMA_CLASS_NO_COPY
+ #define VMA_CLASS_NO_COPY(className) \
+ private: \
+ className(const className&) = delete; \
+ className& operator=(const className&) = delete;
+#endif
+
+static const uint32_t VMA_FRAME_INDEX_LOST = UINT32_MAX;
+
+// Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F.
+static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;
+
+static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC;
+static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;
+
+/*******************************************************************************
+END OF CONFIGURATION
+*/
+
+static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;
+
+static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
+ VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
+
+// Returns number of bits set to 1 in (v).
+static inline uint32_t VmaCountBitsSet(uint32_t v)
+{
+ uint32_t c = v - ((v >> 1) & 0x55555555);
+ c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
+ c = ((c >> 4) + c) & 0x0F0F0F0F;
+ c = ((c >> 8) + c) & 0x00FF00FF;
+ c = ((c >> 16) + c) & 0x0000FFFF;
+ return c;
+}
+
+// Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
+// Use types like uint32_t, uint64_t as T.
+template <typename T>
+static inline T VmaAlignUp(T val, T align)
+{
+ return (val + align - 1) / align * align;
+}
+// Aligns given value down to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 8.
+// Use types like uint32_t, uint64_t as T.
+template <typename T>
+static inline T VmaAlignDown(T val, T align)
+{
+ return val / align * align;
+}
+
+// Division with mathematical rounding to nearest number.
+template <typename T>
+static inline T VmaRoundDiv(T x, T y)
+{
+ return (x + (y / (T)2)) / y;
+}
+
+/*
+Returns true if given number is a power of two.
+T must be unsigned integer number or signed integer but always nonnegative.
+For 0 returns true.
+*/
+template <typename T>
+inline bool VmaIsPow2(T x)
+{
+ return (x & (x-1)) == 0;
+}
+
+// Returns smallest power of 2 greater or equal to v.
+static inline uint32_t VmaNextPow2(uint32_t v)
+{
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ v++;
+ return v;
+}
+static inline uint64_t VmaNextPow2(uint64_t v)
+{
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ v |= v >> 32;
+ v++;
+ return v;
+}
+
+// Returns largest power of 2 less or equal to v.
+static inline uint32_t VmaPrevPow2(uint32_t v)
+{
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ v = v ^ (v >> 1);
+ return v;
+}
+static inline uint64_t VmaPrevPow2(uint64_t v)
+{
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ v |= v >> 32;
+ v = v ^ (v >> 1);
+ return v;
+}
+
+static inline bool VmaStrIsEmpty(const char* pStr)
+{
+ return pStr == VMA_NULL || *pStr == '\0';
+}
+
+static const char* VmaAlgorithmToStr(uint32_t algorithm)
+{
+ switch(algorithm)
+ {
+ case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
+ return "Linear";
+ case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
+ return "Buddy";
+ case 0:
+ return "Default";
+ default:
+ VMA_ASSERT(0);
+ return "";
+ }
+}
+
+#ifndef VMA_SORT
+
+template<typename Iterator, typename Compare>
+Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp)
+{
+ Iterator centerValue = end; --centerValue;
+ Iterator insertIndex = beg;
+ for(Iterator memTypeIndex = beg; memTypeIndex < centerValue; ++memTypeIndex)
+ {
+ if(cmp(*memTypeIndex, *centerValue))
+ {
+ if(insertIndex != memTypeIndex)
+ {
+ VMA_SWAP(*memTypeIndex, *insertIndex);
+ }
+ ++insertIndex;
+ }
+ }
+ if(insertIndex != centerValue)
+ {
+ VMA_SWAP(*insertIndex, *centerValue);
+ }
+ return insertIndex;
+}
+
+template<typename Iterator, typename Compare>
+void VmaQuickSort(Iterator beg, Iterator end, Compare cmp)
+{
+ if(beg < end)
+ {
+ Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp);
+ VmaQuickSort<Iterator, Compare>(beg, it, cmp);
+ VmaQuickSort<Iterator, Compare>(it + 1, end, cmp);
+ }
+}
+
+#define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp)
+
+#endif // #ifndef VMA_SORT
+
+/*
+Returns true if two memory blocks occupy overlapping pages.
+ResourceA must be in less memory offset than ResourceB.
+
+Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
+chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
+*/
+static inline bool VmaBlocksOnSamePage(
+ VkDeviceSize resourceAOffset,
+ VkDeviceSize resourceASize,
+ VkDeviceSize resourceBOffset,
+ VkDeviceSize pageSize)
+{
+ VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
+ VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
+ VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
+ VkDeviceSize resourceBStart = resourceBOffset;
+ VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
+ return resourceAEndPage == resourceBStartPage;
+}
+
+enum VmaSuballocationType
+{
+ VMA_SUBALLOCATION_TYPE_FREE = 0,
+ VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
+ VMA_SUBALLOCATION_TYPE_BUFFER = 2,
+ VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
+ VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
+ VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
+ VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
+};
+
+/*
+Returns true if given suballocation types could conflict and must respect
+VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
+or linear image and another one is optimal image. If type is unknown, behave
+conservatively.
+*/
+static inline bool VmaIsBufferImageGranularityConflict(
+ VmaSuballocationType suballocType1,
+ VmaSuballocationType suballocType2)
+{
+ if(suballocType1 > suballocType2)
+ {
+ VMA_SWAP(suballocType1, suballocType2);
+ }
+
+ switch(suballocType1)
+ {
+ case VMA_SUBALLOCATION_TYPE_FREE:
+ return false;
+ case VMA_SUBALLOCATION_TYPE_UNKNOWN:
+ return true;
+ case VMA_SUBALLOCATION_TYPE_BUFFER:
+ return
+ suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
+ suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
+ case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
+ return
+ suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
+ suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
+ suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
+ case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
+ return
+ suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
+ case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
+ return false;
+ default:
+ VMA_ASSERT(0);
+ return true;
+ }
+}
+
+static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)
+{
+ uint32_t* pDst = (uint32_t*)((char*)pData + offset);
+ const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
+ for(size_t i = 0; i != numberCount; ++i, ++pDst)
+ {
+ *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;
+ }
+}
+
+static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)
+{
+ const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);
+ const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);
+ for(size_t i = 0; i != numberCount; ++i, ++pSrc)
+ {
+ if(*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+// Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
+struct VmaMutexLock
+{
+ VMA_CLASS_NO_COPY(VmaMutexLock)
+public:
+ VmaMutexLock(VMA_MUTEX& mutex, bool useMutex) :
+ m_pMutex(useMutex ? &mutex : VMA_NULL)
+ { if(m_pMutex) { m_pMutex->Lock(); } }
+ ~VmaMutexLock()
+ { if(m_pMutex) { m_pMutex->Unlock(); } }
+private:
+ VMA_MUTEX* m_pMutex;
+};
+
+// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.
+struct VmaMutexLockRead
+{
+ VMA_CLASS_NO_COPY(VmaMutexLockRead)
+public:
+ VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :
+ m_pMutex(useMutex ? &mutex : VMA_NULL)
+ { if(m_pMutex) { m_pMutex->LockRead(); } }
+ ~VmaMutexLockRead() { if(m_pMutex) { m_pMutex->UnlockRead(); } }
+private:
+ VMA_RW_MUTEX* m_pMutex;
+};
+
+// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.
+struct VmaMutexLockWrite
+{
+ VMA_CLASS_NO_COPY(VmaMutexLockWrite)
+public:
+ VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) :
+ m_pMutex(useMutex ? &mutex : VMA_NULL)
+ { if(m_pMutex) { m_pMutex->LockWrite(); } }
+ ~VmaMutexLockWrite() { if(m_pMutex) { m_pMutex->UnlockWrite(); } }
+private:
+ VMA_RW_MUTEX* m_pMutex;
+};
+
+#if VMA_DEBUG_GLOBAL_MUTEX
+ static VMA_MUTEX gDebugGlobalMutex;
+ #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);
+#else
+ #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
+#endif
+
+// Minimum size of a free suballocation to register it in the free suballocation collection.
+static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
+
+/*
+Performs binary search and returns iterator to first element that is greater or
+equal to (key), according to comparison (cmp).
+
+Cmp should return true if first argument is less than second argument.
+
+Returned value is the found element, if present in the collection or place where
+new element with value (key) should be inserted.
+*/
+template <typename CmpLess, typename IterT, typename KeyT>
+static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, CmpLess cmp)
+{
+ size_t down = 0, up = (end - beg);
+ while(down < up)
+ {
+ const size_t mid = (down + up) / 2;
+ if(cmp(*(beg+mid), key))
+ {
+ down = mid + 1;
+ }
+ else
+ {
+ up = mid;
+ }
+ }
+ return beg + down;
+}
+
+/*
+Returns true if all pointers in the array are not-null and unique.
+Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.
+T must be pointer type, e.g. VmaAllocation, VmaPool.
+*/
+template<typename T>
+static bool VmaValidatePointerArray(uint32_t count, const T* arr)
+{
+ for(uint32_t i = 0; i < count; ++i)
+ {
+ const T iPtr = arr[i];
+ if(iPtr == VMA_NULL)
+ {
+ return false;
+ }
+ for(uint32_t j = i + 1; j < count; ++j)
+ {
+ if(iPtr == arr[j])
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Memory allocation
+
+static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
+{
+ if((pAllocationCallbacks != VMA_NULL) &&
+ (pAllocationCallbacks->pfnAllocation != VMA_NULL))
+ {
+ return (*pAllocationCallbacks->pfnAllocation)(
+ pAllocationCallbacks->pUserData,
+ size,
+ alignment,
+ VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+ }
+ else
+ {
+ return VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
+ }
+}
+
+static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
+{
+ if((pAllocationCallbacks != VMA_NULL) &&
+ (pAllocationCallbacks->pfnFree != VMA_NULL))
+ {
+ (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
+ }
+ else
+ {
+ VMA_SYSTEM_FREE(ptr);
+ }
+}
+
+template<typename T>
+static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
+{
+ return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
+}
+
+template<typename T>
+static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
+{
+ return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
+}
+
+#define vma_new(allocator, type) new(VmaAllocate<type>(allocator))(type)
+
+#define vma_new_array(allocator, type, count) new(VmaAllocateArray<type>((allocator), (count)))(type)
+
+template<typename T>
+static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
+{
+ ptr->~T();
+ VmaFree(pAllocationCallbacks, ptr);
+}
+
+template<typename T>
+static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
+{
+ if(ptr != VMA_NULL)
+ {
+ for(size_t i = count; i--; )
+ {
+ ptr[i].~T();
+ }
+ VmaFree(pAllocationCallbacks, ptr);
+ }
+}
+
+// STL-compatible allocator.
+template<typename T>
+class VmaStlAllocator
+{
+public:
+ const VkAllocationCallbacks* const m_pCallbacks;
+ typedef T value_type;
+
+ VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
+ template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
+
+ T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
+ void deallocate(T* p, size_t /*n*/) { VmaFree(m_pCallbacks, p); }
+
+ template<typename U>
+ bool operator==(const VmaStlAllocator<U>& rhs) const
+ {
+ return m_pCallbacks == rhs.m_pCallbacks;
+ }
+ template<typename U>
+ bool operator!=(const VmaStlAllocator<U>& rhs) const
+ {
+ return m_pCallbacks != rhs.m_pCallbacks;
+ }
+
+ VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete;
+};
+
+#if VMA_USE_STL_VECTOR
+
+#define VmaVector std::vector
+
+template<typename T, typename allocatorT>
+static void VmaVectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
+{
+ vec.insert(vec.begin() + index, item);
+}
+
+template<typename T, typename allocatorT>
+static void VmaVectorRemove(std::vector<T, allocatorT>& vec, size_t index)
+{
+ vec.erase(vec.begin() + index);
+}
+
+#else // #if VMA_USE_STL_VECTOR
+
+/* Class with interface compatible with subset of std::vector.
+T must be POD because constructors and destructors are not called and memcpy is
+used for these objects. */
+template<typename T, typename AllocatorT>
+class VmaVector
+{
+public:
+ typedef T value_type;
+
+ VmaVector(const AllocatorT& allocator) :
+ m_Allocator(allocator),
+ m_pArray(VMA_NULL),
+ m_Count(0),
+ m_Capacity(0)
+ {
+ }
+
+ VmaVector(size_t count, const AllocatorT& allocator) :
+ m_Allocator(allocator),
+ m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),
+ m_Count(count),
+ m_Capacity(count)
+ {
+ }
+
+ VmaVector(const VmaVector<T, AllocatorT>& src) :
+ m_Allocator(src.m_Allocator),
+ m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),
+ m_Count(src.m_Count),
+ m_Capacity(src.m_Count)
+ {
+ if(m_Count != 0)
+ {
+ memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
+ }
+ }
+
+ ~VmaVector()
+ {
+ VmaFree(m_Allocator.m_pCallbacks, m_pArray);
+ }
+
+ VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
+ {
+ if(&rhs != this)
+ {
+ resize(rhs.m_Count);
+ if(m_Count != 0)
+ {
+ memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
+ }
+ }
+ return *this;
+ }
+
+ bool empty() const { return m_Count == 0; }
+ size_t size() const { return m_Count; }
+ T* data() { return m_pArray; }
+ const T* data() const { return m_pArray; }
+
+ T& operator[](size_t index)
+ {
+ VMA_HEAVY_ASSERT(index < m_Count);
+ return m_pArray[index];
+ }
+ const T& operator[](size_t index) const
+ {
+ VMA_HEAVY_ASSERT(index < m_Count);
+ return m_pArray[index];
+ }
+
+ T& front()
+ {
+ VMA_HEAVY_ASSERT(m_Count > 0);
+ return m_pArray[0];
+ }
+ const T& front() const
+ {
+ VMA_HEAVY_ASSERT(m_Count > 0);
+ return m_pArray[0];
+ }
+ T& back()
+ {
+ VMA_HEAVY_ASSERT(m_Count > 0);
+ return m_pArray[m_Count - 1];
+ }
+ const T& back() const
+ {
+ VMA_HEAVY_ASSERT(m_Count > 0);
+ return m_pArray[m_Count - 1];
+ }
+
+ void reserve(size_t newCapacity, bool freeMemory = false)
+ {
+ newCapacity = VMA_MAX(newCapacity, m_Count);
+
+ if((newCapacity < m_Capacity) && !freeMemory)
+ {
+ newCapacity = m_Capacity;
+ }
+
+ if(newCapacity != m_Capacity)
+ {
+ T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
+ if(m_Count != 0)
+ {
+ memcpy(newArray, m_pArray, m_Count * sizeof(T));
+ }
+ VmaFree(m_Allocator.m_pCallbacks, m_pArray);
+ m_Capacity = newCapacity;
+ m_pArray = newArray;
+ }
+ }
+
+ void resize(size_t newCount, bool freeMemory = false)
+ {
+ size_t newCapacity = m_Capacity;
+ if(newCount > m_Capacity)
+ {
+ newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
+ }
+ else if(freeMemory)
+ {
+ newCapacity = newCount;
+ }
+
+ if(newCapacity != m_Capacity)
+ {
+ T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
+ const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
+ if(elementsToCopy != 0)
+ {
+ memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
+ }
+ VmaFree(m_Allocator.m_pCallbacks, m_pArray);
+ m_Capacity = newCapacity;
+ m_pArray = newArray;
+ }
+
+ m_Count = newCount;
+ }
+
+ void clear(bool freeMemory = false)
+ {
+ resize(0, freeMemory);
+ }
+
+ void insert(size_t index, const T& src)
+ {
+ VMA_HEAVY_ASSERT(index <= m_Count);
+ const size_t oldCount = size();
+ resize(oldCount + 1);
+ if(index < oldCount)
+ {
+ memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
+ }
+ m_pArray[index] = src;
+ }
+
+ void remove(size_t index)
+ {
+ VMA_HEAVY_ASSERT(index < m_Count);
+ const size_t oldCount = size();
+ if(index < oldCount - 1)
+ {
+ memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
+ }
+ resize(oldCount - 1);
+ }
+
+ void push_back(const T& src)
+ {
+ const size_t newIndex = size();
+ resize(newIndex + 1);
+ m_pArray[newIndex] = src;
+ }
+
+ void pop_back()
+ {
+ VMA_HEAVY_ASSERT(m_Count > 0);
+ resize(size() - 1);
+ }
+
+ void push_front(const T& src)
+ {
+ insert(0, src);
+ }
+
+ void pop_front()
+ {
+ VMA_HEAVY_ASSERT(m_Count > 0);
+ remove(0);
+ }
+
+ typedef T* iterator;
+
+ iterator begin() { return m_pArray; }
+ iterator end() { return m_pArray + m_Count; }
+
+private:
+ AllocatorT m_Allocator;
+ T* m_pArray;
+ size_t m_Count;
+ size_t m_Capacity;
+};
+
+template<typename T, typename allocatorT>
+static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
+{
+ vec.insert(index, item);
+}
+
+template<typename T, typename allocatorT>
+static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
+{
+ vec.remove(index);
+}
+
+#endif // #if VMA_USE_STL_VECTOR
+
+template<typename CmpLess, typename VectorT>
+size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)
+{
+ const size_t indexToInsert = VmaBinaryFindFirstNotLess(
+ vector.data(),
+ vector.data() + vector.size(),
+ value,
+ CmpLess()) - vector.data();
+ VmaVectorInsert(vector, indexToInsert, value);
+ return indexToInsert;
+}
+
+template<typename CmpLess, typename VectorT>
+bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)
+{
+ CmpLess comparator;
+ typename VectorT::iterator it = VmaBinaryFindFirstNotLess(
+ vector.begin(),
+ vector.end(),
+ value,
+ comparator);
+ if((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))
+ {
+ size_t indexToRemove = it - vector.begin();
+ VmaVectorRemove(vector, indexToRemove);
+ return true;
+ }
+ return false;
+}
+
+template<typename CmpLess, typename IterT, typename KeyT>
+IterT VmaVectorFindSorted(const IterT& beg, const IterT& end, const KeyT& value)
+{
+ CmpLess comparator;
+ IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(
+ beg, end, value, comparator);
+ if(it == end ||
+ (!comparator(*it, value) && !comparator(value, *it)))
+ {
+ return it;
+ }
+ return end;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// class VmaPoolAllocator
+
+/*
+Allocator for objects of type T using a list of arrays (pools) to speed up
+allocation. Number of elements that can be allocated is not bounded because
+allocator can create multiple blocks.
+*/
+template<typename T>
+class VmaPoolAllocator
+{
+ VMA_CLASS_NO_COPY(VmaPoolAllocator)
+public:
+ VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock);
+ ~VmaPoolAllocator();
+ void Clear();
+ T* Alloc();
+ void Free(T* ptr);
+
+private:
+ union Item
+ {
+ uint32_t NextFreeIndex;
+ T Value;
+ };
+
+ struct ItemBlock
+ {
+ Item* pItems;
+ uint32_t FirstFreeIndex;
+ };
+
+ const VkAllocationCallbacks* m_pAllocationCallbacks;
+ size_t m_ItemsPerBlock;
+ VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
+
+ ItemBlock& CreateNewBlock();
+};
+
+template<typename T>
+VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock) :
+ m_pAllocationCallbacks(pAllocationCallbacks),
+ m_ItemsPerBlock(itemsPerBlock),
+ m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
+{
+ VMA_ASSERT(itemsPerBlock > 0);
+}
+
+template<typename T>
+VmaPoolAllocator<T>::~VmaPoolAllocator()
+{
+ Clear();
+}
+
+template<typename T>
+void VmaPoolAllocator<T>::Clear()
+{
+ for(size_t i = m_ItemBlocks.size(); i--; )
+ vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemsPerBlock);
+ m_ItemBlocks.clear();
+}
+
+template<typename T>
+T* VmaPoolAllocator<T>::Alloc()
+{
+ for(size_t i = m_ItemBlocks.size(); i--; )
+ {
+ ItemBlock& block = m_ItemBlocks[i];
+ // This block has some free items: Use first one.
+ if(block.FirstFreeIndex != UINT32_MAX)
+ {
+ Item* const pItem = &block.pItems[block.FirstFreeIndex];
+ block.FirstFreeIndex = pItem->NextFreeIndex;
+ return &pItem->Value;
+ }
+ }
+
+ // No block has free item: Create new one and use it.
+ ItemBlock& newBlock = CreateNewBlock();
+ Item* const pItem = &newBlock.pItems[0];
+ newBlock.FirstFreeIndex = pItem->NextFreeIndex;
+ return &pItem->Value;
+}
+
+template<typename T>
+void VmaPoolAllocator<T>::Free(T* ptr)
+{
+ // Search all memory blocks to find ptr.
+ for(size_t i = 0; i < m_ItemBlocks.size(); ++i)
+ {
+ ItemBlock& block = m_ItemBlocks[i];
+
+ // Casting to union.
+ Item* pItemPtr;
+ memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
+
+ // Check if pItemPtr is in address range of this block.
+ if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + m_ItemsPerBlock))
+ {
+ const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
+ pItemPtr->NextFreeIndex = block.FirstFreeIndex;
+ block.FirstFreeIndex = index;
+ return;
+ }
+ }
+ VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
+}
+
+template<typename T>
+typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
+{
+ ItemBlock newBlock = {
+ vma_new_array(m_pAllocationCallbacks, Item, m_ItemsPerBlock), 0 };
+
+ m_ItemBlocks.push_back(newBlock);
+
+ // Setup singly-linked list of all free items in this block.
+ for(uint32_t i = 0; i < m_ItemsPerBlock - 1; ++i)
+ newBlock.pItems[i].NextFreeIndex = i + 1;
+ newBlock.pItems[m_ItemsPerBlock - 1].NextFreeIndex = UINT32_MAX;
+ return m_ItemBlocks.back();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// class VmaRawList, VmaList
+
+#if VMA_USE_STL_LIST
+
+#define VmaList std::list
+
+#else // #if VMA_USE_STL_LIST
+
+template<typename T>
+struct VmaListItem
+{
+ VmaListItem* pPrev;
+ VmaListItem* pNext;
+ T Value;
+};
+
+// Doubly linked list.
+template<typename T>
+class VmaRawList
+{
+ VMA_CLASS_NO_COPY(VmaRawList)
+public:
+ typedef VmaListItem<T> ItemType;
+
+ VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
+ ~VmaRawList();
+ void Clear();
+
+ size_t GetCount() const { return m_Count; }
+ bool IsEmpty() const { return m_Count == 0; }
+
+ ItemType* Front() { return m_pFront; }
+ const ItemType* Front() const { return m_pFront; }
+ ItemType* Back() { return m_pBack; }
+ const ItemType* Back() const { return m_pBack; }
+
+ ItemType* PushBack();
+ ItemType* PushFront();
+ ItemType* PushBack(const T& value);
+ ItemType* PushFront(const T& value);
+ void PopBack();
+ void PopFront();
+
+ // Item can be null - it means PushBack.
+ ItemType* InsertBefore(ItemType* pItem);
+ // Item can be null - it means PushFront.
+ ItemType* InsertAfter(ItemType* pItem);
+
+ ItemType* InsertBefore(ItemType* pItem, const T& value);
+ ItemType* InsertAfter(ItemType* pItem, const T& value);
+
+ void Remove(ItemType* pItem);
+
+private:
+ const VkAllocationCallbacks* const m_pAllocationCallbacks;
+ VmaPoolAllocator<ItemType> m_ItemAllocator;
+ ItemType* m_pFront;
+ ItemType* m_pBack;
+ size_t m_Count;
+};
+
+template<typename T>
+VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
+ m_pAllocationCallbacks(pAllocationCallbacks),
+ m_ItemAllocator(pAllocationCallbacks, 128),
+ m_pFront(VMA_NULL),
+ m_pBack(VMA_NULL),
+ m_Count(0)
+{
+}
+
+template<typename T>
+VmaRawList<T>::~VmaRawList()
+{
+ // Intentionally not calling Clear, because that would be unnecessary
+ // computations to return all items to m_ItemAllocator as free.
+}
+
+template<typename T>
+void VmaRawList<T>::Clear()
+{
+ if(IsEmpty() == false)
+ {
+ ItemType* pItem = m_pBack;
+ while(pItem != VMA_NULL)
+ {
+ ItemType* const pPrevItem = pItem->pPrev;
+ m_ItemAllocator.Free(pItem);
+ pItem = pPrevItem;
+ }
+ m_pFront = VMA_NULL;
+ m_pBack = VMA_NULL;
+ m_Count = 0;
+ }
+}
+
+template<typename T>
+VmaListItem<T>* VmaRawList<T>::PushBack()
+{
+ ItemType* const pNewItem = m_ItemAllocator.Alloc();
+ pNewItem->pNext = VMA_NULL;
+ if(IsEmpty())
+ {
+ pNewItem->pPrev = VMA_NULL;
+ m_pFront = pNewItem;
+ m_pBack = pNewItem;
+ m_Count = 1;
+ }
+ else
+ {
+ pNewItem->pPrev = m_pBack;
+ m_pBack->pNext = pNewItem;
+ m_pBack = pNewItem;
+ ++m_Count;
+ }
+ return pNewItem;
+}
+
+template<typename T>
+VmaListItem<T>* VmaRawList<T>::PushFront()
+{
+ ItemType* const pNewItem = m_ItemAllocator.Alloc();
+ pNewItem->pPrev = VMA_NULL;
+ if(IsEmpty())
+ {
+ pNewItem->pNext = VMA_NULL;
+ m_pFront = pNewItem;
+ m_pBack = pNewItem;
+ m_Count = 1;
+ }
+ else
+ {
+ pNewItem->pNext = m_pFront;
+ m_pFront->pPrev = pNewItem;
+ m_pFront = pNewItem;
+ ++m_Count;
+ }
+ return pNewItem;
+}
+
+template<typename T>
+VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
+{
+ ItemType* const pNewItem = PushBack();
+ pNewItem->Value = value;
+ return pNewItem;
+}
+
+template<typename T>
+VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
+{
+ ItemType* const pNewItem = PushFront();
+ pNewItem->Value = value;
+ return pNewItem;
+}
+
+template<typename T>
+void VmaRawList<T>::PopBack()
+{
+ VMA_HEAVY_ASSERT(m_Count > 0);
+ ItemType* const pBackItem = m_pBack;
+ ItemType* const pPrevItem = pBackItem->pPrev;
+ if(pPrevItem != VMA_NULL)
+ {
+ pPrevItem->pNext = VMA_NULL;
+ }
+ m_pBack = pPrevItem;
+ m_ItemAllocator.Free(pBackItem);
+ --m_Count;
+}
+
+template<typename T>
+void VmaRawList<T>::PopFront()
+{
+ VMA_HEAVY_ASSERT(m_Count > 0);
+ ItemType* const pFrontItem = m_pFront;
+ ItemType* const pNextItem = pFrontItem->pNext;
+ if(pNextItem != VMA_NULL)
+ {
+ pNextItem->pPrev = VMA_NULL;
+ }
+ m_pFront = pNextItem;
+ m_ItemAllocator.Free(pFrontItem);
+ --m_Count;
+}
+
+template<typename T>
+void VmaRawList<T>::Remove(ItemType* pItem)
+{
+ VMA_HEAVY_ASSERT(pItem != VMA_NULL);
+ VMA_HEAVY_ASSERT(m_Count > 0);
+
+ if(pItem->pPrev != VMA_NULL)
+ {
+ pItem->pPrev->pNext = pItem->pNext;
+ }
+ else
+ {
+ VMA_HEAVY_ASSERT(m_pFront == pItem);
+ m_pFront = pItem->pNext;
+ }
+
+ if(pItem->pNext != VMA_NULL)
+ {
+ pItem->pNext->pPrev = pItem->pPrev;
+ }
+ else
+ {
+ VMA_HEAVY_ASSERT(m_pBack == pItem);
+ m_pBack = pItem->pPrev;
+ }
+
+ m_ItemAllocator.Free(pItem);
+ --m_Count;
+}
+
+template<typename T>
+VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
+{
+ if(pItem != VMA_NULL)
+ {
+ ItemType* const prevItem = pItem->pPrev;
+ ItemType* const newItem = m_ItemAllocator.Alloc();
+ newItem->pPrev = prevItem;
+ newItem->pNext = pItem;
+ pItem->pPrev = newItem;
+ if(prevItem != VMA_NULL)
+ {
+ prevItem->pNext = newItem;
+ }
+ else
+ {
+ VMA_HEAVY_ASSERT(m_pFront == pItem);
+ m_pFront = newItem;
+ }
+ ++m_Count;
+ return newItem;
+ }
+ else
+ return PushBack();
+}
+
+template<typename T>
+VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
+{
+ if(pItem != VMA_NULL)
+ {
+ ItemType* const nextItem = pItem->pNext;
+ ItemType* const newItem = m_ItemAllocator.Alloc();
+ newItem->pNext = nextItem;
+ newItem->pPrev = pItem;
+ pItem->pNext = newItem;
+ if(nextItem != VMA_NULL)
+ {
+ nextItem->pPrev = newItem;
+ }
+ else
+ {
+ VMA_HEAVY_ASSERT(m_pBack == pItem);
+ m_pBack = newItem;
+ }
+ ++m_Count;
+ return newItem;
+ }
+ else
+ return PushFront();
+}
+
+template<typename T>
+VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
+{
+ ItemType* const newItem = InsertBefore(pItem);
+ newItem->Value = value;
+ return newItem;
+}
+
+template<typename T>
+VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
+{
+ ItemType* const newItem = InsertAfter(pItem);
+ newItem->Value = value;
+ return newItem;
+}
+
+template<typename T, typename AllocatorT>
+class VmaList
+{
+ VMA_CLASS_NO_COPY(VmaList)
+public:
+ class iterator
+ {
+ public:
+ iterator() :
+ m_pList(VMA_NULL),
+ m_pItem(VMA_NULL)
+ {
+ }
+
+ T& operator*() const
+ {
+ VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
+ return m_pItem->Value;
+ }
+ T* operator->() const
+ {
+ VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
+ return &m_pItem->Value;
+ }
+
+ iterator& operator++()
+ {
+ VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
+ m_pItem = m_pItem->pNext;
+ return *this;
+ }
+ iterator& operator--()
+ {
+ if(m_pItem != VMA_NULL)
+ {
+ m_pItem = m_pItem->pPrev;
+ }
+ else
+ {
+ VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
+ m_pItem = m_pList->Back();
+ }
+ return *this;
+ }
+
+ iterator operator++(int)
+ {
+ iterator result = *this;
+ ++*this;
+ return result;
+ }
+ iterator operator--(int)
+ {
+ iterator result = *this;
+ --*this;
+ return result;
+ }
+
+ bool operator==(const iterator& rhs) const
+ {
+ VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
+ return m_pItem == rhs.m_pItem;
+ }
+ bool operator!=(const iterator& rhs) const
+ {
+ VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
+ return m_pItem != rhs.m_pItem;
+ }
+
+ private:
+ VmaRawList<T>* m_pList;
+ VmaListItem<T>* m_pItem;
+
+ iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
+ m_pList(pList),
+ m_pItem(pItem)
+ {
+ }
+
+ friend class VmaList<T, AllocatorT>;
+ };
+
+ class const_iterator
+ {
+ public:
+ const_iterator() :
+ m_pList(VMA_NULL),
+ m_pItem(VMA_NULL)
+ {
+ }
+
+ const_iterator(const iterator& src) :
+ m_pList(src.m_pList),
+ m_pItem(src.m_pItem)
+ {
+ }
+
+ const T& operator*() const
+ {
+ VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
+ return m_pItem->Value;
+ }
+ const T* operator->() const
+ {
+ VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
+ return &m_pItem->Value;
+ }
+
+ const_iterator& operator++()
+ {
+ VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
+ m_pItem = m_pItem->pNext;
+ return *this;
+ }
+ const_iterator& operator--()
+ {
+ if(m_pItem != VMA_NULL)
+ {
+ m_pItem = m_pItem->pPrev;
+ }
+ else
+ {
+ VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
+ m_pItem = m_pList->Back();
+ }
+ return *this;
+ }
+
+ const_iterator operator++(int)
+ {
+ const_iterator result = *this;
+ ++*this;
+ return result;
+ }
+ const_iterator operator--(int)
+ {
+ const_iterator result = *this;
+ --*this;
+ return result;
+ }
+
+ bool operator==(const const_iterator& rhs) const
+ {
+ VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
+ return m_pItem == rhs.m_pItem;
+ }
+ bool operator!=(const const_iterator& rhs) const
+ {
+ VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
+ return m_pItem != rhs.m_pItem;
+ }
+
+ private:
+ const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
+ m_pList(pList),
+ m_pItem(pItem)
+ {
+ }
+
+ const VmaRawList<T>* m_pList;
+ const VmaListItem<T>* m_pItem;
+
+ friend class VmaList<T, AllocatorT>;
+ };
+
+ VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
+
+ bool empty() const { return m_RawList.IsEmpty(); }
+ size_t size() const { return m_RawList.GetCount(); }
+
+ iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
+ iterator end() { return iterator(&m_RawList, VMA_NULL); }
+
+ const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
+ const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
+
+ void clear() { m_RawList.Clear(); }
+ void push_back(const T& value) { m_RawList.PushBack(value); }
+ void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
+ iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
+
+private:
+ VmaRawList<T> m_RawList;
+};
+
+#endif // #if VMA_USE_STL_LIST
+
+////////////////////////////////////////////////////////////////////////////////
+// class VmaMap
+
+// Unused in this version.
+#if 0
+
+#if VMA_USE_STL_UNORDERED_MAP
+
+#define VmaPair std::pair
+
+#define VMA_MAP_TYPE(KeyT, ValueT) \
+ std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
+
+#else // #if VMA_USE_STL_UNORDERED_MAP
+
+template<typename T1, typename T2>
+struct VmaPair
+{
+ T1 first;
+ T2 second;
+
+ VmaPair() : first(), second() { }
+ VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
+};
+
+/* Class compatible with subset of interface of std::unordered_map.
+KeyT, ValueT must be POD because they will be stored in VmaVector.
+*/
+template<typename KeyT, typename ValueT>
+class VmaMap
+{
+public:
+ typedef VmaPair<KeyT, ValueT> PairType;
+ typedef PairType* iterator;
+
+ VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
+
+ iterator begin() { return m_Vector.begin(); }
+ iterator end() { return m_Vector.end(); }
+
+ void insert(const PairType& pair);
+ iterator find(const KeyT& key);
+ void erase(iterator it);
+
+private:
+ VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
+};
+
+#define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
+
+template<typename FirstT, typename SecondT>
+struct VmaPairFirstLess
+{
+ bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
+ {
+ return lhs.first < rhs.first;
+ }
+ bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
+ {
+ return lhs.first < rhsFirst;
+ }
+};
+
+template<typename KeyT, typename ValueT>
+void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
+{
+ const size_t indexToInsert = VmaBinaryFindFirstNotLess(
+ m_Vector.data(),
+ m_Vector.data() + m_Vector.size(),
+ pair,
+ VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
+ VmaVectorInsert(m_Vector, indexToInsert, pair);
+}
+
+template<typename KeyT, typename ValueT>
+VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
+{
+ PairType* it = VmaBinaryFindFirstNotLess(
+ m_Vector.data(),
+ m_Vector.data() + m_Vector.size(),
+ key,
+ VmaPairFirstLess<KeyT, ValueT>());
+ if((it != m_Vector.end()) && (it->first == key))
+ {
+ return it;
+ }
+ else
+ {
+ return m_Vector.end();
+ }
+}
+
+template<typename KeyT, typename ValueT>
+void VmaMap<KeyT, ValueT>::erase(iterator it)
+{
+ VmaVectorRemove(m_Vector, it - m_Vector.begin());
+}
+
+#endif // #if VMA_USE_STL_UNORDERED_MAP
+
+#endif // #if 0
+
+////////////////////////////////////////////////////////////////////////////////
+
+class VmaDeviceMemoryBlock;
+
+enum VMA_CACHE_OPERATION { VMA_CACHE_FLUSH, VMA_CACHE_INVALIDATE };
+
+struct VmaAllocation_T
+{
+ VMA_CLASS_NO_COPY(VmaAllocation_T)
+private:
+ static const uint8_t MAP_COUNT_FLAG_PERSISTENT_MAP = 0x80;
+
+ enum FLAGS
+ {
+ FLAG_USER_DATA_STRING = 0x01,
+ };
+
+public:
+ enum ALLOCATION_TYPE
+ {
+ ALLOCATION_TYPE_NONE,
+ ALLOCATION_TYPE_BLOCK,
+ ALLOCATION_TYPE_DEDICATED,
+ };
+
+ VmaAllocation_T(uint32_t currentFrameIndex, bool userDataString) :
+ m_Alignment(1),
+ m_Size(0),
+ m_pUserData(VMA_NULL),
+ m_LastUseFrameIndex(currentFrameIndex),
+ m_Type((uint8_t)ALLOCATION_TYPE_NONE),
+ m_SuballocationType((uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN),
+ m_MapCount(0),
+ m_Flags(userDataString ? (uint8_t)FLAG_USER_DATA_STRING : 0)
+ {
+#if VMA_STATS_STRING_ENABLED
+ m_CreationFrameIndex = currentFrameIndex;
+ m_BufferImageUsage = 0;
+#endif
+ }
+
+ ~VmaAllocation_T()
+ {
+ VMA_ASSERT((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) == 0 && "Allocation was not unmapped before destruction.");
+
+ // Check if owned string was freed.
+ VMA_ASSERT(m_pUserData == VMA_NULL);
+ }
+
+ void InitBlockAllocation(
+ VmaPool hPool,
+ VmaDeviceMemoryBlock* block,
+ VkDeviceSize offset,
+ VkDeviceSize alignment,
+ VkDeviceSize size,
+ VmaSuballocationType suballocationType,
+ bool mapped,
+ bool canBecomeLost)
+ {
+ VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
+ VMA_ASSERT(block != VMA_NULL);
+ m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
+ m_Alignment = alignment;
+ m_Size = size;
+ m_MapCount = mapped ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
+ m_SuballocationType = (uint8_t)suballocationType;
+ m_BlockAllocation.m_hPool = hPool;
+ m_BlockAllocation.m_Block = block;
+ m_BlockAllocation.m_Offset = offset;
+ m_BlockAllocation.m_CanBecomeLost = canBecomeLost;
+ }
+
+ void InitLost()
+ {
+ VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
+ VMA_ASSERT(m_LastUseFrameIndex.load() == VMA_FRAME_INDEX_LOST);
+ m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;
+ m_BlockAllocation.m_hPool = VK_NULL_HANDLE;
+ m_BlockAllocation.m_Block = VMA_NULL;
+ m_BlockAllocation.m_Offset = 0;
+ m_BlockAllocation.m_CanBecomeLost = true;
+ }
+
+ void ChangeBlockAllocation(
+ VmaAllocator hAllocator,
+ VmaDeviceMemoryBlock* block,
+ VkDeviceSize offset);
+
+ void ChangeSize(VkDeviceSize newSize);
+ void ChangeOffset(VkDeviceSize newOffset);
+
+ // pMappedData not null means allocation is created with MAPPED flag.
+ void InitDedicatedAllocation(
+ uint32_t memoryTypeIndex,
+ VkDeviceMemory hMemory,
+ VmaSuballocationType suballocationType,
+ void* pMappedData,
+ VkDeviceSize size)
+ {
+ VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
+ VMA_ASSERT(hMemory != VK_NULL_HANDLE);
+ m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;
+ m_Alignment = 0;
+ m_Size = size;
+ m_SuballocationType = (uint8_t)suballocationType;
+ m_MapCount = (pMappedData != VMA_NULL) ? MAP_COUNT_FLAG_PERSISTENT_MAP : 0;
+ m_DedicatedAllocation.m_MemoryTypeIndex = memoryTypeIndex;
+ m_DedicatedAllocation.m_hMemory = hMemory;
+ m_DedicatedAllocation.m_pMappedData = pMappedData;
+ }
+
+ ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }
+ VkDeviceSize GetAlignment() const { return m_Alignment; }
+ VkDeviceSize GetSize() const { return m_Size; }
+ bool IsUserDataString() const { return (m_Flags & FLAG_USER_DATA_STRING) != 0; }
+ void* GetUserData() const { return m_pUserData; }
+ void SetUserData(VmaAllocator hAllocator, void* pUserData);
+ VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }
+
+ VmaDeviceMemoryBlock* GetBlock() const
+ {
+ VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
+ return m_BlockAllocation.m_Block;
+ }
+ VkDeviceSize GetOffset() const;
+ VkDeviceMemory GetMemory() const;
+ uint32_t GetMemoryTypeIndex() const;
+ bool IsPersistentMap() const { return (m_MapCount & MAP_COUNT_FLAG_PERSISTENT_MAP) != 0; }
+ void* GetMappedData() const;
+ bool CanBecomeLost() const;
+ VmaPool GetPool() const;
+
+ uint32_t GetLastUseFrameIndex() const
+ {
+ return m_LastUseFrameIndex.load();
+ }
+ bool CompareExchangeLastUseFrameIndex(uint32_t& expected, uint32_t desired)
+ {
+ return m_LastUseFrameIndex.compare_exchange_weak(expected, desired);
+ }
+ /*
+ - If hAllocation.LastUseFrameIndex + frameInUseCount < allocator.CurrentFrameIndex,
+ makes it lost by setting LastUseFrameIndex = VMA_FRAME_INDEX_LOST and returns true.
+ - Else, returns false.
+
+ If hAllocation is already lost, assert - you should not call it then.
+ If hAllocation was not created with CAN_BECOME_LOST_BIT, assert.
+ */
+ bool MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
+
+ void DedicatedAllocCalcStatsInfo(VmaStatInfo& outInfo)
+ {
+ VMA_ASSERT(m_Type == ALLOCATION_TYPE_DEDICATED);
+ outInfo.blockCount = 1;
+ outInfo.allocationCount = 1;
+ outInfo.unusedRangeCount = 0;
+ outInfo.usedBytes = m_Size;
+ outInfo.unusedBytes = 0;
+ outInfo.allocationSizeMin = outInfo.allocationSizeMax = m_Size;
+ outInfo.unusedRangeSizeMin = UINT64_MAX;
+ outInfo.unusedRangeSizeMax = 0;
+ }
+
+ void BlockAllocMap();
+ void BlockAllocUnmap();
+ VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);
+ void DedicatedAllocUnmap(VmaAllocator hAllocator);
+
+#if VMA_STATS_STRING_ENABLED
+ uint32_t GetCreationFrameIndex() const { return m_CreationFrameIndex; }
+ uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }
+
+ void InitBufferImageUsage(uint32_t bufferImageUsage)
+ {
+ VMA_ASSERT(m_BufferImageUsage == 0);
+ m_BufferImageUsage = bufferImageUsage;
+ }
+
+ void PrintParameters(class VmaJsonWriter& json) const;
+#endif
+
+private:
+ VkDeviceSize m_Alignment;
+ VkDeviceSize m_Size;
+ void* m_pUserData;
+ VMA_ATOMIC_UINT32 m_LastUseFrameIndex;
+ uint8_t m_Type; // ALLOCATION_TYPE
+ uint8_t m_SuballocationType; // VmaSuballocationType
+ // Bit 0x80 is set when allocation was created with VMA_ALLOCATION_CREATE_MAPPED_BIT.
+ // Bits with mask 0x7F are reference counter for vmaMapMemory()/vmaUnmapMemory().
+ uint8_t m_MapCount;
+ uint8_t m_Flags; // enum FLAGS
+
+ // Allocation out of VmaDeviceMemoryBlock.
+ struct BlockAllocation
+ {
+ VmaPool m_hPool; // Null if belongs to general memory.
+ VmaDeviceMemoryBlock* m_Block;
+ VkDeviceSize m_Offset;
+ bool m_CanBecomeLost;
+ };
+
+ // Allocation for an object that has its own private VkDeviceMemory.
+ struct DedicatedAllocation
+ {
+ uint32_t m_MemoryTypeIndex;
+ VkDeviceMemory m_hMemory;
+ void* m_pMappedData; // Not null means memory is mapped.
+ };
+
+ union
+ {
+ // Allocation out of VmaDeviceMemoryBlock.
+ BlockAllocation m_BlockAllocation;
+ // Allocation for an object that has its own private VkDeviceMemory.
+ DedicatedAllocation m_DedicatedAllocation;
+ };
+
+#if VMA_STATS_STRING_ENABLED
+ uint32_t m_CreationFrameIndex;
+ uint32_t m_BufferImageUsage; // 0 if unknown.
+#endif
+
+ void FreeUserDataString(VmaAllocator hAllocator);
+};
+
+/*
+Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as
+allocated memory block or free.
+*/
+struct VmaSuballocation
+{
+ VkDeviceSize offset;
+ VkDeviceSize size;
+ VmaAllocation hAllocation;
+ VmaSuballocationType type;
+};
+
+// Comparator for offsets.
+struct VmaSuballocationOffsetLess
+{
+ bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
+ {
+ return lhs.offset < rhs.offset;
+ }
+};
+struct VmaSuballocationOffsetGreater
+{
+ bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const
+ {
+ return lhs.offset > rhs.offset;
+ }
+};
+
+typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
+
+// Cost of one additional allocation lost, as equivalent in bytes.
+static const VkDeviceSize VMA_LOST_ALLOCATION_COST = 1048576;
+
+/*
+Parameters of planned allocation inside a VmaDeviceMemoryBlock.
+
+If canMakeOtherLost was false:
+- item points to a FREE suballocation.
+- itemsToMakeLostCount is 0.
+
+If canMakeOtherLost was true:
+- item points to first of sequence of suballocations, which are either FREE,
+ or point to VmaAllocations that can become lost.
+- itemsToMakeLostCount is the number of VmaAllocations that need to be made lost for
+ the requested allocation to succeed.
+*/
+struct VmaAllocationRequest
+{
+ VkDeviceSize offset;
+ VkDeviceSize sumFreeSize; // Sum size of free items that overlap with proposed allocation.
+ VkDeviceSize sumItemSize; // Sum size of items to make lost that overlap with proposed allocation.
+ VmaSuballocationList::iterator item;
+ size_t itemsToMakeLostCount;
+ void* customData;
+
+ VkDeviceSize CalcCost() const
+ {
+ return sumItemSize + itemsToMakeLostCount * VMA_LOST_ALLOCATION_COST;
+ }
+};
+
+/*
+Data structure used for bookkeeping of allocations and unused ranges of memory
+in a single VkDeviceMemory block.
+*/
+class VmaBlockMetadata
+{
+public:
+ VmaBlockMetadata(VmaAllocator hAllocator);
+ virtual ~VmaBlockMetadata() { }
+ virtual void Init(VkDeviceSize size) { m_Size = size; }
+
+ // Validates all data structures inside this object. If not valid, returns false.
+ virtual bool Validate() const = 0;
+ VkDeviceSize GetSize() const { return m_Size; }
+ virtual size_t GetAllocationCount() const = 0;
+ virtual VkDeviceSize GetSumFreeSize() const = 0;
+ virtual VkDeviceSize GetUnusedRangeSizeMax() const = 0;
+ // Returns true if this block is empty - contains only single free suballocation.
+ virtual bool IsEmpty() const = 0;
+
+ virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0;
+ // Shouldn't modify blockCount.
+ virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0;
+
+#if VMA_STATS_STRING_ENABLED
+ virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;
+#endif
+
+ // Tries to find a place for suballocation with given parameters inside this block.
+ // If succeeded, fills pAllocationRequest and returns true.
+ // If failed, returns false.
+ virtual bool CreateAllocationRequest(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VkDeviceSize bufferImageGranularity,
+ VkDeviceSize allocSize,
+ VkDeviceSize allocAlignment,
+ bool upperAddress,
+ VmaSuballocationType allocType,
+ bool canMakeOtherLost,
+ // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.
+ uint32_t strategy,
+ VmaAllocationRequest* pAllocationRequest) = 0;
+
+ virtual bool MakeRequestedAllocationsLost(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VmaAllocationRequest* pAllocationRequest) = 0;
+
+ virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount) = 0;
+
+ virtual VkResult CheckCorruption(const void* pBlockData) = 0;
+
+ // Makes actual allocation based on request. Request must already be checked and valid.
+ virtual void Alloc(
+ const VmaAllocationRequest& request,
+ VmaSuballocationType type,
+ VkDeviceSize allocSize,
+ bool upperAddress,
+ VmaAllocation hAllocation) = 0;
+
+ // Frees suballocation assigned to given memory region.
+ virtual void Free(const VmaAllocation allocation) = 0;
+ virtual void FreeAtOffset(VkDeviceSize offset) = 0;
+
+ // Tries to resize (grow or shrink) space for given allocation, in place.
+ virtual bool ResizeAllocation(const VmaAllocation /*alloc*/, VkDeviceSize /*newSize*/) { return false; }
+
+protected:
+ const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }
+
+#if VMA_STATS_STRING_ENABLED
+ void PrintDetailedMap_Begin(class VmaJsonWriter& json,
+ VkDeviceSize unusedBytes,
+ size_t allocationCount,
+ size_t unusedRangeCount) const;
+ void PrintDetailedMap_Allocation(class VmaJsonWriter& json,
+ VkDeviceSize offset,
+ VmaAllocation hAllocation) const;
+ void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
+ VkDeviceSize offset,
+ VkDeviceSize size) const;
+ void PrintDetailedMap_End(class VmaJsonWriter& json) const;
+#endif
+
+private:
+ VkDeviceSize m_Size;
+ const VkAllocationCallbacks* m_pAllocationCallbacks;
+};
+
+#define VMA_VALIDATE(cond) do { if(!(cond)) { \
+ VMA_ASSERT(0 && "Validation failed: " #cond); \
+ return false; \
+ } } while(false)
+
+class VmaBlockMetadata_Generic : public VmaBlockMetadata
+{
+ VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic)
+public:
+ VmaBlockMetadata_Generic(VmaAllocator hAllocator);
+ virtual ~VmaBlockMetadata_Generic();
+ virtual void Init(VkDeviceSize size);
+
+ virtual bool Validate() const;
+ virtual size_t GetAllocationCount() const { return m_Suballocations.size() - m_FreeCount; }
+ virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
+ virtual VkDeviceSize GetUnusedRangeSizeMax() const;
+ virtual bool IsEmpty() const;
+
+ virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
+ virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
+
+#if VMA_STATS_STRING_ENABLED
+ virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
+#endif
+
+ virtual bool CreateAllocationRequest(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VkDeviceSize bufferImageGranularity,
+ VkDeviceSize allocSize,
+ VkDeviceSize allocAlignment,
+ bool upperAddress,
+ VmaSuballocationType allocType,
+ bool canMakeOtherLost,
+ uint32_t strategy,
+ VmaAllocationRequest* pAllocationRequest);
+
+ virtual bool MakeRequestedAllocationsLost(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VmaAllocationRequest* pAllocationRequest);
+
+ virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
+
+ virtual VkResult CheckCorruption(const void* pBlockData);
+
+ virtual void Alloc(
+ const VmaAllocationRequest& request,
+ VmaSuballocationType type,
+ VkDeviceSize allocSize,
+ bool upperAddress,
+ VmaAllocation hAllocation);
+
+ virtual void Free(const VmaAllocation allocation);
+ virtual void FreeAtOffset(VkDeviceSize offset);
+
+ virtual bool ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize);
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // For defragmentation
+
+ bool IsBufferImageGranularityConflictPossible(
+ VkDeviceSize bufferImageGranularity,
+ VmaSuballocationType& inOutPrevSuballocType) const;
+
+private:
+ friend class VmaDefragmentationAlgorithm_Generic;
+ friend class VmaDefragmentationAlgorithm_Fast;
+
+ uint32_t m_FreeCount;
+ VkDeviceSize m_SumFreeSize;
+ VmaSuballocationList m_Suballocations;
+ // Suballocations that are free and have size greater than certain threshold.
+ // Sorted by size, ascending.
+ VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
+
+ bool ValidateFreeSuballocationList() const;
+
+ // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
+ // If yes, fills pOffset and returns true. If no, returns false.
+ bool CheckAllocation(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VkDeviceSize bufferImageGranularity,
+ VkDeviceSize allocSize,
+ VkDeviceSize allocAlignment,
+ VmaSuballocationType allocType,
+ VmaSuballocationList::const_iterator suballocItem,
+ bool canMakeOtherLost,
+ VkDeviceSize* pOffset,
+ size_t* itemsToMakeLostCount,
+ VkDeviceSize* pSumFreeSize,
+ VkDeviceSize* pSumItemSize) const;
+ // Given free suballocation, it merges it with following one, which must also be free.
+ void MergeFreeWithNext(VmaSuballocationList::iterator item);
+ // Releases given suballocation, making it free.
+ // Merges it with adjacent free suballocations if applicable.
+ // Returns iterator to new free suballocation at this place.
+ VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);
+ // Given free suballocation, it inserts it into sorted list of
+ // m_FreeSuballocationsBySize if it's suitable.
+ void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
+ // Given free suballocation, it removes it from sorted list of
+ // m_FreeSuballocationsBySize if it's suitable.
+ void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
+};
+
+/*
+Allocations and their references in internal data structure look like this:
+
+if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):
+
+ 0 +-------+
+ | |
+ | |
+ | |
+ +-------+
+ | Alloc | 1st[m_1stNullItemsBeginCount]
+ +-------+
+ | Alloc | 1st[m_1stNullItemsBeginCount + 1]
+ +-------+
+ | ... |
+ +-------+
+ | Alloc | 1st[1st.size() - 1]
+ +-------+
+ | |
+ | |
+ | |
+GetSize() +-------+
+
+if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):
+
+ 0 +-------+
+ | Alloc | 2nd[0]
+ +-------+
+ | Alloc | 2nd[1]
+ +-------+
+ | ... |
+ +-------+
+ | Alloc | 2nd[2nd.size() - 1]
+ +-------+
+ | |
+ | |
+ | |
+ +-------+
+ | Alloc | 1st[m_1stNullItemsBeginCount]
+ +-------+
+ | Alloc | 1st[m_1stNullItemsBeginCount + 1]
+ +-------+
+ | ... |
+ +-------+
+ | Alloc | 1st[1st.size() - 1]
+ +-------+
+ | |
+GetSize() +-------+
+
+if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):
+
+ 0 +-------+
+ | |
+ | |
+ | |
+ +-------+
+ | Alloc | 1st[m_1stNullItemsBeginCount]
+ +-------+
+ | Alloc | 1st[m_1stNullItemsBeginCount + 1]
+ +-------+
+ | ... |
+ +-------+
+ | Alloc | 1st[1st.size() - 1]
+ +-------+
+ | |
+ | |
+ | |
+ +-------+
+ | Alloc | 2nd[2nd.size() - 1]
+ +-------+
+ | ... |
+ +-------+
+ | Alloc | 2nd[1]
+ +-------+
+ | Alloc | 2nd[0]
+GetSize() +-------+
+
+*/
+class VmaBlockMetadata_Linear : public VmaBlockMetadata
+{
+ VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear)
+public:
+ VmaBlockMetadata_Linear(VmaAllocator hAllocator);
+ virtual ~VmaBlockMetadata_Linear();
+ virtual void Init(VkDeviceSize size);
+
+ virtual bool Validate() const;
+ virtual size_t GetAllocationCount() const;
+ virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize; }
+ virtual VkDeviceSize GetUnusedRangeSizeMax() const;
+ virtual bool IsEmpty() const { return GetAllocationCount() == 0; }
+
+ virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
+ virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
+
+#if VMA_STATS_STRING_ENABLED
+ virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
+#endif
+
+ virtual bool CreateAllocationRequest(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VkDeviceSize bufferImageGranularity,
+ VkDeviceSize allocSize,
+ VkDeviceSize allocAlignment,
+ bool upperAddress,
+ VmaSuballocationType allocType,
+ bool canMakeOtherLost,
+ uint32_t strategy,
+ VmaAllocationRequest* pAllocationRequest);
+
+ virtual bool MakeRequestedAllocationsLost(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VmaAllocationRequest* pAllocationRequest);
+
+ virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
+
+ virtual VkResult CheckCorruption(const void* pBlockData);
+
+ virtual void Alloc(
+ const VmaAllocationRequest& request,
+ VmaSuballocationType type,
+ VkDeviceSize allocSize,
+ bool upperAddress,
+ VmaAllocation hAllocation);
+
+ virtual void Free(const VmaAllocation allocation);
+ virtual void FreeAtOffset(VkDeviceSize offset);
+
+private:
+ /*
+ There are two suballocation vectors, used in ping-pong way.
+ The one with index m_1stVectorIndex is called 1st.
+ The one with index (m_1stVectorIndex ^ 1) is called 2nd.
+ 2nd can be non-empty only when 1st is not empty.
+ When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.
+ */
+ typedef VmaVector< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > SuballocationVectorType;
+
+ enum SECOND_VECTOR_MODE
+ {
+ SECOND_VECTOR_EMPTY,
+ /*
+ Suballocations in 2nd vector are created later than the ones in 1st, but they
+ all have smaller offset.
+ */
+ SECOND_VECTOR_RING_BUFFER,
+ /*
+ Suballocations in 2nd vector are upper side of double stack.
+ They all have offsets higher than those in 1st vector.
+ Top of this stack means smaller offsets, but higher indices in this vector.
+ */
+ SECOND_VECTOR_DOUBLE_STACK,
+ };
+
+ VkDeviceSize m_SumFreeSize;
+ SuballocationVectorType m_Suballocations0, m_Suballocations1;
+ uint32_t m_1stVectorIndex;
+ SECOND_VECTOR_MODE m_2ndVectorMode;
+
+ SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
+ SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
+ const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }
+ const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }
+
+ // Number of items in 1st vector with hAllocation = null at the beginning.
+ size_t m_1stNullItemsBeginCount;
+ // Number of other items in 1st vector with hAllocation = null somewhere in the middle.
+ size_t m_1stNullItemsMiddleCount;
+ // Number of items in 2nd vector with hAllocation = null.
+ size_t m_2ndNullItemsCount;
+
+ bool ShouldCompact1st() const;
+ void CleanupAfterFree();
+};
+
+/*
+- GetSize() is the original size of allocated memory block.
+- m_UsableSize is this size aligned down to a power of two.
+ All allocations and calculations happen relative to m_UsableSize.
+- GetUnusableSize() is the difference between them.
+ It is repoted as separate, unused range, not available for allocations.
+
+Node at level 0 has size = m_UsableSize.
+Each next level contains nodes with size 2 times smaller than current level.
+m_LevelCount is the maximum number of levels to use in the current object.
+*/
+class VmaBlockMetadata_Buddy : public VmaBlockMetadata
+{
+ VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy)
+public:
+ VmaBlockMetadata_Buddy(VmaAllocator hAllocator);
+ virtual ~VmaBlockMetadata_Buddy();
+ virtual void Init(VkDeviceSize size);
+
+ virtual bool Validate() const;
+ virtual size_t GetAllocationCount() const { return m_AllocationCount; }
+ virtual VkDeviceSize GetSumFreeSize() const { return m_SumFreeSize + GetUnusableSize(); }
+ virtual VkDeviceSize GetUnusedRangeSizeMax() const;
+ virtual bool IsEmpty() const { return m_Root->type == Node::TYPE_FREE; }
+
+ virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const;
+ virtual void AddPoolStats(VmaPoolStats& inoutStats) const;
+
+#if VMA_STATS_STRING_ENABLED
+ virtual void PrintDetailedMap(class VmaJsonWriter& json) const;
+#endif
+
+ virtual bool CreateAllocationRequest(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VkDeviceSize bufferImageGranularity,
+ VkDeviceSize allocSize,
+ VkDeviceSize allocAlignment,
+ bool upperAddress,
+ VmaSuballocationType allocType,
+ bool canMakeOtherLost,
+ uint32_t strategy,
+ VmaAllocationRequest* pAllocationRequest);
+
+ virtual bool MakeRequestedAllocationsLost(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VmaAllocationRequest* pAllocationRequest);
+
+ virtual uint32_t MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount);
+
+ virtual VkResult CheckCorruption(const void* /*pBlockData*/) { return VK_ERROR_FEATURE_NOT_PRESENT; }
+
+ virtual void Alloc(
+ const VmaAllocationRequest& request,
+ VmaSuballocationType type,
+ VkDeviceSize allocSize,
+ bool upperAddress,
+ VmaAllocation hAllocation);
+
+ virtual void Free(const VmaAllocation allocation) { FreeAtOffset(allocation, allocation->GetOffset()); }
+ virtual void FreeAtOffset(VkDeviceSize offset) { FreeAtOffset(VMA_NULL, offset); }
+
+private:
+ static const VkDeviceSize MIN_NODE_SIZE = 32;
+ static const size_t MAX_LEVELS = 30;
+
+ struct ValidationContext
+ {
+ size_t calculatedAllocationCount;
+ size_t calculatedFreeCount;
+ VkDeviceSize calculatedSumFreeSize;
+
+ ValidationContext() :
+ calculatedAllocationCount(0),
+ calculatedFreeCount(0),
+ calculatedSumFreeSize(0) { }
+ };
+
+ struct Node
+ {
+ VkDeviceSize offset;
+ enum TYPE
+ {
+ TYPE_FREE,
+ TYPE_ALLOCATION,
+ TYPE_SPLIT,
+ TYPE_COUNT
+ } type;
+ Node* parent;
+ Node* buddy;
+
+ union
+ {
+ struct
+ {
+ Node* prev;
+ Node* next;
+ } free;
+ struct
+ {
+ VmaAllocation alloc;
+ } allocation;
+ struct
+ {
+ Node* leftChild;
+ } split;
+ };
+ };
+
+ // Size of the memory block aligned down to a power of two.
+ VkDeviceSize m_UsableSize;
+ uint32_t m_LevelCount;
+
+ Node* m_Root;
+ struct {
+ Node* front;
+ Node* back;
+ } m_FreeList[MAX_LEVELS];
+ // Number of nodes in the tree with type == TYPE_ALLOCATION.
+ size_t m_AllocationCount;
+ // Number of nodes in the tree with type == TYPE_FREE.
+ size_t m_FreeCount;
+ // This includes space wasted due to internal fragmentation. Doesn't include unusable size.
+ VkDeviceSize m_SumFreeSize;
+
+ VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }
+ void DeleteNode(Node* node);
+ bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;
+ uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;
+ inline VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }
+ // Alloc passed just for validation. Can be null.
+ void FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset);
+ void CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const;
+ // Adds node to the front of FreeList at given level.
+ // node->type must be FREE.
+ // node->free.prev, next can be undefined.
+ void AddToFreeListFront(uint32_t level, Node* node);
+ // Removes node from FreeList at given level.
+ // node->type must be FREE.
+ // node->free.prev, next stay untouched.
+ void RemoveFromFreeList(uint32_t level, Node* node);
+
+#if VMA_STATS_STRING_ENABLED
+ void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;
+#endif
+};
+
+/*
+Represents a single block of device memory (`VkDeviceMemory`) with all the
+data about its regions (aka suballocations, #VmaAllocation), assigned and free.
+
+Thread-safety: This class must be externally synchronized.
+*/
+class VmaDeviceMemoryBlock
+{
+ VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock)
+public:
+ VmaBlockMetadata* m_pMetadata;
+
+ VmaDeviceMemoryBlock(VmaAllocator hAllocator);
+
+ ~VmaDeviceMemoryBlock()
+ {
+ VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");
+ VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
+ }
+
+ // Always call after construction.
+ void Init(
+ VmaAllocator hAllocator,
+ uint32_t newMemoryTypeIndex,
+ VkDeviceMemory newMemory,
+ VkDeviceSize newSize,
+ uint32_t id,
+ uint32_t algorithm);
+ // Always call before destruction.
+ void Destroy(VmaAllocator allocator);
+
+ VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }
+ uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
+ uint32_t GetId() const { return m_Id; }
+ void* GetMappedData() const { return m_pMappedData; }
+
+ // Validates all data structures inside this object. If not valid, returns false.
+ bool Validate() const;
+
+ VkResult CheckCorruption(VmaAllocator hAllocator);
+
+ // ppData can be null.
+ VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);
+ void Unmap(VmaAllocator hAllocator, uint32_t count);
+
+ VkResult WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
+ VkResult ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);
+
+ VkResult BindBufferMemory(
+ const VmaAllocator hAllocator,
+ const VmaAllocation hAllocation,
+ VkBuffer hBuffer);
+ VkResult BindImageMemory(
+ const VmaAllocator hAllocator,
+ const VmaAllocation hAllocation,
+ VkImage hImage);
+
+private:
+ uint32_t m_MemoryTypeIndex;
+ uint32_t m_Id;
+ VkDeviceMemory m_hMemory;
+
+ /*
+ Protects access to m_hMemory so it's not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.
+ Also protects m_MapCount, m_pMappedData.
+ Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.
+ */
+ VMA_MUTEX m_Mutex;
+ uint32_t m_MapCount;
+ void* m_pMappedData;
+};
+
+struct VmaPointerLess
+{
+ bool operator()(const void* lhs, const void* rhs) const
+ {
+ return lhs < rhs;
+ }
+};
+
+struct VmaDefragmentationMove
+{
+ size_t srcBlockIndex;
+ size_t dstBlockIndex;
+ VkDeviceSize srcOffset;
+ VkDeviceSize dstOffset;
+ VkDeviceSize size;
+};
+
+class VmaDefragmentationAlgorithm;
+
+/*
+Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
+Vulkan memory type.
+
+Synchronized internally with a mutex.
+*/
+struct VmaBlockVector
+{
+ VMA_CLASS_NO_COPY(VmaBlockVector)
+public:
+ VmaBlockVector(
+ VmaAllocator hAllocator,
+ uint32_t memoryTypeIndex,
+ VkDeviceSize preferredBlockSize,
+ size_t minBlockCount,
+ size_t maxBlockCount,
+ VkDeviceSize bufferImageGranularity,
+ uint32_t frameInUseCount,
+ bool isCustomPool,
+ bool explicitBlockSize,
+ uint32_t algorithm);
+ ~VmaBlockVector();
+
+ VkResult CreateMinBlocks();
+
+ uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }
+ VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }
+ VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }
+ uint32_t GetFrameInUseCount() const { return m_FrameInUseCount; }
+ uint32_t GetAlgorithm() const { return m_Algorithm; }
+
+ void GetPoolStats(VmaPoolStats* pStats);
+
+ bool IsEmpty() const { return m_Blocks.empty(); }
+ bool IsCorruptionDetectionEnabled() const;
+
+ VkResult Allocate(
+ VmaPool hCurrentPool,
+ uint32_t currentFrameIndex,
+ VkDeviceSize size,
+ VkDeviceSize alignment,
+ const VmaAllocationCreateInfo& createInfo,
+ VmaSuballocationType suballocType,
+ size_t allocationCount,
+ VmaAllocation* pAllocations);
+
+ void Free(
+ VmaAllocation hAllocation);
+
+ // Adds statistics of this BlockVector to pStats.
+ void AddStats(VmaStats* pStats);
+
+#if VMA_STATS_STRING_ENABLED
+ void PrintDetailedMap(class VmaJsonWriter& json);
+#endif
+
+ void MakePoolAllocationsLost(
+ uint32_t currentFrameIndex,
+ size_t* pLostAllocationCount);
+ VkResult CheckCorruption();
+
+ // Saves results in pCtx->res.
+ void Defragment(
+ class VmaBlockVectorDefragmentationContext* pCtx,
+ VmaDefragmentationStats* pStats,
+ VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
+ VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
+ VkCommandBuffer commandBuffer);
+ void DefragmentationEnd(
+ class VmaBlockVectorDefragmentationContext* pCtx,
+ VmaDefragmentationStats* pStats);
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // To be used only while the m_Mutex is locked. Used during defragmentation.
+
+ size_t GetBlockCount() const { return m_Blocks.size(); }
+ VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }
+ size_t CalcAllocationCount() const;
+ bool IsBufferImageGranularityConflictPossible() const;
+
+private:
+ friend class VmaDefragmentationAlgorithm_Generic;
+
+ const VmaAllocator m_hAllocator;
+ const uint32_t m_MemoryTypeIndex;
+ const VkDeviceSize m_PreferredBlockSize;
+ const size_t m_MinBlockCount;
+ const size_t m_MaxBlockCount;
+ const VkDeviceSize m_BufferImageGranularity;
+ const uint32_t m_FrameInUseCount;
+ const bool m_IsCustomPool;
+ const bool m_ExplicitBlockSize;
+ const uint32_t m_Algorithm;
+ /* There can be at most one allocation that is completely empty - a
+ hysteresis to avoid pessimistic case of alternating creation and destruction
+ of a VkDeviceMemory. */
+ bool m_HasEmptyBlock;
+ VMA_RW_MUTEX m_Mutex;
+ // Incrementally sorted by sumFreeSize, ascending.
+ VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
+ uint32_t m_NextBlockId;
+
+ VkDeviceSize CalcMaxBlockSize() const;
+
+ // Finds and removes given block from vector.
+ void Remove(VmaDeviceMemoryBlock* pBlock);
+
+ // Performs single step in sorting m_Blocks. They may not be fully sorted
+ // after this call.
+ void IncrementallySortBlocks();
+
+ VkResult AllocatePage(
+ VmaPool hCurrentPool,
+ uint32_t currentFrameIndex,
+ VkDeviceSize size,
+ VkDeviceSize alignment,
+ const VmaAllocationCreateInfo& createInfo,
+ VmaSuballocationType suballocType,
+ VmaAllocation* pAllocation);
+
+ // To be used only without CAN_MAKE_OTHER_LOST flag.
+ VkResult AllocateFromBlock(
+ VmaDeviceMemoryBlock* pBlock,
+ VmaPool hCurrentPool,
+ uint32_t currentFrameIndex,
+ VkDeviceSize size,
+ VkDeviceSize alignment,
+ VmaAllocationCreateFlags allocFlags,
+ void* pUserData,
+ VmaSuballocationType suballocType,
+ uint32_t strategy,
+ VmaAllocation* pAllocation);
+
+ VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
+
+ // Saves result to pCtx->res.
+ void ApplyDefragmentationMovesCpu(
+ class VmaBlockVectorDefragmentationContext* pDefragCtx,
+ const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves);
+ // Saves result to pCtx->res.
+ void ApplyDefragmentationMovesGpu(
+ class VmaBlockVectorDefragmentationContext* pDefragCtx,
+ const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
+ VkCommandBuffer commandBuffer);
+
+ /*
+ Used during defragmentation. pDefragmentationStats is optional. It's in/out
+ - updated with new data.
+ */
+ void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
+};
+
+struct VmaPool_T
+{
+ VMA_CLASS_NO_COPY(VmaPool_T)
+public:
+ VmaBlockVector m_BlockVector;
+
+ VmaPool_T(
+ VmaAllocator hAllocator,
+ const VmaPoolCreateInfo& createInfo,
+ VkDeviceSize preferredBlockSize);
+ ~VmaPool_T();
+
+ uint32_t GetId() const { return m_Id; }
+ void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }
+
+#if VMA_STATS_STRING_ENABLED
+ //void PrintDetailedMap(class VmaStringBuilder& sb);
+#endif
+
+private:
+ uint32_t m_Id;
+};
+
+/*
+Performs defragmentation:
+
+- Updates `pBlockVector->m_pMetadata`.
+- Updates allocations by calling ChangeBlockAllocation() or ChangeOffset().
+- Does not move actual data, only returns requested moves as `moves`.
+*/
+class VmaDefragmentationAlgorithm
+{
+ VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
+public:
+ VmaDefragmentationAlgorithm(
+ VmaAllocator hAllocator,
+ VmaBlockVector* pBlockVector,
+ uint32_t currentFrameIndex) :
+ m_hAllocator(hAllocator),
+ m_pBlockVector(pBlockVector),
+ m_CurrentFrameIndex(currentFrameIndex)
+ {
+ }
+ virtual ~VmaDefragmentationAlgorithm()
+ {
+ }
+
+ virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) = 0;
+ virtual void AddAll() = 0;
+
+ virtual VkResult Defragment(
+ VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
+ VkDeviceSize maxBytesToMove,
+ uint32_t maxAllocationsToMove) = 0;
+
+ virtual VkDeviceSize GetBytesMoved() const = 0;
+ virtual uint32_t GetAllocationsMoved() const = 0;
+
+protected:
+ VmaAllocator const m_hAllocator;
+ VmaBlockVector* const m_pBlockVector;
+ const uint32_t m_CurrentFrameIndex;
+
+ struct AllocationInfo
+ {
+ VmaAllocation m_hAllocation;
+ VkBool32* m_pChanged;
+
+ AllocationInfo() :
+ m_hAllocation(VK_NULL_HANDLE),
+ m_pChanged(VMA_NULL)
+ {
+ }
+ AllocationInfo(VmaAllocation hAlloc, VkBool32* pChanged) :
+ m_hAllocation(hAlloc),
+ m_pChanged(pChanged)
+ {
+ }
+ };
+};
+
+class VmaDefragmentationAlgorithm_Generic : public VmaDefragmentationAlgorithm
+{
+ VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Generic)
+public:
+ VmaDefragmentationAlgorithm_Generic(
+ VmaAllocator hAllocator,
+ VmaBlockVector* pBlockVector,
+ uint32_t currentFrameIndex,
+ bool overlappingMoveSupported);
+ virtual ~VmaDefragmentationAlgorithm_Generic();
+
+ virtual void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
+ virtual void AddAll() { m_AllAllocations = true; }
+
+ virtual VkResult Defragment(
+ VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
+ VkDeviceSize maxBytesToMove,
+ uint32_t maxAllocationsToMove);
+
+ virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
+ virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
+
+private:
+ uint32_t m_AllocationCount;
+ bool m_AllAllocations;
+
+ VkDeviceSize m_BytesMoved;
+ uint32_t m_AllocationsMoved;
+
+ struct AllocationInfoSizeGreater
+ {
+ bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
+ {
+ return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
+ }
+ };
+
+ struct AllocationInfoOffsetGreater
+ {
+ bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
+ {
+ return lhs.m_hAllocation->GetOffset() > rhs.m_hAllocation->GetOffset();
+ }
+ };
+
+ struct BlockInfo
+ {
+ size_t m_OriginalBlockIndex;
+ VmaDeviceMemoryBlock* m_pBlock;
+ bool m_HasNonMovableAllocations;
+ VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
+
+ BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
+ m_OriginalBlockIndex(SIZE_MAX),
+ m_pBlock(VMA_NULL),
+ m_HasNonMovableAllocations(true),
+ m_Allocations(pAllocationCallbacks)
+ {
+ }
+
+ void CalcHasNonMovableAllocations()
+ {
+ const size_t blockAllocCount = m_pBlock->m_pMetadata->GetAllocationCount();
+ const size_t defragmentAllocCount = m_Allocations.size();
+ m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
+ }
+
+ void SortAllocationsBySizeDescending()
+ {
+ VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
+ }
+
+ void SortAllocationsByOffsetDescending()
+ {
+ VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoOffsetGreater());
+ }
+ };
+
+ struct BlockPointerLess
+ {
+ bool operator()(const BlockInfo* pLhsBlockInfo, const VmaDeviceMemoryBlock* pRhsBlock) const
+ {
+ return pLhsBlockInfo->m_pBlock < pRhsBlock;
+ }
+ bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
+ {
+ return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
+ }
+ };
+
+ // 1. Blocks with some non-movable allocations go first.
+ // 2. Blocks with smaller sumFreeSize go first.
+ struct BlockInfoCompareMoveDestination
+ {
+ bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
+ {
+ if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
+ {
+ return true;
+ }
+ if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
+ {
+ return false;
+ }
+ if(pLhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize() < pRhsBlockInfo->m_pBlock->m_pMetadata->GetSumFreeSize())
+ {
+ return true;
+ }
+ return false;
+ }
+ };
+
+ typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
+ BlockInfoVector m_Blocks;
+
+ VkResult DefragmentRound(
+ VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
+ VkDeviceSize maxBytesToMove,
+ uint32_t maxAllocationsToMove);
+
+ size_t CalcBlocksWithNonMovableCount() const;
+
+ static bool MoveMakesSense(
+ size_t dstBlockIndex, VkDeviceSize dstOffset,
+ size_t srcBlockIndex, VkDeviceSize srcOffset);
+};
+
+class VmaDefragmentationAlgorithm_Fast : public VmaDefragmentationAlgorithm
+{
+ VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm_Fast)
+public:
+ VmaDefragmentationAlgorithm_Fast(
+ VmaAllocator hAllocator,
+ VmaBlockVector* pBlockVector,
+ uint32_t currentFrameIndex,
+ bool overlappingMoveSupported);
+ virtual ~VmaDefragmentationAlgorithm_Fast();
+
+ virtual void AddAllocation(VmaAllocation /*hAlloc*/, VkBool32* /*pChanged*/) { ++m_AllocationCount; }
+ virtual void AddAll() { m_AllAllocations = true; }
+
+ virtual VkResult Defragment(
+ VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
+ VkDeviceSize maxBytesToMove,
+ uint32_t maxAllocationsToMove);
+
+ virtual VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
+ virtual uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
+
+private:
+ struct BlockInfo
+ {
+ size_t origBlockIndex;
+ };
+
+ class FreeSpaceDatabase
+ {
+ public:
+ FreeSpaceDatabase()
+ {
+ FreeSpace s = {};
+ s.blockInfoIndex = SIZE_MAX;
+ for(size_t i = 0; i < MAX_COUNT; ++i)
+ {
+ m_FreeSpaces[i] = s;
+ }
+ }
+
+ void Register(size_t blockInfoIndex, VkDeviceSize offset, VkDeviceSize size)
+ {
+ if(size < VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
+ {
+ return;
+ }
+
+ // Find first invalid or the smallest structure.
+ size_t bestIndex = SIZE_MAX;
+ for(size_t i = 0; i < MAX_COUNT; ++i)
+ {
+ // Empty structure.
+ if(m_FreeSpaces[i].blockInfoIndex == SIZE_MAX)
+ {
+ bestIndex = i;
+ break;
+ }
+ if(m_FreeSpaces[i].size < size &&
+ (bestIndex == SIZE_MAX || m_FreeSpaces[bestIndex].size > m_FreeSpaces[i].size))
+ {
+ bestIndex = i;
+ }
+ }
+
+ if(bestIndex != SIZE_MAX)
+ {
+ m_FreeSpaces[bestIndex].blockInfoIndex = blockInfoIndex;
+ m_FreeSpaces[bestIndex].offset = offset;
+ m_FreeSpaces[bestIndex].size = size;
+ }
+ }
+
+ bool Fetch(VkDeviceSize alignment, VkDeviceSize size,
+ size_t& outBlockInfoIndex, VkDeviceSize& outDstOffset)
+ {
+ size_t bestIndex = SIZE_MAX;
+ VkDeviceSize bestFreeSpaceAfter = 0;
+ for(size_t i = 0; i < MAX_COUNT; ++i)
+ {
+ // Structure is valid.
+ if(m_FreeSpaces[i].blockInfoIndex != SIZE_MAX)
+ {
+ const VkDeviceSize dstOffset = VmaAlignUp(m_FreeSpaces[i].offset, alignment);
+ // Allocation fits into this structure.
+ if(dstOffset + size <= m_FreeSpaces[i].offset + m_FreeSpaces[i].size)
+ {
+ const VkDeviceSize freeSpaceAfter = (m_FreeSpaces[i].offset + m_FreeSpaces[i].size) -
+ (dstOffset + size);
+ if(bestIndex == SIZE_MAX || freeSpaceAfter > bestFreeSpaceAfter)
+ {
+ bestIndex = i;
+ bestFreeSpaceAfter = freeSpaceAfter;
+ }
+ }
+ }
+ }
+
+ if(bestIndex != SIZE_MAX)
+ {
+ outBlockInfoIndex = m_FreeSpaces[bestIndex].blockInfoIndex;
+ outDstOffset = VmaAlignUp(m_FreeSpaces[bestIndex].offset, alignment);
+
+ if(bestFreeSpaceAfter >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
+ {
+ // Leave this structure for remaining empty space.
+ const VkDeviceSize alignmentPlusSize = (outDstOffset - m_FreeSpaces[bestIndex].offset) + size;
+ m_FreeSpaces[bestIndex].offset += alignmentPlusSize;
+ m_FreeSpaces[bestIndex].size -= alignmentPlusSize;
+ }
+ else
+ {
+ // This structure becomes invalid.
+ m_FreeSpaces[bestIndex].blockInfoIndex = SIZE_MAX;
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ private:
+ static const size_t MAX_COUNT = 4;
+
+ struct FreeSpace
+ {
+ size_t blockInfoIndex; // SIZE_MAX means this structure is invalid.
+ VkDeviceSize offset;
+ VkDeviceSize size;
+ } m_FreeSpaces[MAX_COUNT];
+ };
+
+ const bool m_OverlappingMoveSupported;
+
+ uint32_t m_AllocationCount;
+ bool m_AllAllocations;
+
+ VkDeviceSize m_BytesMoved;
+ uint32_t m_AllocationsMoved;
+
+ VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> > m_BlockInfos;
+
+ void PreprocessMetadata();
+ void PostprocessMetadata();
+ void InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc);
+};
+
+struct VmaBlockDefragmentationContext
+{
+ enum BLOCK_FLAG
+ {
+ BLOCK_FLAG_USED = 0x00000001,
+ };
+ uint32_t flags;
+ VkBuffer hBuffer;
+
+ VmaBlockDefragmentationContext() :
+ flags(0),
+ hBuffer(VK_NULL_HANDLE)
+ {
+ }
+};
+
+class VmaBlockVectorDefragmentationContext
+{
+ VMA_CLASS_NO_COPY(VmaBlockVectorDefragmentationContext)
+public:
+ VkResult res;
+ bool mutexLocked;
+ VmaVector< VmaBlockDefragmentationContext, VmaStlAllocator<VmaBlockDefragmentationContext> > blockContexts;
+
+ VmaBlockVectorDefragmentationContext(
+ VmaAllocator hAllocator,
+ VmaPool hCustomPool, // Optional.
+ VmaBlockVector* pBlockVector,
+ uint32_t currFrameIndex,
+ uint32_t flags);
+ ~VmaBlockVectorDefragmentationContext();
+
+ VmaPool GetCustomPool() const { return m_hCustomPool; }
+ VmaBlockVector* GetBlockVector() const { return m_pBlockVector; }
+ VmaDefragmentationAlgorithm* GetAlgorithm() const { return m_pAlgorithm; }
+
+ void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
+ void AddAll() { m_AllAllocations = true; }
+
+ void Begin(bool overlappingMoveSupported);
+
+private:
+ const VmaAllocator m_hAllocator;
+ // Null if not from custom pool.
+ const VmaPool m_hCustomPool;
+ // Redundant, for convenience not to fetch from m_hCustomPool->m_BlockVector or m_hAllocator->m_pBlockVectors.
+ VmaBlockVector* const m_pBlockVector;
+ const uint32_t m_CurrFrameIndex;
+ /*const uint32_t m_AlgorithmFlags;*/
+ // Owner of this object.
+ VmaDefragmentationAlgorithm* m_pAlgorithm;
+
+ struct AllocInfo
+ {
+ VmaAllocation hAlloc;
+ VkBool32* pChanged;
+ };
+ // Used between constructor and Begin.
+ VmaVector< AllocInfo, VmaStlAllocator<AllocInfo> > m_Allocations;
+ bool m_AllAllocations;
+};
+
+struct VmaDefragmentationContext_T
+{
+private:
+ VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
+public:
+ VmaDefragmentationContext_T(
+ VmaAllocator hAllocator,
+ uint32_t currFrameIndex,
+ uint32_t flags,
+ VmaDefragmentationStats* pStats);
+ ~VmaDefragmentationContext_T();
+
+ void AddPools(uint32_t poolCount, VmaPool* pPools);
+ void AddAllocations(
+ uint32_t allocationCount,
+ VmaAllocation* pAllocations,
+ VkBool32* pAllocationsChanged);
+
+ /*
+ Returns:
+ - `VK_SUCCESS` if succeeded and object can be destroyed immediately.
+ - `VK_NOT_READY` if succeeded but the object must remain alive until vmaDefragmentationEnd().
+ - Negative value if error occured and object can be destroyed immediately.
+ */
+ VkResult Defragment(
+ VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
+ VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
+ VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats);
+
+private:
+ const VmaAllocator m_hAllocator;
+ const uint32_t m_CurrFrameIndex;
+ const uint32_t m_Flags;
+ VmaDefragmentationStats* const m_pStats;
+ // Owner of these objects.
+ VmaBlockVectorDefragmentationContext* m_DefaultPoolContexts[VK_MAX_MEMORY_TYPES];
+ // Owner of these objects.
+ VmaVector< VmaBlockVectorDefragmentationContext*, VmaStlAllocator<VmaBlockVectorDefragmentationContext*> > m_CustomPoolContexts;
+};
+
+#if VMA_RECORDING_ENABLED
+
+class VmaRecorder
+{
+public:
+ VmaRecorder();
+ VkResult Init(const VmaRecordSettings& settings, bool useMutex);
+ void WriteConfiguration(
+ const VkPhysicalDeviceProperties& devProps,
+ const VkPhysicalDeviceMemoryProperties& memProps,
+ bool dedicatedAllocationExtensionEnabled);
+ ~VmaRecorder();
+
+ void RecordCreateAllocator(uint32_t frameIndex);
+ void RecordDestroyAllocator(uint32_t frameIndex);
+ void RecordCreatePool(uint32_t frameIndex,
+ const VmaPoolCreateInfo& createInfo,
+ VmaPool pool);
+ void RecordDestroyPool(uint32_t frameIndex, VmaPool pool);
+ void RecordAllocateMemory(uint32_t frameIndex,
+ const VkMemoryRequirements& vkMemReq,
+ const VmaAllocationCreateInfo& createInfo,
+ VmaAllocation allocation);
+ void RecordAllocateMemoryPages(uint32_t frameIndex,
+ const VkMemoryRequirements& vkMemReq,
+ const VmaAllocationCreateInfo& createInfo,
+ uint64_t allocationCount,
+ const VmaAllocation* pAllocations);
+ void RecordAllocateMemoryForBuffer(uint32_t frameIndex,
+ const VkMemoryRequirements& vkMemReq,
+ bool requiresDedicatedAllocation,
+ bool prefersDedicatedAllocation,
+ const VmaAllocationCreateInfo& createInfo,
+ VmaAllocation allocation);
+ void RecordAllocateMemoryForImage(uint32_t frameIndex,
+ const VkMemoryRequirements& vkMemReq,
+ bool requiresDedicatedAllocation,
+ bool prefersDedicatedAllocation,
+ const VmaAllocationCreateInfo& createInfo,
+ VmaAllocation allocation);
+ void RecordFreeMemory(uint32_t frameIndex,
+ VmaAllocation allocation);
+ void RecordFreeMemoryPages(uint32_t frameIndex,
+ uint64_t allocationCount,
+ const VmaAllocation* pAllocations);
+ void RecordResizeAllocation(
+ uint32_t frameIndex,
+ VmaAllocation allocation,
+ VkDeviceSize newSize);
+ void RecordSetAllocationUserData(uint32_t frameIndex,
+ VmaAllocation allocation,
+ const void* pUserData);
+ void RecordCreateLostAllocation(uint32_t frameIndex,
+ VmaAllocation allocation);
+ void RecordMapMemory(uint32_t frameIndex,
+ VmaAllocation allocation);
+ void RecordUnmapMemory(uint32_t frameIndex,
+ VmaAllocation allocation);
+ void RecordFlushAllocation(uint32_t frameIndex,
+ VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
+ void RecordInvalidateAllocation(uint32_t frameIndex,
+ VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size);
+ void RecordCreateBuffer(uint32_t frameIndex,
+ const VkBufferCreateInfo& bufCreateInfo,
+ const VmaAllocationCreateInfo& allocCreateInfo,
+ VmaAllocation allocation);
+ void RecordCreateImage(uint32_t frameIndex,
+ const VkImageCreateInfo& imageCreateInfo,
+ const VmaAllocationCreateInfo& allocCreateInfo,
+ VmaAllocation allocation);
+ void RecordDestroyBuffer(uint32_t frameIndex,
+ VmaAllocation allocation);
+ void RecordDestroyImage(uint32_t frameIndex,
+ VmaAllocation allocation);
+ void RecordTouchAllocation(uint32_t frameIndex,
+ VmaAllocation allocation);
+ void RecordGetAllocationInfo(uint32_t frameIndex,
+ VmaAllocation allocation);
+ void RecordMakePoolAllocationsLost(uint32_t frameIndex,
+ VmaPool pool);
+ void RecordDefragmentationBegin(uint32_t frameIndex,
+ const VmaDefragmentationInfo2& info,
+ VmaDefragmentationContext ctx);
+ void RecordDefragmentationEnd(uint32_t frameIndex,
+ VmaDefragmentationContext ctx);
+
+private:
+ struct CallParams
+ {
+ uint32_t threadId;
+ double time;
+ };
+
+ class UserDataString
+ {
+ public:
+ UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData);
+ const char* GetString() const { return m_Str; }
+
+ private:
+ char m_PtrStr[17];
+ const char* m_Str;
+ };
+
+ bool m_UseMutex;
+ VmaRecordFlags m_Flags;
+ FILE* m_File;
+ VMA_MUTEX m_FileMutex;
+ int64_t m_Freq;
+ int64_t m_StartCounter;
+
+ void GetBasicParams(CallParams& outParams);
+
+ // T must be a pointer type, e.g. VmaAllocation, VmaPool.
+ template<typename T>
+ void PrintPointerList(uint64_t count, const T* pItems)
+ {
+ if(count)
+ {
+ fprintf(m_File, "%p", pItems[0]);
+ for(uint64_t i = 1; i < count; ++i)
+ {
+ fprintf(m_File, " %p", pItems[i]);
+ }
+ }
+ }
+
+ void PrintPointerList(uint64_t count, const VmaAllocation* pItems);
+ void Flush();
+};
+
+#endif // #if VMA_RECORDING_ENABLED
+
+// Main allocator object.
+struct VmaAllocator_T
+{
+ VMA_CLASS_NO_COPY(VmaAllocator_T)
+public:
+ bool m_UseMutex;
+ bool m_UseKhrDedicatedAllocation;
+ VkDevice m_hDevice;
+ bool m_AllocationCallbacksSpecified;
+ VkAllocationCallbacks m_AllocationCallbacks;
+ VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
+
+ // Number of bytes free out of limit, or VK_WHOLE_SIZE if no limit for that heap.
+ VkDeviceSize m_HeapSizeLimit[VK_MAX_MEMORY_HEAPS];
+ VMA_MUTEX m_HeapSizeLimitMutex;
+
+ VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
+ VkPhysicalDeviceMemoryProperties m_MemProps;
+
+ // Default pools.
+ VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
+
+ // Each vector is sorted by memory (handle value).
+ typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType;
+ AllocationVectorType* m_pDedicatedAllocations[VK_MAX_MEMORY_TYPES];
+ VMA_RW_MUTEX m_DedicatedAllocationsMutex[VK_MAX_MEMORY_TYPES];
+
+ VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
+ VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);
+ ~VmaAllocator_T();
+
+ const VkAllocationCallbacks* GetAllocationCallbacks() const
+ {
+ return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
+ }
+ const VmaVulkanFunctions& GetVulkanFunctions() const
+ {
+ return m_VulkanFunctions;
+ }
+
+ VkDeviceSize GetBufferImageGranularity() const
+ {
+ return VMA_MAX(
+ static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
+ m_PhysicalDeviceProperties.limits.bufferImageGranularity);
+ }
+
+ uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
+ uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
+
+ uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const
+ {
+ VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);
+ return m_MemProps.memoryTypes[memTypeIndex].heapIndex;
+ }
+ // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.
+ bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const
+ {
+ return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
+ }
+ // Minimum alignment for all allocations in specific memory type.
+ VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const
+ {
+ return IsMemoryTypeNonCoherent(memTypeIndex) ?
+ VMA_MAX((VkDeviceSize)VMA_DEBUG_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :
+ (VkDeviceSize)VMA_DEBUG_ALIGNMENT;
+ }
+
+ bool IsIntegratedGpu() const
+ {
+ return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
+ }
+
+#if VMA_RECORDING_ENABLED
+ VmaRecorder* GetRecorder() const { return m_pRecorder; }
+#endif
+
+ void GetBufferMemoryRequirements(
+ VkBuffer hBuffer,
+ VkMemoryRequirements& memReq,
+ bool& requiresDedicatedAllocation,
+ bool& prefersDedicatedAllocation) const;
+ void GetImageMemoryRequirements(
+ VkImage hImage,
+ VkMemoryRequirements& memReq,
+ bool& requiresDedicatedAllocation,
+ bool& prefersDedicatedAllocation) const;
+
+ // Main allocation function.
+ VkResult AllocateMemory(
+ const VkMemoryRequirements& vkMemReq,
+ bool requiresDedicatedAllocation,
+ bool prefersDedicatedAllocation,
+ VkBuffer dedicatedBuffer,
+ VkImage dedicatedImage,
+ const VmaAllocationCreateInfo& createInfo,
+ VmaSuballocationType suballocType,
+ size_t allocationCount,
+ VmaAllocation* pAllocations);
+
+ // Main deallocation function.
+ void FreeMemory(
+ size_t allocationCount,
+ const VmaAllocation* pAllocations);
+
+ VkResult ResizeAllocation(
+ const VmaAllocation alloc,
+ VkDeviceSize newSize);
+
+ void CalculateStats(VmaStats* pStats);
+
+#if VMA_STATS_STRING_ENABLED
+ void PrintDetailedMap(class VmaJsonWriter& json);
+#endif
+
+ VkResult DefragmentationBegin(
+ const VmaDefragmentationInfo2& info,
+ VmaDefragmentationStats* pStats,
+ VmaDefragmentationContext* pContext);
+ VkResult DefragmentationEnd(
+ VmaDefragmentationContext context);
+
+ void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
+ bool TouchAllocation(VmaAllocation hAllocation);
+
+ VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);
+ void DestroyPool(VmaPool pool);
+ void GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats);
+
+ void SetCurrentFrameIndex(uint32_t frameIndex);
+ uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }
+
+ void MakePoolAllocationsLost(
+ VmaPool hPool,
+ size_t* pLostAllocationCount);
+ VkResult CheckPoolCorruption(VmaPool hPool);
+ VkResult CheckCorruption(uint32_t memoryTypeBits);
+
+ void CreateLostAllocation(VmaAllocation* pAllocation);
+
+ VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);
+ void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);
+
+ VkResult Map(VmaAllocation hAllocation, void** ppData);
+ void Unmap(VmaAllocation hAllocation);
+
+ VkResult BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer);
+ VkResult BindImageMemory(VmaAllocation hAllocation, VkImage hImage);
+
+ void FlushOrInvalidateAllocation(
+ VmaAllocation hAllocation,
+ VkDeviceSize offset, VkDeviceSize size,
+ VMA_CACHE_OPERATION op);
+
+ void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
+
+private:
+ VkDeviceSize m_PreferredLargeHeapBlockSize;
+
+ VkPhysicalDevice m_PhysicalDevice;
+ VMA_ATOMIC_UINT32 m_CurrentFrameIndex;
+
+ VMA_RW_MUTEX m_PoolsMutex;
+ // Protected by m_PoolsMutex. Sorted by pointer value.
+ VmaVector<VmaPool, VmaStlAllocator<VmaPool> > m_Pools;
+ uint32_t m_NextPoolId;
+
+ VmaVulkanFunctions m_VulkanFunctions;
+
+#if VMA_RECORDING_ENABLED
+ VmaRecorder* m_pRecorder;
+#endif
+
+ void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);
+
+ VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
+
+ VkResult AllocateMemoryOfType(
+ VkDeviceSize size,
+ VkDeviceSize alignment,
+ bool dedicatedAllocation,
+ VkBuffer dedicatedBuffer,
+ VkImage dedicatedImage,
+ const VmaAllocationCreateInfo& createInfo,
+ uint32_t memTypeIndex,
+ VmaSuballocationType suballocType,
+ size_t allocationCount,
+ VmaAllocation* pAllocations);
+
+ // Helper function only to be used inside AllocateDedicatedMemory.
+ VkResult AllocateDedicatedMemoryPage(
+ VkDeviceSize size,
+ VmaSuballocationType suballocType,
+ uint32_t memTypeIndex,
+ const VkMemoryAllocateInfo& allocInfo,
+ bool map,
+ bool isUserDataString,
+ void* pUserData,
+ VmaAllocation* pAllocation);
+
+ // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.
+ VkResult AllocateDedicatedMemory(
+ VkDeviceSize size,
+ VmaSuballocationType suballocType,
+ uint32_t memTypeIndex,
+ bool map,
+ bool isUserDataString,
+ void* pUserData,
+ VkBuffer dedicatedBuffer,
+ VkImage dedicatedImage,
+ size_t allocationCount,
+ VmaAllocation* pAllocations);
+
+ // Tries to free pMemory as Dedicated Memory. Returns true if found and freed.
+ void FreeDedicatedMemory(VmaAllocation allocation);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Memory allocation #2 after VmaAllocator_T definition
+
+static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
+{
+ return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
+}
+
+static void VmaFree(VmaAllocator hAllocator, void* ptr)
+{
+ VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
+}
+
+template<typename T>
+static T* VmaAllocate(VmaAllocator hAllocator)
+{
+ return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
+}
+
+template<typename T>
+static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
+{
+ return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
+}
+
+template<typename T>
+static void vma_delete(VmaAllocator hAllocator, T* ptr)
+{
+ if(ptr != VMA_NULL)
+ {
+ ptr->~T();
+ VmaFree(hAllocator, ptr);
+ }
+}
+
+template<typename T>
+static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
+{
+ if(ptr != VMA_NULL)
+ {
+ for(size_t i = count; i--; )
+ ptr[i].~T();
+ VmaFree(hAllocator, ptr);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// VmaStringBuilder
+
+#if VMA_STATS_STRING_ENABLED
+
+class VmaStringBuilder
+{
+public:
+ VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
+ size_t GetLength() const { return m_Data.size(); }
+ const char* GetData() const { return m_Data.data(); }
+
+ void Add(char ch) { m_Data.push_back(ch); }
+ void Add(const char* pStr);
+ void AddNewLine() { Add('\n'); }
+ void AddNumber(uint32_t num);
+ void AddNumber(uint64_t num);
+ void AddPointer(const void* ptr);
+
+private:
+ VmaVector< char, VmaStlAllocator<char> > m_Data;
+};
+
+void VmaStringBuilder::Add(const char* pStr)
+{
+ const size_t strLen = strlen(pStr);
+ if(strLen > 0)
+ {
+ const size_t oldCount = m_Data.size();
+ m_Data.resize(oldCount + strLen);
+ memcpy(m_Data.data() + oldCount, pStr, strLen);
+ }
+}
+
+void VmaStringBuilder::AddNumber(uint32_t num)
+{
+ char buf[11];
+ VmaUint32ToStr(buf, sizeof(buf), num);
+ Add(buf);
+}
+
+void VmaStringBuilder::AddNumber(uint64_t num)
+{
+ char buf[21];
+ VmaUint64ToStr(buf, sizeof(buf), num);
+ Add(buf);
+}
+
+void VmaStringBuilder::AddPointer(const void* ptr)
+{
+ char buf[21];
+ VmaPtrToStr(buf, sizeof(buf), ptr);
+ Add(buf);
+}
+
+#endif // #if VMA_STATS_STRING_ENABLED
+
+////////////////////////////////////////////////////////////////////////////////
+// VmaJsonWriter
+
+#if VMA_STATS_STRING_ENABLED
+
+class VmaJsonWriter
+{
+ VMA_CLASS_NO_COPY(VmaJsonWriter)
+public:
+ VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);
+ ~VmaJsonWriter();
+
+ void BeginObject(bool singleLine = false);
+ void EndObject();
+
+ void BeginArray(bool singleLine = false);
+ void EndArray();
+
+ void WriteString(const char* pStr);
+ void BeginString(const char* pStr = VMA_NULL);
+ void ContinueString(const char* pStr);
+ void ContinueString(uint32_t n);
+ void ContinueString(uint64_t n);
+ void ContinueString_Pointer(const void* ptr);
+ void EndString(const char* pStr = VMA_NULL);
+
+ void WriteNumber(uint32_t n);
+ void WriteNumber(uint64_t n);
+ void WriteBool(bool b);
+ void WriteNull();
+
+private:
+ static const char* const INDENT;
+
+ enum COLLECTION_TYPE
+ {
+ COLLECTION_TYPE_OBJECT,
+ COLLECTION_TYPE_ARRAY,
+ };
+ struct StackItem
+ {
+ COLLECTION_TYPE type;
+ uint32_t valueCount;
+ bool singleLineMode;
+ };
+
+ VmaStringBuilder& m_SB;
+ VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;
+ bool m_InsideString;
+
+ void BeginValue(bool isString);
+ void WriteIndent(bool oneLess = false);
+};
+
+const char* const VmaJsonWriter::INDENT = " ";
+
+VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) :
+ m_SB(sb),
+ m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),
+ m_InsideString(false)
+{
+}
+
+VmaJsonWriter::~VmaJsonWriter()
+{
+ VMA_ASSERT(!m_InsideString);
+ VMA_ASSERT(m_Stack.empty());
+}
+
+void VmaJsonWriter::BeginObject(bool singleLine)
+{
+ VMA_ASSERT(!m_InsideString);
+
+ BeginValue(false);
+ m_SB.Add('{');
+
+ StackItem item;
+ item.type = COLLECTION_TYPE_OBJECT;
+ item.valueCount = 0;
+ item.singleLineMode = singleLine;
+ m_Stack.push_back(item);
+}
+
+void VmaJsonWriter::EndObject()
+{
+ VMA_ASSERT(!m_InsideString);
+
+ WriteIndent(true);
+ m_SB.Add('}');
+
+ VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);
+ m_Stack.pop_back();
+}
+
+void VmaJsonWriter::BeginArray(bool singleLine)
+{
+ VMA_ASSERT(!m_InsideString);
+
+ BeginValue(false);
+ m_SB.Add('[');
+
+ StackItem item;
+ item.type = COLLECTION_TYPE_ARRAY;
+ item.valueCount = 0;
+ item.singleLineMode = singleLine;
+ m_Stack.push_back(item);
+}
+
+void VmaJsonWriter::EndArray()
+{
+ VMA_ASSERT(!m_InsideString);
+
+ WriteIndent(true);
+ m_SB.Add(']');
+
+ VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);
+ m_Stack.pop_back();
+}
+
+void VmaJsonWriter::WriteString(const char* pStr)
+{
+ BeginString(pStr);
+ EndString();
+}
+
+void VmaJsonWriter::BeginString(const char* pStr)
+{
+ VMA_ASSERT(!m_InsideString);
+
+ BeginValue(true);
+ m_SB.Add('"');
+ m_InsideString = true;
+ if(pStr != VMA_NULL && pStr[0] != '\0')
+ {
+ ContinueString(pStr);
+ }
+}
+
+void VmaJsonWriter::ContinueString(const char* pStr)
+{
+ VMA_ASSERT(m_InsideString);
+
+ const size_t strLen = strlen(pStr);
+ for(size_t i = 0; i < strLen; ++i)
+ {
+ char ch = pStr[i];
+ if(ch == '\\')
+ {
+ m_SB.Add("\\\\");
+ }
+ else if(ch == '"')
+ {
+ m_SB.Add("\\\"");
+ }
+ else if(ch >= 32)
+ {
+ m_SB.Add(ch);
+ }
+ else switch(ch)
+ {
+ case '\b':
+ m_SB.Add("\\b");
+ break;
+ case '\f':
+ m_SB.Add("\\f");
+ break;
+ case '\n':
+ m_SB.Add("\\n");
+ break;
+ case '\r':
+ m_SB.Add("\\r");
+ break;
+ case '\t':
+ m_SB.Add("\\t");
+ break;
+ default:
+ VMA_ASSERT(0 && "Character not currently supported.");
+ break;
+ }
+ }
+}
+
+void VmaJsonWriter::ContinueString(uint32_t n)
+{
+ VMA_ASSERT(m_InsideString);
+ m_SB.AddNumber(n);
+}
+
+void VmaJsonWriter::ContinueString(uint64_t n)
+{
+ VMA_ASSERT(m_InsideString);
+ m_SB.AddNumber(n);
+}
+
+void VmaJsonWriter::ContinueString_Pointer(const void* ptr)
+{
+ VMA_ASSERT(m_InsideString);
+ m_SB.AddPointer(ptr);
+}
+
+void VmaJsonWriter::EndString(const char* pStr)
+{
+ VMA_ASSERT(m_InsideString);
+ if(pStr != VMA_NULL && pStr[0] != '\0')
+ {
+ ContinueString(pStr);
+ }
+ m_SB.Add('"');
+ m_InsideString = false;
+}
+
+void VmaJsonWriter::WriteNumber(uint32_t n)
+{
+ VMA_ASSERT(!m_InsideString);
+ BeginValue(false);
+ m_SB.AddNumber(n);
+}
+
+void VmaJsonWriter::WriteNumber(uint64_t n)
+{
+ VMA_ASSERT(!m_InsideString);
+ BeginValue(false);
+ m_SB.AddNumber(n);
+}
+
+void VmaJsonWriter::WriteBool(bool b)
+{
+ VMA_ASSERT(!m_InsideString);
+ BeginValue(false);
+ m_SB.Add(b ? "true" : "false");
+}
+
+void VmaJsonWriter::WriteNull()
+{
+ VMA_ASSERT(!m_InsideString);
+ BeginValue(false);
+ m_SB.Add("null");
+}
+
+void VmaJsonWriter::BeginValue(bool isString)
+{
+ if(!m_Stack.empty())
+ {
+ StackItem& currItem = m_Stack.back();
+ if(currItem.type == COLLECTION_TYPE_OBJECT &&
+ currItem.valueCount % 2 == 0)
+ {
+ (void) isString;
+ VMA_ASSERT(isString);
+ }
+
+ if(currItem.type == COLLECTION_TYPE_OBJECT &&
+ currItem.valueCount % 2 != 0)
+ {
+ m_SB.Add(": ");
+ }
+ else if(currItem.valueCount > 0)
+ {
+ m_SB.Add(", ");
+ WriteIndent();
+ }
+ else
+ {
+ WriteIndent();
+ }
+ ++currItem.valueCount;
+ }
+}
+
+void VmaJsonWriter::WriteIndent(bool oneLess)
+{
+ if(!m_Stack.empty() && !m_Stack.back().singleLineMode)
+ {
+ m_SB.AddNewLine();
+
+ size_t count = m_Stack.size();
+ if(count > 0 && oneLess)
+ {
+ --count;
+ }
+ for(size_t i = 0; i < count; ++i)
+ {
+ m_SB.Add(INDENT);
+ }
+ }
+}
+
+#endif // #if VMA_STATS_STRING_ENABLED
+
+////////////////////////////////////////////////////////////////////////////////
+
+void VmaAllocation_T::SetUserData(VmaAllocator hAllocator, void* pUserData)
+{
+ if(IsUserDataString())
+ {
+ VMA_ASSERT(pUserData == VMA_NULL || pUserData != m_pUserData);
+
+ FreeUserDataString(hAllocator);
+
+ if(pUserData != VMA_NULL)
+ {
+ const char* const newStrSrc = (char*)pUserData;
+ const size_t newStrLen = strlen(newStrSrc);
+ char* const newStrDst = vma_new_array(hAllocator, char, newStrLen + 1);
+ memcpy(newStrDst, newStrSrc, newStrLen + 1);
+ m_pUserData = newStrDst;
+ }
+ }
+ else
+ {
+ m_pUserData = pUserData;
+ }
+}
+
+void VmaAllocation_T::ChangeBlockAllocation(
+ VmaAllocator hAllocator,
+ VmaDeviceMemoryBlock* block,
+ VkDeviceSize offset)
+{
+ VMA_ASSERT(block != VMA_NULL);
+ VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
+
+ // Move mapping reference counter from old block to new block.
+ if(block != m_BlockAllocation.m_Block)
+ {
+ uint32_t mapRefCount = m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP;
+ if(IsPersistentMap())
+ ++mapRefCount;
+ m_BlockAllocation.m_Block->Unmap(hAllocator, mapRefCount);
+ block->Map(hAllocator, mapRefCount, VMA_NULL);
+ }
+
+ m_BlockAllocation.m_Block = block;
+ m_BlockAllocation.m_Offset = offset;
+}
+
+void VmaAllocation_T::ChangeSize(VkDeviceSize newSize)
+{
+ VMA_ASSERT(newSize > 0);
+ m_Size = newSize;
+}
+
+void VmaAllocation_T::ChangeOffset(VkDeviceSize newOffset)
+{
+ VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
+ m_BlockAllocation.m_Offset = newOffset;
+}
+
+VkDeviceSize VmaAllocation_T::GetOffset() const
+{
+ switch(m_Type)
+ {
+ case ALLOCATION_TYPE_BLOCK:
+ return m_BlockAllocation.m_Offset;
+ case ALLOCATION_TYPE_DEDICATED:
+ return 0;
+ default:
+ VMA_ASSERT(0);
+ return 0;
+ }
+}
+
+VkDeviceMemory VmaAllocation_T::GetMemory() const
+{
+ switch(m_Type)
+ {
+ case ALLOCATION_TYPE_BLOCK:
+ return m_BlockAllocation.m_Block->GetDeviceMemory();
+ case ALLOCATION_TYPE_DEDICATED:
+ return m_DedicatedAllocation.m_hMemory;
+ default:
+ VMA_ASSERT(0);
+ return VK_NULL_HANDLE;
+ }
+}
+
+uint32_t VmaAllocation_T::GetMemoryTypeIndex() const
+{
+ switch(m_Type)
+ {
+ case ALLOCATION_TYPE_BLOCK:
+ return m_BlockAllocation.m_Block->GetMemoryTypeIndex();
+ case ALLOCATION_TYPE_DEDICATED:
+ return m_DedicatedAllocation.m_MemoryTypeIndex;
+ default:
+ VMA_ASSERT(0);
+ return UINT32_MAX;
+ }
+}
+
+void* VmaAllocation_T::GetMappedData() const
+{
+ switch(m_Type)
+ {
+ case ALLOCATION_TYPE_BLOCK:
+ if(m_MapCount != 0)
+ {
+ void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();
+ VMA_ASSERT(pBlockData != VMA_NULL);
+ return (char*)pBlockData + m_BlockAllocation.m_Offset;
+ }
+ else
+ {
+ return VMA_NULL;
+ }
+ break;
+ case ALLOCATION_TYPE_DEDICATED:
+ VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0));
+ return m_DedicatedAllocation.m_pMappedData;
+ default:
+ VMA_ASSERT(0);
+ return VMA_NULL;
+ }
+}
+
+bool VmaAllocation_T::CanBecomeLost() const
+{
+ switch(m_Type)
+ {
+ case ALLOCATION_TYPE_BLOCK:
+ return m_BlockAllocation.m_CanBecomeLost;
+ case ALLOCATION_TYPE_DEDICATED:
+ return false;
+ default:
+ VMA_ASSERT(0);
+ return false;
+ }
+}
+
+VmaPool VmaAllocation_T::GetPool() const
+{
+ VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
+ return m_BlockAllocation.m_hPool;
+}
+
+bool VmaAllocation_T::MakeLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
+{
+ VMA_ASSERT(CanBecomeLost());
+
+ /*
+ Warning: This is a carefully designed algorithm.
+ Do not modify unless you really know what you're doing :)
+ */
+ uint32_t localLastUseFrameIndex = GetLastUseFrameIndex();
+ for(;;)
+ {
+ if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
+ {
+ VMA_ASSERT(0);
+ return false;
+ }
+ else if(localLastUseFrameIndex + frameInUseCount >= currentFrameIndex)
+ {
+ return false;
+ }
+ else // Last use time earlier than current time.
+ {
+ if(CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, VMA_FRAME_INDEX_LOST))
+ {
+ // Setting hAllocation.LastUseFrameIndex atomic to VMA_FRAME_INDEX_LOST is enough to mark it as LOST.
+ // Calling code just needs to unregister this allocation in owning VmaDeviceMemoryBlock.
+ return true;
+ }
+ }
+ }
+}
+
+#if VMA_STATS_STRING_ENABLED
+
+// Correspond to values of enum VmaSuballocationType.
+static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
+ "FREE",
+ "UNKNOWN",
+ "BUFFER",
+ "IMAGE_UNKNOWN",
+ "IMAGE_LINEAR",
+ "IMAGE_OPTIMAL",
+};
+
+void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const
+{
+ json.WriteString("Type");
+ json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);
+
+ json.WriteString("Size");
+ json.WriteNumber(m_Size);
+
+ if(m_pUserData != VMA_NULL)
+ {
+ json.WriteString("UserData");
+ if(IsUserDataString())
+ {
+ json.WriteString((const char*)m_pUserData);
+ }
+ else
+ {
+ json.BeginString();
+ json.ContinueString_Pointer(m_pUserData);
+ json.EndString();
+ }
+ }
+
+ json.WriteString("CreationFrameIndex");
+ json.WriteNumber(m_CreationFrameIndex);
+
+ json.WriteString("LastUseFrameIndex");
+ json.WriteNumber(GetLastUseFrameIndex());
+
+ if(m_BufferImageUsage != 0)
+ {
+ json.WriteString("Usage");
+ json.WriteNumber(m_BufferImageUsage);
+ }
+}
+
+#endif
+
+void VmaAllocation_T::FreeUserDataString(VmaAllocator hAllocator)
+{
+ VMA_ASSERT(IsUserDataString());
+ if(m_pUserData != VMA_NULL)
+ {
+ char* const oldStr = (char*)m_pUserData;
+ const size_t oldStrLen = strlen(oldStr);
+ vma_delete_array(hAllocator, oldStr, oldStrLen + 1);
+ m_pUserData = VMA_NULL;
+ }
+}
+
+void VmaAllocation_T::BlockAllocMap()
+{
+ VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
+
+ if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
+ {
+ ++m_MapCount;
+ }
+ else
+ {
+ VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");
+ }
+}
+
+void VmaAllocation_T::BlockAllocUnmap()
+{
+ VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);
+
+ if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
+ {
+ --m_MapCount;
+ }
+ else
+ {
+ VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");
+ }
+}
+
+VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)
+{
+ VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
+
+ if(m_MapCount != 0)
+ {
+ if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) < 0x7F)
+ {
+ VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);
+ *ppData = m_DedicatedAllocation.m_pMappedData;
+ ++m_MapCount;
+ return VK_SUCCESS;
+ }
+ else
+ {
+ VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");
+ return VK_ERROR_MEMORY_MAP_FAILED;
+ }
+ }
+ else
+ {
+ VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
+ hAllocator->m_hDevice,
+ m_DedicatedAllocation.m_hMemory,
+ 0, // offset
+ VK_WHOLE_SIZE,
+ 0, // flags
+ ppData);
+ if(result == VK_SUCCESS)
+ {
+ m_DedicatedAllocation.m_pMappedData = *ppData;
+ m_MapCount = 1;
+ }
+ return result;
+ }
+}
+
+void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)
+{
+ VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);
+
+ if((m_MapCount & ~MAP_COUNT_FLAG_PERSISTENT_MAP) != 0)
+ {
+ --m_MapCount;
+ if(m_MapCount == 0)
+ {
+ m_DedicatedAllocation.m_pMappedData = VMA_NULL;
+ (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(
+ hAllocator->m_hDevice,
+ m_DedicatedAllocation.m_hMemory);
+ }
+ }
+ else
+ {
+ VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");
+ }
+}
+
+#if VMA_STATS_STRING_ENABLED
+
+static void VmaPrintStatInfo(VmaJsonWriter& json, const VmaStatInfo& stat)
+{
+ json.BeginObject();
+
+ json.WriteString("Blocks");
+ json.WriteNumber(stat.blockCount);
+
+ json.WriteString("Allocations");
+ json.WriteNumber(stat.allocationCount);
+
+ json.WriteString("UnusedRanges");
+ json.WriteNumber(stat.unusedRangeCount);
+
+ json.WriteString("UsedBytes");
+ json.WriteNumber(stat.usedBytes);
+
+ json.WriteString("UnusedBytes");
+ json.WriteNumber(stat.unusedBytes);
+
+ if(stat.allocationCount > 1)
+ {
+ json.WriteString("AllocationSize");
+ json.BeginObject(true);
+ json.WriteString("Min");
+ json.WriteNumber(stat.allocationSizeMin);
+ json.WriteString("Avg");
+ json.WriteNumber(stat.allocationSizeAvg);
+ json.WriteString("Max");
+ json.WriteNumber(stat.allocationSizeMax);
+ json.EndObject();
+ }
+
+ if(stat.unusedRangeCount > 1)
+ {
+ json.WriteString("UnusedRangeSize");
+ json.BeginObject(true);
+ json.WriteString("Min");
+ json.WriteNumber(stat.unusedRangeSizeMin);
+ json.WriteString("Avg");
+ json.WriteNumber(stat.unusedRangeSizeAvg);
+ json.WriteString("Max");
+ json.WriteNumber(stat.unusedRangeSizeMax);
+ json.EndObject();
+ }
+
+ json.EndObject();
+}
+
+#endif // #if VMA_STATS_STRING_ENABLED
+
+struct VmaSuballocationItemSizeLess
+{
+ bool operator()(
+ const VmaSuballocationList::iterator lhs,
+ const VmaSuballocationList::iterator rhs) const
+ {
+ return lhs->size < rhs->size;
+ }
+ bool operator()(
+ const VmaSuballocationList::iterator lhs,
+ VkDeviceSize rhsSize) const
+ {
+ return lhs->size < rhsSize;
+ }
+};
+
+
+////////////////////////////////////////////////////////////////////////////////
+// class VmaBlockMetadata
+
+VmaBlockMetadata::VmaBlockMetadata(VmaAllocator hAllocator) :
+ m_Size(0),
+ m_pAllocationCallbacks(hAllocator->GetAllocationCallbacks())
+{
+}
+
+#if VMA_STATS_STRING_ENABLED
+
+void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,
+ VkDeviceSize unusedBytes,
+ size_t allocationCount,
+ size_t unusedRangeCount) const
+{
+ json.BeginObject();
+
+ json.WriteString("TotalBytes");
+ json.WriteNumber(GetSize());
+
+ json.WriteString("UnusedBytes");
+ json.WriteNumber(unusedBytes);
+
+ json.WriteString("Allocations");
+ json.WriteNumber((uint64_t)allocationCount);
+
+ json.WriteString("UnusedRanges");
+ json.WriteNumber((uint64_t)unusedRangeCount);
+
+ json.WriteString("Suballocations");
+ json.BeginArray();
+}
+
+void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,
+ VkDeviceSize offset,
+ VmaAllocation hAllocation) const
+{
+ json.BeginObject(true);
+
+ json.WriteString("Offset");
+ json.WriteNumber(offset);
+
+ hAllocation->PrintParameters(json);
+
+ json.EndObject();
+}
+
+void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,
+ VkDeviceSize offset,
+ VkDeviceSize size) const
+{
+ json.BeginObject(true);
+
+ json.WriteString("Offset");
+ json.WriteNumber(offset);
+
+ json.WriteString("Type");
+ json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);
+
+ json.WriteString("Size");
+ json.WriteNumber(size);
+
+ json.EndObject();
+}
+
+void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const
+{
+ json.EndArray();
+ json.EndObject();
+}
+
+#endif // #if VMA_STATS_STRING_ENABLED
+
+////////////////////////////////////////////////////////////////////////////////
+// class VmaBlockMetadata_Generic
+
+VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(VmaAllocator hAllocator) :
+ VmaBlockMetadata(hAllocator),
+ m_FreeCount(0),
+ m_SumFreeSize(0),
+ m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
+ m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
+{
+}
+
+VmaBlockMetadata_Generic::~VmaBlockMetadata_Generic()
+{
+}
+
+void VmaBlockMetadata_Generic::Init(VkDeviceSize size)
+{
+ VmaBlockMetadata::Init(size);
+
+ m_FreeCount = 1;
+ m_SumFreeSize = size;
+
+ VmaSuballocation suballoc = {};
+ suballoc.offset = 0;
+ suballoc.size = size;
+ suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
+ suballoc.hAllocation = VK_NULL_HANDLE;
+
+ VMA_ASSERT(size > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
+ m_Suballocations.push_back(suballoc);
+ VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
+ --suballocItem;
+ m_FreeSuballocationsBySize.push_back(suballocItem);
+}
+
+bool VmaBlockMetadata_Generic::Validate() const
+{
+ VMA_VALIDATE(!m_Suballocations.empty());
+
+ // Expected offset of new suballocation as calculated from previous ones.
+ VkDeviceSize calculatedOffset = 0;
+ // Expected number of free suballocations as calculated from traversing their list.
+ uint32_t calculatedFreeCount = 0;
+ // Expected sum size of free suballocations as calculated from traversing their list.
+ VkDeviceSize calculatedSumFreeSize = 0;
+ // Expected number of free suballocations that should be registered in
+ // m_FreeSuballocationsBySize calculated from traversing their list.
+ size_t freeSuballocationsToRegister = 0;
+ // True if previous visited suballocation was free.
+ bool prevFree = false;
+
+ for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
+ suballocItem != m_Suballocations.cend();
+ ++suballocItem)
+ {
+ const VmaSuballocation& subAlloc = *suballocItem;
+
+ // Actual offset of this suballocation doesn't match expected one.
+ VMA_VALIDATE(subAlloc.offset == calculatedOffset);
+
+ const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
+ // Two adjacent free suballocations are invalid. They should be merged.
+ VMA_VALIDATE(!prevFree || !currFree);
+
+ VMA_VALIDATE(currFree == (subAlloc.hAllocation == VK_NULL_HANDLE));
+
+ if(currFree)
+ {
+ calculatedSumFreeSize += subAlloc.size;
+ ++calculatedFreeCount;
+ if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
+ {
+ ++freeSuballocationsToRegister;
+ }
+
+ // Margin required between allocations - every free space must be at least that large.
+#if VMA_DEBUG_MARGIN
+ VMA_VALIDATE(subAlloc.size >= VMA_DEBUG_MARGIN);
+#endif
+ }
+ else
+ {
+ VMA_VALIDATE(subAlloc.hAllocation->GetOffset() == subAlloc.offset);
+ VMA_VALIDATE(subAlloc.hAllocation->GetSize() == subAlloc.size);
+
+ // Margin required between allocations - previous allocation must be free.
+ VMA_VALIDATE(VMA_DEBUG_MARGIN == 0 || prevFree);
+ }
+
+ calculatedOffset += subAlloc.size;
+ prevFree = currFree;
+ }
+
+ // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
+ // match expected one.
+ VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);
+
+ VkDeviceSize lastSize = 0;
+ for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
+ {
+ VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
+
+ // Only free suballocations can be registered in m_FreeSuballocationsBySize.
+ VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);
+ // They must be sorted by size ascending.
+ VMA_VALIDATE(suballocItem->size >= lastSize);
+
+ lastSize = suballocItem->size;
+ }
+
+ // Check if totals match calculacted values.
+ VMA_VALIDATE(ValidateFreeSuballocationList());
+ VMA_VALIDATE(calculatedOffset == GetSize());
+ VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);
+ VMA_VALIDATE(calculatedFreeCount == m_FreeCount);
+
+ return true;
+}
+
+VkDeviceSize VmaBlockMetadata_Generic::GetUnusedRangeSizeMax() const
+{
+ if(!m_FreeSuballocationsBySize.empty())
+ {
+ return m_FreeSuballocationsBySize.back()->size;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+bool VmaBlockMetadata_Generic::IsEmpty() const
+{
+ return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
+}
+
+void VmaBlockMetadata_Generic::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
+{
+ outInfo.blockCount = 1;
+
+ const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
+ outInfo.allocationCount = rangeCount - m_FreeCount;
+ outInfo.unusedRangeCount = m_FreeCount;
+
+ outInfo.unusedBytes = m_SumFreeSize;
+ outInfo.usedBytes = GetSize() - outInfo.unusedBytes;
+
+ outInfo.allocationSizeMin = UINT64_MAX;
+ outInfo.allocationSizeMax = 0;
+ outInfo.unusedRangeSizeMin = UINT64_MAX;
+ outInfo.unusedRangeSizeMax = 0;
+
+ for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
+ suballocItem != m_Suballocations.cend();
+ ++suballocItem)
+ {
+ const VmaSuballocation& suballoc = *suballocItem;
+ if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
+ outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, suballoc.size);
+ }
+ else
+ {
+ outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, suballoc.size);
+ outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, suballoc.size);
+ }
+ }
+}
+
+void VmaBlockMetadata_Generic::AddPoolStats(VmaPoolStats& inoutStats) const
+{
+ const uint32_t rangeCount = (uint32_t)m_Suballocations.size();
+
+ inoutStats.size += GetSize();
+ inoutStats.unusedSize += m_SumFreeSize;
+ inoutStats.allocationCount += rangeCount - m_FreeCount;
+ inoutStats.unusedRangeCount += m_FreeCount;
+ inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
+}
+
+#if VMA_STATS_STRING_ENABLED
+
+void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const
+{
+ PrintDetailedMap_Begin(json,
+ m_SumFreeSize, // unusedBytes
+ m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount
+ m_FreeCount); // unusedRangeCount
+
+ size_t i = 0;
+ for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
+ suballocItem != m_Suballocations.cend();
+ ++suballocItem, ++i)
+ {
+ if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ PrintDetailedMap_UnusedRange(json, suballocItem->offset, suballocItem->size);
+ }
+ else
+ {
+ PrintDetailedMap_Allocation(json, suballocItem->offset, suballocItem->hAllocation);
+ }
+ }
+
+ PrintDetailedMap_End(json);
+}
+
+#endif // #if VMA_STATS_STRING_ENABLED
+
+bool VmaBlockMetadata_Generic::CreateAllocationRequest(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VkDeviceSize bufferImageGranularity,
+ VkDeviceSize allocSize,
+ VkDeviceSize allocAlignment,
+ bool upperAddress,
+ VmaSuballocationType allocType,
+ bool canMakeOtherLost,
+ uint32_t strategy,
+ VmaAllocationRequest* pAllocationRequest)
+{
+ VMA_ASSERT(allocSize > 0);
+ VMA_ASSERT(!upperAddress);
+ (void) upperAddress;
+ VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
+ VMA_ASSERT(pAllocationRequest != VMA_NULL);
+ VMA_HEAVY_ASSERT(Validate());
+
+ // There is not enough total free space in this block to fullfill the request: Early return.
+ if(canMakeOtherLost == false &&
+ m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN)
+ {
+ return false;
+ }
+
+ // New algorithm, efficiently searching freeSuballocationsBySize.
+ const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
+ if(freeSuballocCount > 0)
+ {
+ if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
+ {
+ // Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN.
+ VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
+ m_FreeSuballocationsBySize.data(),
+ m_FreeSuballocationsBySize.data() + freeSuballocCount,
+ allocSize + 2 * VMA_DEBUG_MARGIN,
+ VmaSuballocationItemSizeLess());
+ size_t index = it - m_FreeSuballocationsBySize.data();
+ for(; index < freeSuballocCount; ++index)
+ {
+ if(CheckAllocation(
+ currentFrameIndex,
+ frameInUseCount,
+ bufferImageGranularity,
+ allocSize,
+ allocAlignment,
+ allocType,
+ m_FreeSuballocationsBySize[index],
+ false, // canMakeOtherLost
+ &pAllocationRequest->offset,
+ &pAllocationRequest->itemsToMakeLostCount,
+ &pAllocationRequest->sumFreeSize,
+ &pAllocationRequest->sumItemSize))
+ {
+ pAllocationRequest->item = m_FreeSuballocationsBySize[index];
+ return true;
+ }
+ }
+ }
+ else if(strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)
+ {
+ for(VmaSuballocationList::iterator it = m_Suballocations.begin();
+ it != m_Suballocations.end();
+ ++it)
+ {
+ if(it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(
+ currentFrameIndex,
+ frameInUseCount,
+ bufferImageGranularity,
+ allocSize,
+ allocAlignment,
+ allocType,
+ it,
+ false, // canMakeOtherLost
+ &pAllocationRequest->offset,
+ &pAllocationRequest->itemsToMakeLostCount,
+ &pAllocationRequest->sumFreeSize,
+ &pAllocationRequest->sumItemSize))
+ {
+ pAllocationRequest->item = it;
+ return true;
+ }
+ }
+ }
+ else // WORST_FIT, FIRST_FIT
+ {
+ // Search staring from biggest suballocations.
+ for(size_t index = freeSuballocCount; index--; )
+ {
+ if(CheckAllocation(
+ currentFrameIndex,
+ frameInUseCount,
+ bufferImageGranularity,
+ allocSize,
+ allocAlignment,
+ allocType,
+ m_FreeSuballocationsBySize[index],
+ false, // canMakeOtherLost
+ &pAllocationRequest->offset,
+ &pAllocationRequest->itemsToMakeLostCount,
+ &pAllocationRequest->sumFreeSize,
+ &pAllocationRequest->sumItemSize))
+ {
+ pAllocationRequest->item = m_FreeSuballocationsBySize[index];
+ return true;
+ }
+ }
+ }
+ }
+
+ if(canMakeOtherLost)
+ {
+ // Brute-force algorithm. TODO: Come up with something better.
+
+ pAllocationRequest->sumFreeSize = VK_WHOLE_SIZE;
+ pAllocationRequest->sumItemSize = VK_WHOLE_SIZE;
+
+ VmaAllocationRequest tmpAllocRequest = {};
+ for(VmaSuballocationList::iterator suballocIt = m_Suballocations.begin();
+ suballocIt != m_Suballocations.end();
+ ++suballocIt)
+ {
+ if(suballocIt->type == VMA_SUBALLOCATION_TYPE_FREE ||
+ suballocIt->hAllocation->CanBecomeLost())
+ {
+ if(CheckAllocation(
+ currentFrameIndex,
+ frameInUseCount,
+ bufferImageGranularity,
+ allocSize,
+ allocAlignment,
+ allocType,
+ suballocIt,
+ canMakeOtherLost,
+ &tmpAllocRequest.offset,
+ &tmpAllocRequest.itemsToMakeLostCount,
+ &tmpAllocRequest.sumFreeSize,
+ &tmpAllocRequest.sumItemSize))
+ {
+ tmpAllocRequest.item = suballocIt;
+
+ if(tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost() ||
+ strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
+ {
+ *pAllocationRequest = tmpAllocRequest;
+ }
+ }
+ }
+ }
+
+ if(pAllocationRequest->sumItemSize != VK_WHOLE_SIZE)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool VmaBlockMetadata_Generic::MakeRequestedAllocationsLost(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VmaAllocationRequest* pAllocationRequest)
+{
+ while(pAllocationRequest->itemsToMakeLostCount > 0)
+ {
+ if(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ ++pAllocationRequest->item;
+ }
+ VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
+ VMA_ASSERT(pAllocationRequest->item->hAllocation != VK_NULL_HANDLE);
+ VMA_ASSERT(pAllocationRequest->item->hAllocation->CanBecomeLost());
+ if(pAllocationRequest->item->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
+ {
+ pAllocationRequest->item = FreeSuballocation(pAllocationRequest->item);
+ --pAllocationRequest->itemsToMakeLostCount;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ VMA_HEAVY_ASSERT(Validate());
+ VMA_ASSERT(pAllocationRequest->item != m_Suballocations.end());
+ VMA_ASSERT(pAllocationRequest->item->type == VMA_SUBALLOCATION_TYPE_FREE);
+
+ return true;
+}
+
+uint32_t VmaBlockMetadata_Generic::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
+{
+ uint32_t lostAllocationCount = 0;
+ for(VmaSuballocationList::iterator it = m_Suballocations.begin();
+ it != m_Suballocations.end();
+ ++it)
+ {
+ if(it->type != VMA_SUBALLOCATION_TYPE_FREE &&
+ it->hAllocation->CanBecomeLost() &&
+ it->hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
+ {
+ it = FreeSuballocation(it);
+ ++lostAllocationCount;
+ }
+ }
+ return lostAllocationCount;
+}
+
+VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)
+{
+ for(VmaSuballocationList::iterator it = m_Suballocations.begin();
+ it != m_Suballocations.end();
+ ++it)
+ {
+ if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ if(!VmaValidateMagicValue(pBlockData, it->offset - VMA_DEBUG_MARGIN))
+ {
+ VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
+ return VK_ERROR_VALIDATION_FAILED_EXT;
+ }
+ if(!VmaValidateMagicValue(pBlockData, it->offset + it->size))
+ {
+ VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
+ return VK_ERROR_VALIDATION_FAILED_EXT;
+ }
+ }
+ }
+
+ return VK_SUCCESS;
+}
+
+void VmaBlockMetadata_Generic::Alloc(
+ const VmaAllocationRequest& request,
+ VmaSuballocationType type,
+ VkDeviceSize allocSize,
+ bool upperAddress,
+ VmaAllocation hAllocation)
+{
+ VMA_ASSERT(!upperAddress);
+ (void) upperAddress;
+ VMA_ASSERT(request.item != m_Suballocations.end());
+ VmaSuballocation& suballoc = *request.item;
+ // Given suballocation is a free block.
+ VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
+ // Given offset is inside this suballocation.
+ VMA_ASSERT(request.offset >= suballoc.offset);
+ const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
+ VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
+ const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
+
+ // Unregister this free suballocation from m_FreeSuballocationsBySize and update
+ // it to become used.
+ UnregisterFreeSuballocation(request.item);
+
+ suballoc.offset = request.offset;
+ suballoc.size = allocSize;
+ suballoc.type = type;
+ suballoc.hAllocation = hAllocation;
+
+ // If there are any free bytes remaining at the end, insert new free suballocation after current one.
+ if(paddingEnd)
+ {
+ VmaSuballocation paddingSuballoc = {};
+ paddingSuballoc.offset = request.offset + allocSize;
+ paddingSuballoc.size = paddingEnd;
+ paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
+ VmaSuballocationList::iterator next = request.item;
+ ++next;
+ const VmaSuballocationList::iterator paddingEndItem =
+ m_Suballocations.insert(next, paddingSuballoc);
+ RegisterFreeSuballocation(paddingEndItem);
+ }
+
+ // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
+ if(paddingBegin)
+ {
+ VmaSuballocation paddingSuballoc = {};
+ paddingSuballoc.offset = request.offset - paddingBegin;
+ paddingSuballoc.size = paddingBegin;
+ paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
+ const VmaSuballocationList::iterator paddingBeginItem =
+ m_Suballocations.insert(request.item, paddingSuballoc);
+ RegisterFreeSuballocation(paddingBeginItem);
+ }
+
+ // Update totals.
+ m_FreeCount = m_FreeCount - 1;
+ if(paddingBegin > 0)
+ {
+ ++m_FreeCount;
+ }
+ if(paddingEnd > 0)
+ {
+ ++m_FreeCount;
+ }
+ m_SumFreeSize -= allocSize;
+}
+
+void VmaBlockMetadata_Generic::Free(const VmaAllocation allocation)
+{
+ for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
+ suballocItem != m_Suballocations.end();
+ ++suballocItem)
+ {
+ VmaSuballocation& suballoc = *suballocItem;
+ if(suballoc.hAllocation == allocation)
+ {
+ FreeSuballocation(suballocItem);
+ VMA_HEAVY_ASSERT(Validate());
+ return;
+ }
+ }
+ VMA_ASSERT(0 && "Not found!");
+}
+
+void VmaBlockMetadata_Generic::FreeAtOffset(VkDeviceSize offset)
+{
+ for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
+ suballocItem != m_Suballocations.end();
+ ++suballocItem)
+ {
+ VmaSuballocation& suballoc = *suballocItem;
+ if(suballoc.offset == offset)
+ {
+ FreeSuballocation(suballocItem);
+ return;
+ }
+ }
+ VMA_ASSERT(0 && "Not found!");
+}
+
+bool VmaBlockMetadata_Generic::ResizeAllocation(const VmaAllocation alloc, VkDeviceSize newSize)
+{
+ typedef VmaSuballocationList::iterator iter_type;
+ for(iter_type suballocItem = m_Suballocations.begin();
+ suballocItem != m_Suballocations.end();
+ ++suballocItem)
+ {
+ VmaSuballocation& suballoc = *suballocItem;
+ if(suballoc.hAllocation == alloc)
+ {
+ iter_type nextItem = suballocItem;
+ ++nextItem;
+
+ // Should have been ensured on higher level.
+ VMA_ASSERT(newSize != alloc->GetSize() && newSize > 0);
+
+ // Shrinking.
+ if(newSize < alloc->GetSize())
+ {
+ const VkDeviceSize sizeDiff = suballoc.size - newSize;
+
+ // There is next item.
+ if(nextItem != m_Suballocations.end())
+ {
+ // Next item is free.
+ if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ // Grow this next item backward.
+ UnregisterFreeSuballocation(nextItem);
+ nextItem->offset -= sizeDiff;
+ nextItem->size += sizeDiff;
+ RegisterFreeSuballocation(nextItem);
+ }
+ // Next item is not free.
+ else
+ {
+ // Create free item after current one.
+ VmaSuballocation newFreeSuballoc;
+ newFreeSuballoc.hAllocation = VK_NULL_HANDLE;
+ newFreeSuballoc.offset = suballoc.offset + newSize;
+ newFreeSuballoc.size = sizeDiff;
+ newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
+ iter_type newFreeSuballocIt = m_Suballocations.insert(nextItem, newFreeSuballoc);
+ RegisterFreeSuballocation(newFreeSuballocIt);
+
+ ++m_FreeCount;
+ }
+ }
+ // This is the last item.
+ else
+ {
+ // Create free item at the end.
+ VmaSuballocation newFreeSuballoc;
+ newFreeSuballoc.hAllocation = VK_NULL_HANDLE;
+ newFreeSuballoc.offset = suballoc.offset + newSize;
+ newFreeSuballoc.size = sizeDiff;
+ newFreeSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
+ m_Suballocations.push_back(newFreeSuballoc);
+
+ iter_type newFreeSuballocIt = m_Suballocations.end();
+ RegisterFreeSuballocation(--newFreeSuballocIt);
+
+ ++m_FreeCount;
+ }
+
+ suballoc.size = newSize;
+ m_SumFreeSize += sizeDiff;
+ }
+ // Growing.
+ else
+ {
+ const VkDeviceSize sizeDiff = newSize - suballoc.size;
+
+ // There is next item.
+ if(nextItem != m_Suballocations.end())
+ {
+ // Next item is free.
+ if(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ // There is not enough free space, including margin.
+ if(nextItem->size < sizeDiff + VMA_DEBUG_MARGIN)
+ {
+ return false;
+ }
+
+ // There is more free space than required.
+ if(nextItem->size > sizeDiff)
+ {
+ // Move and shrink this next item.
+ UnregisterFreeSuballocation(nextItem);
+ nextItem->offset += sizeDiff;
+ nextItem->size -= sizeDiff;
+ RegisterFreeSuballocation(nextItem);
+ }
+ // There is exactly the amount of free space required.
+ else
+ {
+ // Remove this next free item.
+ UnregisterFreeSuballocation(nextItem);
+ m_Suballocations.erase(nextItem);
+ --m_FreeCount;
+ }
+ }
+ // Next item is not free - there is no space to grow.
+ else
+ {
+ return false;
+ }
+ }
+ // This is the last item - there is no space to grow.
+ else
+ {
+ return false;
+ }
+
+ suballoc.size = newSize;
+ m_SumFreeSize -= sizeDiff;
+ }
+
+ // We cannot call Validate() here because alloc object is updated to new size outside of this call.
+ return true;
+ }
+ }
+ VMA_ASSERT(0 && "Not found!");
+ return false;
+}
+
+bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const
+{
+ VkDeviceSize lastSize = 0;
+ for(size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)
+ {
+ const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];
+
+ VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);
+ VMA_VALIDATE(it->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER);
+ VMA_VALIDATE(it->size >= lastSize);
+ lastSize = it->size;
+ }
+ return true;
+}
+
+bool VmaBlockMetadata_Generic::CheckAllocation(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VkDeviceSize bufferImageGranularity,
+ VkDeviceSize allocSize,
+ VkDeviceSize allocAlignment,
+ VmaSuballocationType allocType,
+ VmaSuballocationList::const_iterator suballocItem,
+ bool canMakeOtherLost,
+ VkDeviceSize* pOffset,
+ size_t* itemsToMakeLostCount,
+ VkDeviceSize* pSumFreeSize,
+ VkDeviceSize* pSumItemSize) const
+{
+ VMA_ASSERT(allocSize > 0);
+ VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
+ VMA_ASSERT(suballocItem != m_Suballocations.cend());
+ VMA_ASSERT(pOffset != VMA_NULL);
+
+ *itemsToMakeLostCount = 0;
+ *pSumFreeSize = 0;
+ *pSumItemSize = 0;
+
+ if(canMakeOtherLost)
+ {
+ if(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ *pSumFreeSize = suballocItem->size;
+ }
+ else
+ {
+ if(suballocItem->hAllocation->CanBecomeLost() &&
+ suballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
+ {
+ ++*itemsToMakeLostCount;
+ *pSumItemSize = suballocItem->size;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ // Remaining size is too small for this request: Early return.
+ if(GetSize() - suballocItem->offset < allocSize)
+ {
+ return false;
+ }
+
+ // Start from offset equal to beginning of this suballocation.
+ *pOffset = suballocItem->offset;
+
+ // Apply VMA_DEBUG_MARGIN at the beginning.
+ if(VMA_DEBUG_MARGIN > 0)
+ {
+ *pOffset += VMA_DEBUG_MARGIN;
+ }
+
+ // Apply alignment.
+ *pOffset = VmaAlignUp(*pOffset, allocAlignment);
+
+ // Check previous suballocations for BufferImageGranularity conflicts.
+ // Make bigger alignment if necessary.
+ if(bufferImageGranularity > 1)
+ {
+ bool bufferImageGranularityConflict = false;
+ VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
+ while(prevSuballocItem != m_Suballocations.cbegin())
+ {
+ --prevSuballocItem;
+ const VmaSuballocation& prevSuballoc = *prevSuballocItem;
+ if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
+ {
+ if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
+ {
+ bufferImageGranularityConflict = true;
+ break;
+ }
+ }
+ else
+ // Already on previous page.
+ break;
+ }
+ if(bufferImageGranularityConflict)
+ {
+ *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
+ }
+ }
+
+ // Now that we have final *pOffset, check if we are past suballocItem.
+ // If yes, return false - this function should be called for another suballocItem as starting point.
+ if(*pOffset >= suballocItem->offset + suballocItem->size)
+ {
+ return false;
+ }
+
+ // Calculate padding at the beginning based on current offset.
+ const VkDeviceSize paddingBegin = *pOffset - suballocItem->offset;
+
+ // Calculate required margin at the end.
+ const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
+
+ const VkDeviceSize totalSize = paddingBegin + allocSize + requiredEndMargin;
+ // Another early return check.
+ if(suballocItem->offset + totalSize > GetSize())
+ {
+ return false;
+ }
+
+ // Advance lastSuballocItem until desired size is reached.
+ // Update itemsToMakeLostCount.
+ VmaSuballocationList::const_iterator lastSuballocItem = suballocItem;
+ if(totalSize > suballocItem->size)
+ {
+ VkDeviceSize remainingSize = totalSize - suballocItem->size;
+ while(remainingSize > 0)
+ {
+ ++lastSuballocItem;
+ if(lastSuballocItem == m_Suballocations.cend())
+ {
+ return false;
+ }
+ if(lastSuballocItem->type == VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ *pSumFreeSize += lastSuballocItem->size;
+ }
+ else
+ {
+ VMA_ASSERT(lastSuballocItem->hAllocation != VK_NULL_HANDLE);
+ if(lastSuballocItem->hAllocation->CanBecomeLost() &&
+ lastSuballocItem->hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
+ {
+ ++*itemsToMakeLostCount;
+ *pSumItemSize += lastSuballocItem->size;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ remainingSize = (lastSuballocItem->size < remainingSize) ?
+ remainingSize - lastSuballocItem->size : 0;
+ }
+ }
+
+ // Check next suballocations for BufferImageGranularity conflicts.
+ // If conflict exists, we must mark more allocations lost or fail.
+ if(bufferImageGranularity > 1)
+ {
+ VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
+ ++nextSuballocItem;
+ while(nextSuballocItem != m_Suballocations.cend())
+ {
+ const VmaSuballocation& nextSuballoc = *nextSuballocItem;
+ if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
+ {
+ if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
+ {
+ VMA_ASSERT(nextSuballoc.hAllocation != VK_NULL_HANDLE);
+ if(nextSuballoc.hAllocation->CanBecomeLost() &&
+ nextSuballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
+ {
+ ++*itemsToMakeLostCount;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ // Already on next page.
+ break;
+ }
+ ++nextSuballocItem;
+ }
+ }
+ }
+ else
+ {
+ const VmaSuballocation& suballoc = *suballocItem;
+ VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
+
+ *pSumFreeSize = suballoc.size;
+
+ // Size of this suballocation is too small for this request: Early return.
+ if(suballoc.size < allocSize)
+ {
+ return false;
+ }
+
+ // Start from offset equal to beginning of this suballocation.
+ *pOffset = suballoc.offset;
+
+ // Apply VMA_DEBUG_MARGIN at the beginning.
+ if(VMA_DEBUG_MARGIN > 0)
+ {
+ *pOffset += VMA_DEBUG_MARGIN;
+ }
+
+ // Apply alignment.
+ *pOffset = VmaAlignUp(*pOffset, allocAlignment);
+
+ // Check previous suballocations for BufferImageGranularity conflicts.
+ // Make bigger alignment if necessary.
+ if(bufferImageGranularity > 1)
+ {
+ bool bufferImageGranularityConflict = false;
+ VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
+ while(prevSuballocItem != m_Suballocations.cbegin())
+ {
+ --prevSuballocItem;
+ const VmaSuballocation& prevSuballoc = *prevSuballocItem;
+ if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
+ {
+ if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
+ {
+ bufferImageGranularityConflict = true;
+ break;
+ }
+ }
+ else
+ // Already on previous page.
+ break;
+ }
+ if(bufferImageGranularityConflict)
+ {
+ *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
+ }
+ }
+
+ // Calculate padding at the beginning based on current offset.
+ const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
+
+ // Calculate required margin at the end.
+ const VkDeviceSize requiredEndMargin = VMA_DEBUG_MARGIN;
+
+ // Fail if requested size plus margin before and after is bigger than size of this suballocation.
+ if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
+ {
+ return false;
+ }
+
+ // Check next suballocations for BufferImageGranularity conflicts.
+ // If conflict exists, allocation cannot be made here.
+ if(bufferImageGranularity > 1)
+ {
+ VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
+ ++nextSuballocItem;
+ while(nextSuballocItem != m_Suballocations.cend())
+ {
+ const VmaSuballocation& nextSuballoc = *nextSuballocItem;
+ if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
+ {
+ if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ // Already on next page.
+ break;
+ }
+ ++nextSuballocItem;
+ }
+ }
+ }
+
+ // All tests passed: Success. pOffset is already filled.
+ return true;
+}
+
+void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)
+{
+ VMA_ASSERT(item != m_Suballocations.end());
+ VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
+
+ VmaSuballocationList::iterator nextItem = item;
+ ++nextItem;
+ VMA_ASSERT(nextItem != m_Suballocations.end());
+ VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
+
+ item->size += nextItem->size;
+ --m_FreeCount;
+ m_Suballocations.erase(nextItem);
+}
+
+VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
+{
+ // Change this suballocation to be marked as free.
+ VmaSuballocation& suballoc = *suballocItem;
+ suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
+ suballoc.hAllocation = VK_NULL_HANDLE;
+
+ // Update totals.
+ ++m_FreeCount;
+ m_SumFreeSize += suballoc.size;
+
+ // Merge with previous and/or next suballocation if it's also free.
+ bool mergeWithNext = false;
+ bool mergeWithPrev = false;
+
+ VmaSuballocationList::iterator nextItem = suballocItem;
+ ++nextItem;
+ if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
+ {
+ mergeWithNext = true;
+ }
+
+ VmaSuballocationList::iterator prevItem = suballocItem;
+ if(suballocItem != m_Suballocations.begin())
+ {
+ --prevItem;
+ if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ mergeWithPrev = true;
+ }
+ }
+
+ if(mergeWithNext)
+ {
+ UnregisterFreeSuballocation(nextItem);
+ MergeFreeWithNext(suballocItem);
+ }
+
+ if(mergeWithPrev)
+ {
+ UnregisterFreeSuballocation(prevItem);
+ MergeFreeWithNext(prevItem);
+ RegisterFreeSuballocation(prevItem);
+ return prevItem;
+ }
+ else
+ {
+ RegisterFreeSuballocation(suballocItem);
+ return suballocItem;
+ }
+}
+
+void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
+{
+ VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
+ VMA_ASSERT(item->size > 0);
+
+ // You may want to enable this validation at the beginning or at the end of
+ // this function, depending on what do you want to check.
+ VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
+
+ if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
+ {
+ if(m_FreeSuballocationsBySize.empty())
+ {
+ m_FreeSuballocationsBySize.push_back(item);
+ }
+ else
+ {
+ VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);
+ }
+ }
+
+ //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
+}
+
+
+void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
+{
+ VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
+ VMA_ASSERT(item->size > 0);
+
+ // You may want to enable this validation at the beginning or at the end of
+ // this function, depending on what do you want to check.
+ VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
+
+ if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
+ {
+ VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
+ m_FreeSuballocationsBySize.data(),
+ m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
+ item,
+ VmaSuballocationItemSizeLess());
+ for(size_t index = it - m_FreeSuballocationsBySize.data();
+ index < m_FreeSuballocationsBySize.size();
+ ++index)
+ {
+ if(m_FreeSuballocationsBySize[index] == item)
+ {
+ VmaVectorRemove(m_FreeSuballocationsBySize, index);
+ return;
+ }
+ VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
+ }
+ VMA_ASSERT(0 && "Not found.");
+ }
+
+ //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());
+}
+
+bool VmaBlockMetadata_Generic::IsBufferImageGranularityConflictPossible(
+ VkDeviceSize bufferImageGranularity,
+ VmaSuballocationType& inOutPrevSuballocType) const
+{
+ if(bufferImageGranularity == 1 || IsEmpty())
+ {
+ return false;
+ }
+
+ VkDeviceSize minAlignment = VK_WHOLE_SIZE;
+ bool typeConflictFound = false;
+ for(VmaSuballocationList::const_iterator it = m_Suballocations.cbegin();
+ it != m_Suballocations.cend();
+ ++it)
+ {
+ const VmaSuballocationType suballocType = it->type;
+ if(suballocType != VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ minAlignment = VMA_MIN(minAlignment, it->hAllocation->GetAlignment());
+ if(VmaIsBufferImageGranularityConflict(inOutPrevSuballocType, suballocType))
+ {
+ typeConflictFound = true;
+ }
+ inOutPrevSuballocType = suballocType;
+ }
+ }
+
+ return typeConflictFound || minAlignment >= bufferImageGranularity;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// class VmaBlockMetadata_Linear
+
+VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(VmaAllocator hAllocator) :
+ VmaBlockMetadata(hAllocator),
+ m_SumFreeSize(0),
+ m_Suballocations0(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
+ m_Suballocations1(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
+ m_1stVectorIndex(0),
+ m_2ndVectorMode(SECOND_VECTOR_EMPTY),
+ m_1stNullItemsBeginCount(0),
+ m_1stNullItemsMiddleCount(0),
+ m_2ndNullItemsCount(0)
+{
+}
+
+VmaBlockMetadata_Linear::~VmaBlockMetadata_Linear()
+{
+}
+
+void VmaBlockMetadata_Linear::Init(VkDeviceSize size)
+{
+ VmaBlockMetadata::Init(size);
+ m_SumFreeSize = size;
+}
+
+bool VmaBlockMetadata_Linear::Validate() const
+{
+ const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
+ const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
+
+ VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));
+ VMA_VALIDATE(!suballocations1st.empty() ||
+ suballocations2nd.empty() ||
+ m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);
+
+ if(!suballocations1st.empty())
+ {
+ // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.
+ VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].hAllocation != VK_NULL_HANDLE);
+ // Null item at the end should be just pop_back().
+ VMA_VALIDATE(suballocations1st.back().hAllocation != VK_NULL_HANDLE);
+ }
+ if(!suballocations2nd.empty())
+ {
+ // Null item at the end should be just pop_back().
+ VMA_VALIDATE(suballocations2nd.back().hAllocation != VK_NULL_HANDLE);
+ }
+
+ VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());
+ VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());
+
+ VkDeviceSize sumUsedSize = 0;
+ const size_t suballoc1stCount = suballocations1st.size();
+ VkDeviceSize offset = VMA_DEBUG_MARGIN;
+
+ if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
+ {
+ const size_t suballoc2ndCount = suballocations2nd.size();
+ size_t nullItem2ndCount = 0;
+ for(size_t i = 0; i < suballoc2ndCount; ++i)
+ {
+ const VmaSuballocation& suballoc = suballocations2nd[i];
+ const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
+
+ VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
+ VMA_VALIDATE(suballoc.offset >= offset);
+
+ if(!currFree)
+ {
+ VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
+ VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
+ sumUsedSize += suballoc.size;
+ }
+ else
+ {
+ ++nullItem2ndCount;
+ }
+
+ offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
+ }
+
+ VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
+ }
+
+ for(size_t i = 0; i < m_1stNullItemsBeginCount; ++i)
+ {
+ const VmaSuballocation& suballoc = suballocations1st[i];
+ VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&
+ suballoc.hAllocation == VK_NULL_HANDLE);
+ }
+
+ size_t nullItem1stCount = m_1stNullItemsBeginCount;
+
+ for(size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)
+ {
+ const VmaSuballocation& suballoc = suballocations1st[i];
+ const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
+
+ VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
+ VMA_VALIDATE(suballoc.offset >= offset);
+ VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);
+
+ if(!currFree)
+ {
+ VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
+ VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
+ sumUsedSize += suballoc.size;
+ }
+ else
+ {
+ ++nullItem1stCount;
+ }
+
+ offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
+ }
+ VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);
+
+ if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
+ {
+ const size_t suballoc2ndCount = suballocations2nd.size();
+ size_t nullItem2ndCount = 0;
+ for(size_t i = suballoc2ndCount; i--; )
+ {
+ const VmaSuballocation& suballoc = suballocations2nd[i];
+ const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
+
+ VMA_VALIDATE(currFree == (suballoc.hAllocation == VK_NULL_HANDLE));
+ VMA_VALIDATE(suballoc.offset >= offset);
+
+ if(!currFree)
+ {
+ VMA_VALIDATE(suballoc.hAllocation->GetOffset() == suballoc.offset);
+ VMA_VALIDATE(suballoc.hAllocation->GetSize() == suballoc.size);
+ sumUsedSize += suballoc.size;
+ }
+ else
+ {
+ ++nullItem2ndCount;
+ }
+
+ offset = suballoc.offset + suballoc.size + VMA_DEBUG_MARGIN;
+ }
+
+ VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);
+ }
+
+ VMA_VALIDATE(offset <= GetSize());
+ VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);
+
+ return true;
+}
+
+size_t VmaBlockMetadata_Linear::GetAllocationCount() const
+{
+ return AccessSuballocations1st().size() - (m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount) +
+ AccessSuballocations2nd().size() - m_2ndNullItemsCount;
+}
+
+VkDeviceSize VmaBlockMetadata_Linear::GetUnusedRangeSizeMax() const
+{
+ const VkDeviceSize size = GetSize();
+
+ /*
+ We don't consider gaps inside allocation vectors with freed allocations because
+ they are not suitable for reuse in linear allocator. We consider only space that
+ is available for new allocations.
+ */
+ if(IsEmpty())
+ {
+ return size;
+ }
+
+ const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
+
+ switch(m_2ndVectorMode)
+ {
+ case SECOND_VECTOR_EMPTY:
+ /*
+ Available space is after end of 1st, as well as before beginning of 1st (which
+ whould make it a ring buffer).
+ */
+ {
+ const size_t suballocations1stCount = suballocations1st.size();
+ VMA_ASSERT(suballocations1stCount > m_1stNullItemsBeginCount);
+ const VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
+ const VmaSuballocation& lastSuballoc = suballocations1st[suballocations1stCount - 1];
+ return VMA_MAX(
+ firstSuballoc.offset,
+ size - (lastSuballoc.offset + lastSuballoc.size));
+ }
+ break;
+
+ case SECOND_VECTOR_RING_BUFFER:
+ /*
+ Available space is only between end of 2nd and beginning of 1st.
+ */
+ {
+ const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
+ const VmaSuballocation& lastSuballoc2nd = suballocations2nd.back();
+ const VmaSuballocation& firstSuballoc1st = suballocations1st[m_1stNullItemsBeginCount];
+ return firstSuballoc1st.offset - (lastSuballoc2nd.offset + lastSuballoc2nd.size);
+ }
+ break;
+
+ case SECOND_VECTOR_DOUBLE_STACK:
+ /*
+ Available space is only between end of 1st and top of 2nd.
+ */
+ {
+ const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
+ const VmaSuballocation& topSuballoc2nd = suballocations2nd.back();
+ const VmaSuballocation& lastSuballoc1st = suballocations1st.back();
+ return topSuballoc2nd.offset - (lastSuballoc1st.offset + lastSuballoc1st.size);
+ }
+ break;
+
+ default:
+ VMA_ASSERT(0);
+ return 0;
+ }
+}
+
+void VmaBlockMetadata_Linear::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
+{
+ const VkDeviceSize size = GetSize();
+ const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
+ const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
+ const size_t suballoc1stCount = suballocations1st.size();
+ const size_t suballoc2ndCount = suballocations2nd.size();
+
+ outInfo.blockCount = 1;
+ outInfo.allocationCount = (uint32_t)GetAllocationCount();
+ outInfo.unusedRangeCount = 0;
+ outInfo.usedBytes = 0;
+ outInfo.allocationSizeMin = UINT64_MAX;
+ outInfo.allocationSizeMax = 0;
+ outInfo.unusedRangeSizeMin = UINT64_MAX;
+ outInfo.unusedRangeSizeMax = 0;
+
+ VkDeviceSize lastOffset = 0;
+
+ if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
+ {
+ const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
+ size_t nextAlloc2ndIndex = 0;
+ while(lastOffset < freeSpace2ndTo1stEnd)
+ {
+ // Find next non-null allocation or move nextAllocIndex to the end.
+ while(nextAlloc2ndIndex < suballoc2ndCount &&
+ suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
+ {
+ ++nextAlloc2ndIndex;
+ }
+
+ // Found non-null allocation.
+ if(nextAlloc2ndIndex < suballoc2ndCount)
+ {
+ const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
+
+ // 1. Process free space before this allocation.
+ if(lastOffset < suballoc.offset)
+ {
+ // There is free space from lastOffset to suballoc.offset.
+ const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
+ ++outInfo.unusedRangeCount;
+ outInfo.unusedBytes += unusedRangeSize;
+ outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
+ outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
+ }
+
+ // 2. Process this allocation.
+ // There is allocation with suballoc.offset, suballoc.size.
+ outInfo.usedBytes += suballoc.size;
+ outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
+ outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
+
+ // 3. Prepare for next iteration.
+ lastOffset = suballoc.offset + suballoc.size;
+ ++nextAlloc2ndIndex;
+ }
+ // We are at the end.
+ else
+ {
+ // There is free space from lastOffset to freeSpace2ndTo1stEnd.
+ if(lastOffset < freeSpace2ndTo1stEnd)
+ {
+ const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
+ ++outInfo.unusedRangeCount;
+ outInfo.unusedBytes += unusedRangeSize;
+ outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
+ outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
+ }
+
+ // End of loop.
+ lastOffset = freeSpace2ndTo1stEnd;
+ }
+ }
+ }
+
+ size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
+ const VkDeviceSize freeSpace1stTo2ndEnd =
+ m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
+ while(lastOffset < freeSpace1stTo2ndEnd)
+ {
+ // Find next non-null allocation or move nextAllocIndex to the end.
+ while(nextAlloc1stIndex < suballoc1stCount &&
+ suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
+ {
+ ++nextAlloc1stIndex;
+ }
+
+ // Found non-null allocation.
+ if(nextAlloc1stIndex < suballoc1stCount)
+ {
+ const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
+
+ // 1. Process free space before this allocation.
+ if(lastOffset < suballoc.offset)
+ {
+ // There is free space from lastOffset to suballoc.offset.
+ const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
+ ++outInfo.unusedRangeCount;
+ outInfo.unusedBytes += unusedRangeSize;
+ outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
+ outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
+ }
+
+ // 2. Process this allocation.
+ // There is allocation with suballoc.offset, suballoc.size.
+ outInfo.usedBytes += suballoc.size;
+ outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
+ outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
+
+ // 3. Prepare for next iteration.
+ lastOffset = suballoc.offset + suballoc.size;
+ ++nextAlloc1stIndex;
+ }
+ // We are at the end.
+ else
+ {
+ // There is free space from lastOffset to freeSpace1stTo2ndEnd.
+ if(lastOffset < freeSpace1stTo2ndEnd)
+ {
+ const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
+ ++outInfo.unusedRangeCount;
+ outInfo.unusedBytes += unusedRangeSize;
+ outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
+ outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
+ }
+
+ // End of loop.
+ lastOffset = freeSpace1stTo2ndEnd;
+ }
+ }
+
+ if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
+ {
+ size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
+ while(lastOffset < size)
+ {
+ // Find next non-null allocation or move nextAllocIndex to the end.
+ while(nextAlloc2ndIndex != SIZE_MAX &&
+ suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
+ {
+ --nextAlloc2ndIndex;
+ }
+
+ // Found non-null allocation.
+ if(nextAlloc2ndIndex != SIZE_MAX)
+ {
+ const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
+
+ // 1. Process free space before this allocation.
+ if(lastOffset < suballoc.offset)
+ {
+ // There is free space from lastOffset to suballoc.offset.
+ const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
+ ++outInfo.unusedRangeCount;
+ outInfo.unusedBytes += unusedRangeSize;
+ outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
+ outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
+ }
+
+ // 2. Process this allocation.
+ // There is allocation with suballoc.offset, suballoc.size.
+ outInfo.usedBytes += suballoc.size;
+ outInfo.allocationSizeMin = VMA_MIN(outInfo.allocationSizeMin, suballoc.size);
+ outInfo.allocationSizeMax = VMA_MIN(outInfo.allocationSizeMax, suballoc.size);
+
+ // 3. Prepare for next iteration.
+ lastOffset = suballoc.offset + suballoc.size;
+ --nextAlloc2ndIndex;
+ }
+ // We are at the end.
+ else
+ {
+ // There is free space from lastOffset to size.
+ if(lastOffset < size)
+ {
+ const VkDeviceSize unusedRangeSize = size - lastOffset;
+ ++outInfo.unusedRangeCount;
+ outInfo.unusedBytes += unusedRangeSize;
+ outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusedRangeSize);
+ outInfo.unusedRangeSizeMax = VMA_MIN(outInfo.unusedRangeSizeMax, unusedRangeSize);
+ }
+
+ // End of loop.
+ lastOffset = size;
+ }
+ }
+ }
+
+ outInfo.unusedBytes = size - outInfo.usedBytes;
+}
+
+void VmaBlockMetadata_Linear::AddPoolStats(VmaPoolStats& inoutStats) const
+{
+ const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
+ const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
+ const VkDeviceSize size = GetSize();
+ const size_t suballoc1stCount = suballocations1st.size();
+ const size_t suballoc2ndCount = suballocations2nd.size();
+
+ inoutStats.size += size;
+
+ VkDeviceSize lastOffset = 0;
+
+ if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
+ {
+ const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
+ size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;
+ while(lastOffset < freeSpace2ndTo1stEnd)
+ {
+ // Find next non-null allocation or move nextAlloc2ndIndex to the end.
+ while(nextAlloc2ndIndex < suballoc2ndCount &&
+ suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
+ {
+ ++nextAlloc2ndIndex;
+ }
+
+ // Found non-null allocation.
+ if(nextAlloc2ndIndex < suballoc2ndCount)
+ {
+ const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
+
+ // 1. Process free space before this allocation.
+ if(lastOffset < suballoc.offset)
+ {
+ // There is free space from lastOffset to suballoc.offset.
+ const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
+ inoutStats.unusedSize += unusedRangeSize;
+ ++inoutStats.unusedRangeCount;
+ inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
+ }
+
+ // 2. Process this allocation.
+ // There is allocation with suballoc.offset, suballoc.size.
+ ++inoutStats.allocationCount;
+
+ // 3. Prepare for next iteration.
+ lastOffset = suballoc.offset + suballoc.size;
+ ++nextAlloc2ndIndex;
+ }
+ // We are at the end.
+ else
+ {
+ if(lastOffset < freeSpace2ndTo1stEnd)
+ {
+ // There is free space from lastOffset to freeSpace2ndTo1stEnd.
+ const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
+ inoutStats.unusedSize += unusedRangeSize;
+ ++inoutStats.unusedRangeCount;
+ inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
+ }
+
+ // End of loop.
+ lastOffset = freeSpace2ndTo1stEnd;
+ }
+ }
+ }
+
+ size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
+ const VkDeviceSize freeSpace1stTo2ndEnd =
+ m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
+ while(lastOffset < freeSpace1stTo2ndEnd)
+ {
+ // Find next non-null allocation or move nextAllocIndex to the end.
+ while(nextAlloc1stIndex < suballoc1stCount &&
+ suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
+ {
+ ++nextAlloc1stIndex;
+ }
+
+ // Found non-null allocation.
+ if(nextAlloc1stIndex < suballoc1stCount)
+ {
+ const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
+
+ // 1. Process free space before this allocation.
+ if(lastOffset < suballoc.offset)
+ {
+ // There is free space from lastOffset to suballoc.offset.
+ const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
+ inoutStats.unusedSize += unusedRangeSize;
+ ++inoutStats.unusedRangeCount;
+ inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
+ }
+
+ // 2. Process this allocation.
+ // There is allocation with suballoc.offset, suballoc.size.
+ ++inoutStats.allocationCount;
+
+ // 3. Prepare for next iteration.
+ lastOffset = suballoc.offset + suballoc.size;
+ ++nextAlloc1stIndex;
+ }
+ // We are at the end.
+ else
+ {
+ if(lastOffset < freeSpace1stTo2ndEnd)
+ {
+ // There is free space from lastOffset to freeSpace1stTo2ndEnd.
+ const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
+ inoutStats.unusedSize += unusedRangeSize;
+ ++inoutStats.unusedRangeCount;
+ inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
+ }
+
+ // End of loop.
+ lastOffset = freeSpace1stTo2ndEnd;
+ }
+ }
+
+ if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
+ {
+ size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
+ while(lastOffset < size)
+ {
+ // Find next non-null allocation or move nextAlloc2ndIndex to the end.
+ while(nextAlloc2ndIndex != SIZE_MAX &&
+ suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
+ {
+ --nextAlloc2ndIndex;
+ }
+
+ // Found non-null allocation.
+ if(nextAlloc2ndIndex != SIZE_MAX)
+ {
+ const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
+
+ // 1. Process free space before this allocation.
+ if(lastOffset < suballoc.offset)
+ {
+ // There is free space from lastOffset to suballoc.offset.
+ const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
+ inoutStats.unusedSize += unusedRangeSize;
+ ++inoutStats.unusedRangeCount;
+ inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
+ }
+
+ // 2. Process this allocation.
+ // There is allocation with suballoc.offset, suballoc.size.
+ ++inoutStats.allocationCount;
+
+ // 3. Prepare for next iteration.
+ lastOffset = suballoc.offset + suballoc.size;
+ --nextAlloc2ndIndex;
+ }
+ // We are at the end.
+ else
+ {
+ if(lastOffset < size)
+ {
+ // There is free space from lastOffset to size.
+ const VkDeviceSize unusedRangeSize = size - lastOffset;
+ inoutStats.unusedSize += unusedRangeSize;
+ ++inoutStats.unusedRangeCount;
+ inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, unusedRangeSize);
+ }
+
+ // End of loop.
+ lastOffset = size;
+ }
+ }
+ }
+}
+
+#if VMA_STATS_STRING_ENABLED
+void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const
+{
+ const VkDeviceSize size = GetSize();
+ const SuballocationVectorType& suballocations1st = AccessSuballocations1st();
+ const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
+ const size_t suballoc1stCount = suballocations1st.size();
+ const size_t suballoc2ndCount = suballocations2nd.size();
+
+ // FIRST PASS
+
+ size_t unusedRangeCount = 0;
+ VkDeviceSize usedBytes = 0;
+
+ VkDeviceSize lastOffset = 0;
+
+ size_t alloc2ndCount = 0;
+ if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
+ {
+ const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
+ size_t nextAlloc2ndIndex = 0;
+ while(lastOffset < freeSpace2ndTo1stEnd)
+ {
+ // Find next non-null allocation or move nextAlloc2ndIndex to the end.
+ while(nextAlloc2ndIndex < suballoc2ndCount &&
+ suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
+ {
+ ++nextAlloc2ndIndex;
+ }
+
+ // Found non-null allocation.
+ if(nextAlloc2ndIndex < suballoc2ndCount)
+ {
+ const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
+
+ // 1. Process free space before this allocation.
+ if(lastOffset < suballoc.offset)
+ {
+ // There is free space from lastOffset to suballoc.offset.
+ ++unusedRangeCount;
+ }
+
+ // 2. Process this allocation.
+ // There is allocation with suballoc.offset, suballoc.size.
+ ++alloc2ndCount;
+ usedBytes += suballoc.size;
+
+ // 3. Prepare for next iteration.
+ lastOffset = suballoc.offset + suballoc.size;
+ ++nextAlloc2ndIndex;
+ }
+ // We are at the end.
+ else
+ {
+ if(lastOffset < freeSpace2ndTo1stEnd)
+ {
+ // There is free space from lastOffset to freeSpace2ndTo1stEnd.
+ ++unusedRangeCount;
+ }
+
+ // End of loop.
+ lastOffset = freeSpace2ndTo1stEnd;
+ }
+ }
+ }
+
+ size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;
+ size_t alloc1stCount = 0;
+ const VkDeviceSize freeSpace1stTo2ndEnd =
+ m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;
+ while(lastOffset < freeSpace1stTo2ndEnd)
+ {
+ // Find next non-null allocation or move nextAllocIndex to the end.
+ while(nextAlloc1stIndex < suballoc1stCount &&
+ suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
+ {
+ ++nextAlloc1stIndex;
+ }
+
+ // Found non-null allocation.
+ if(nextAlloc1stIndex < suballoc1stCount)
+ {
+ const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
+
+ // 1. Process free space before this allocation.
+ if(lastOffset < suballoc.offset)
+ {
+ // There is free space from lastOffset to suballoc.offset.
+ ++unusedRangeCount;
+ }
+
+ // 2. Process this allocation.
+ // There is allocation with suballoc.offset, suballoc.size.
+ ++alloc1stCount;
+ usedBytes += suballoc.size;
+
+ // 3. Prepare for next iteration.
+ lastOffset = suballoc.offset + suballoc.size;
+ ++nextAlloc1stIndex;
+ }
+ // We are at the end.
+ else
+ {
+ if(lastOffset < size)
+ {
+ // There is free space from lastOffset to freeSpace1stTo2ndEnd.
+ ++unusedRangeCount;
+ }
+
+ // End of loop.
+ lastOffset = freeSpace1stTo2ndEnd;
+ }
+ }
+
+ if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
+ {
+ size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
+ while(lastOffset < size)
+ {
+ // Find next non-null allocation or move nextAlloc2ndIndex to the end.
+ while(nextAlloc2ndIndex != SIZE_MAX &&
+ suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
+ {
+ --nextAlloc2ndIndex;
+ }
+
+ // Found non-null allocation.
+ if(nextAlloc2ndIndex != SIZE_MAX)
+ {
+ const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
+
+ // 1. Process free space before this allocation.
+ if(lastOffset < suballoc.offset)
+ {
+ // There is free space from lastOffset to suballoc.offset.
+ ++unusedRangeCount;
+ }
+
+ // 2. Process this allocation.
+ // There is allocation with suballoc.offset, suballoc.size.
+ ++alloc2ndCount;
+ usedBytes += suballoc.size;
+
+ // 3. Prepare for next iteration.
+ lastOffset = suballoc.offset + suballoc.size;
+ --nextAlloc2ndIndex;
+ }
+ // We are at the end.
+ else
+ {
+ if(lastOffset < size)
+ {
+ // There is free space from lastOffset to size.
+ ++unusedRangeCount;
+ }
+
+ // End of loop.
+ lastOffset = size;
+ }
+ }
+ }
+
+ const VkDeviceSize unusedBytes = size - usedBytes;
+ PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);
+
+ // SECOND PASS
+ lastOffset = 0;
+
+ if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
+ {
+ const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;
+ size_t nextAlloc2ndIndex = 0;
+ while(lastOffset < freeSpace2ndTo1stEnd)
+ {
+ // Find next non-null allocation or move nextAlloc2ndIndex to the end.
+ while(nextAlloc2ndIndex < suballoc2ndCount &&
+ suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
+ {
+ ++nextAlloc2ndIndex;
+ }
+
+ // Found non-null allocation.
+ if(nextAlloc2ndIndex < suballoc2ndCount)
+ {
+ const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
+
+ // 1. Process free space before this allocation.
+ if(lastOffset < suballoc.offset)
+ {
+ // There is free space from lastOffset to suballoc.offset.
+ const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
+ PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
+ }
+
+ // 2. Process this allocation.
+ // There is allocation with suballoc.offset, suballoc.size.
+ PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
+
+ // 3. Prepare for next iteration.
+ lastOffset = suballoc.offset + suballoc.size;
+ ++nextAlloc2ndIndex;
+ }
+ // We are at the end.
+ else
+ {
+ if(lastOffset < freeSpace2ndTo1stEnd)
+ {
+ // There is free space from lastOffset to freeSpace2ndTo1stEnd.
+ const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;
+ PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
+ }
+
+ // End of loop.
+ lastOffset = freeSpace2ndTo1stEnd;
+ }
+ }
+ }
+
+ nextAlloc1stIndex = m_1stNullItemsBeginCount;
+ while(lastOffset < freeSpace1stTo2ndEnd)
+ {
+ // Find next non-null allocation or move nextAllocIndex to the end.
+ while(nextAlloc1stIndex < suballoc1stCount &&
+ suballocations1st[nextAlloc1stIndex].hAllocation == VK_NULL_HANDLE)
+ {
+ ++nextAlloc1stIndex;
+ }
+
+ // Found non-null allocation.
+ if(nextAlloc1stIndex < suballoc1stCount)
+ {
+ const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];
+
+ // 1. Process free space before this allocation.
+ if(lastOffset < suballoc.offset)
+ {
+ // There is free space from lastOffset to suballoc.offset.
+ const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
+ PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
+ }
+
+ // 2. Process this allocation.
+ // There is allocation with suballoc.offset, suballoc.size.
+ PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
+
+ // 3. Prepare for next iteration.
+ lastOffset = suballoc.offset + suballoc.size;
+ ++nextAlloc1stIndex;
+ }
+ // We are at the end.
+ else
+ {
+ if(lastOffset < freeSpace1stTo2ndEnd)
+ {
+ // There is free space from lastOffset to freeSpace1stTo2ndEnd.
+ const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;
+ PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
+ }
+
+ // End of loop.
+ lastOffset = freeSpace1stTo2ndEnd;
+ }
+ }
+
+ if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
+ {
+ size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;
+ while(lastOffset < size)
+ {
+ // Find next non-null allocation or move nextAlloc2ndIndex to the end.
+ while(nextAlloc2ndIndex != SIZE_MAX &&
+ suballocations2nd[nextAlloc2ndIndex].hAllocation == VK_NULL_HANDLE)
+ {
+ --nextAlloc2ndIndex;
+ }
+
+ // Found non-null allocation.
+ if(nextAlloc2ndIndex != SIZE_MAX)
+ {
+ const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];
+
+ // 1. Process free space before this allocation.
+ if(lastOffset < suballoc.offset)
+ {
+ // There is free space from lastOffset to suballoc.offset.
+ const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;
+ PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
+ }
+
+ // 2. Process this allocation.
+ // There is allocation with suballoc.offset, suballoc.size.
+ PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.hAllocation);
+
+ // 3. Prepare for next iteration.
+ lastOffset = suballoc.offset + suballoc.size;
+ --nextAlloc2ndIndex;
+ }
+ // We are at the end.
+ else
+ {
+ if(lastOffset < size)
+ {
+ // There is free space from lastOffset to size.
+ const VkDeviceSize unusedRangeSize = size - lastOffset;
+ PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);
+ }
+
+ // End of loop.
+ lastOffset = size;
+ }
+ }
+ }
+
+ PrintDetailedMap_End(json);
+}
+#endif // #if VMA_STATS_STRING_ENABLED
+
+bool VmaBlockMetadata_Linear::CreateAllocationRequest(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VkDeviceSize bufferImageGranularity,
+ VkDeviceSize allocSize,
+ VkDeviceSize allocAlignment,
+ bool upperAddress,
+ VmaSuballocationType allocType,
+ bool canMakeOtherLost,
+ uint32_t /*strategy*/,
+ VmaAllocationRequest* pAllocationRequest)
+{
+ VMA_ASSERT(allocSize > 0);
+ VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
+ VMA_ASSERT(pAllocationRequest != VMA_NULL);
+ VMA_HEAVY_ASSERT(Validate());
+
+ const VkDeviceSize size = GetSize();
+ SuballocationVectorType& suballocations1st = AccessSuballocations1st();
+ SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
+
+ if(upperAddress)
+ {
+ if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
+ {
+ VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");
+ return false;
+ }
+
+ // Try to allocate before 2nd.back(), or end of block if 2nd.empty().
+ if(allocSize > size)
+ {
+ return false;
+ }
+ VkDeviceSize resultBaseOffset = size - allocSize;
+ if(!suballocations2nd.empty())
+ {
+ const VmaSuballocation& lastSuballoc = suballocations2nd.back();
+ resultBaseOffset = lastSuballoc.offset - allocSize;
+ if(allocSize > lastSuballoc.offset)
+ {
+ return false;
+ }
+ }
+
+ // Start from offset equal to end of free space.
+ VkDeviceSize resultOffset = resultBaseOffset;
+
+ // Apply VMA_DEBUG_MARGIN at the end.
+ if(VMA_DEBUG_MARGIN > 0)
+ {
+#if VMA_DEBUG_MARGIN
+ if(resultOffset < VMA_DEBUG_MARGIN)
+ {
+ return false;
+ }
+#endif
+ resultOffset -= VMA_DEBUG_MARGIN;
+ }
+
+ // Apply alignment.
+ resultOffset = VmaAlignDown(resultOffset, allocAlignment);
+
+ // Check next suballocations from 2nd for BufferImageGranularity conflicts.
+ // Make bigger alignment if necessary.
+ if(bufferImageGranularity > 1 && !suballocations2nd.empty())
+ {
+ bool bufferImageGranularityConflict = false;
+ for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
+ {
+ const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
+ if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
+ {
+ if(VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))
+ {
+ bufferImageGranularityConflict = true;
+ break;
+ }
+ }
+ else
+ // Already on previous page.
+ break;
+ }
+ if(bufferImageGranularityConflict)
+ {
+ resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);
+ }
+ }
+
+ // There is enough free space.
+ const VkDeviceSize endOf1st = !suballocations1st.empty() ?
+ suballocations1st.back().offset + suballocations1st.back().size :
+ 0;
+ if(endOf1st + VMA_DEBUG_MARGIN <= resultOffset)
+ {
+ // Check previous suballocations for BufferImageGranularity conflicts.
+ // If conflict exists, allocation cannot be made here.
+ if(bufferImageGranularity > 1)
+ {
+ for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
+ {
+ const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
+ if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
+ {
+ if(VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ // Already on next page.
+ break;
+ }
+ }
+ }
+
+ // All tests passed: Success.
+ pAllocationRequest->offset = resultOffset;
+ pAllocationRequest->sumFreeSize = resultBaseOffset + allocSize - endOf1st;
+ pAllocationRequest->sumItemSize = 0;
+ // pAllocationRequest->item unused.
+ pAllocationRequest->itemsToMakeLostCount = 0;
+ return true;
+ }
+ }
+ else // !upperAddress
+ {
+ if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
+ {
+ // Try to allocate at the end of 1st vector.
+
+ VkDeviceSize resultBaseOffset = 0;
+ if(!suballocations1st.empty())
+ {
+ const VmaSuballocation& lastSuballoc = suballocations1st.back();
+ resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
+ }
+
+ // Start from offset equal to beginning of free space.
+ VkDeviceSize resultOffset = resultBaseOffset;
+
+ // Apply VMA_DEBUG_MARGIN at the beginning.
+ if(VMA_DEBUG_MARGIN > 0)
+ {
+ resultOffset += VMA_DEBUG_MARGIN;
+ }
+
+ // Apply alignment.
+ resultOffset = VmaAlignUp(resultOffset, allocAlignment);
+
+ // Check previous suballocations for BufferImageGranularity conflicts.
+ // Make bigger alignment if necessary.
+ if(bufferImageGranularity > 1 && !suballocations1st.empty())
+ {
+ bool bufferImageGranularityConflict = false;
+ for(size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )
+ {
+ const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];
+ if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
+ {
+ if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
+ {
+ bufferImageGranularityConflict = true;
+ break;
+ }
+ }
+ else
+ // Already on previous page.
+ break;
+ }
+ if(bufferImageGranularityConflict)
+ {
+ resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
+ }
+ }
+
+ const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?
+ suballocations2nd.back().offset : size;
+
+ // There is enough free space at the end after alignment.
+ if(resultOffset + allocSize + VMA_DEBUG_MARGIN <= freeSpaceEnd)
+ {
+ // Check next suballocations for BufferImageGranularity conflicts.
+ // If conflict exists, allocation cannot be made here.
+ if(bufferImageGranularity > 1 && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
+ {
+ for(size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )
+ {
+ const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];
+ if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
+ {
+ if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ // Already on previous page.
+ break;
+ }
+ }
+ }
+
+ // All tests passed: Success.
+ pAllocationRequest->offset = resultOffset;
+ pAllocationRequest->sumFreeSize = freeSpaceEnd - resultBaseOffset;
+ pAllocationRequest->sumItemSize = 0;
+ // pAllocationRequest->item unused.
+ pAllocationRequest->itemsToMakeLostCount = 0;
+ return true;
+ }
+ }
+
+ // Wrap-around to end of 2nd vector. Try to allocate there, watching for the
+ // beginning of 1st vector as the end of free space.
+ if(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
+ {
+ VMA_ASSERT(!suballocations1st.empty());
+
+ VkDeviceSize resultBaseOffset = 0;
+ if(!suballocations2nd.empty())
+ {
+ const VmaSuballocation& lastSuballoc = suballocations2nd.back();
+ resultBaseOffset = lastSuballoc.offset + lastSuballoc.size;
+ }
+
+ // Start from offset equal to beginning of free space.
+ VkDeviceSize resultOffset = resultBaseOffset;
+
+ // Apply VMA_DEBUG_MARGIN at the beginning.
+ if(VMA_DEBUG_MARGIN > 0)
+ {
+ resultOffset += VMA_DEBUG_MARGIN;
+ }
+
+ // Apply alignment.
+ resultOffset = VmaAlignUp(resultOffset, allocAlignment);
+
+ // Check previous suballocations for BufferImageGranularity conflicts.
+ // Make bigger alignment if necessary.
+ if(bufferImageGranularity > 1 && !suballocations2nd.empty())
+ {
+ bool bufferImageGranularityConflict = false;
+ for(size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )
+ {
+ const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];
+ if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))
+ {
+ if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
+ {
+ bufferImageGranularityConflict = true;
+ break;
+ }
+ }
+ else
+ // Already on previous page.
+ break;
+ }
+ if(bufferImageGranularityConflict)
+ {
+ resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);
+ }
+ }
+
+ pAllocationRequest->itemsToMakeLostCount = 0;
+ pAllocationRequest->sumItemSize = 0;
+ size_t index1st = m_1stNullItemsBeginCount;
+
+ if(canMakeOtherLost)
+ {
+ while(index1st < suballocations1st.size() &&
+ resultOffset + allocSize + VMA_DEBUG_MARGIN > suballocations1st[index1st].offset)
+ {
+ // Next colliding allocation at the beginning of 1st vector found. Try to make it lost.
+ const VmaSuballocation& suballoc = suballocations1st[index1st];
+ if(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ // No problem.
+ }
+ else
+ {
+ VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
+ if(suballoc.hAllocation->CanBecomeLost() &&
+ suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
+ {
+ ++pAllocationRequest->itemsToMakeLostCount;
+ pAllocationRequest->sumItemSize += suballoc.size;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ ++index1st;
+ }
+
+ // Check next suballocations for BufferImageGranularity conflicts.
+ // If conflict exists, we must mark more allocations lost or fail.
+ if(bufferImageGranularity > 1)
+ {
+ while(index1st < suballocations1st.size())
+ {
+ const VmaSuballocation& suballoc = suballocations1st[index1st];
+ if(VmaBlocksOnSamePage(resultOffset, allocSize, suballoc.offset, bufferImageGranularity))
+ {
+ if(suballoc.hAllocation != VK_NULL_HANDLE)
+ {
+ // Not checking actual VmaIsBufferImageGranularityConflict(allocType, suballoc.type).
+ if(suballoc.hAllocation->CanBecomeLost() &&
+ suballoc.hAllocation->GetLastUseFrameIndex() + frameInUseCount < currentFrameIndex)
+ {
+ ++pAllocationRequest->itemsToMakeLostCount;
+ pAllocationRequest->sumItemSize += suballoc.size;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ // Already on next page.
+ break;
+ }
+ ++index1st;
+ }
+ }
+ }
+
+ // There is enough free space at the end after alignment.
+ if((index1st == suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN < size) ||
+ (index1st < suballocations1st.size() && resultOffset + allocSize + VMA_DEBUG_MARGIN <= suballocations1st[index1st].offset))
+ {
+ // Check next suballocations for BufferImageGranularity conflicts.
+ // If conflict exists, allocation cannot be made here.
+ if(bufferImageGranularity > 1)
+ {
+ for(size_t nextSuballocIndex = index1st;
+ nextSuballocIndex < suballocations1st.size();
+ nextSuballocIndex++)
+ {
+ const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];
+ if(VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
+ {
+ if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ // Already on next page.
+ break;
+ }
+ }
+ }
+
+ // All tests passed: Success.
+ pAllocationRequest->offset = resultOffset;
+ pAllocationRequest->sumFreeSize =
+ (index1st < suballocations1st.size() ? suballocations1st[index1st].offset : size)
+ - resultBaseOffset
+ - pAllocationRequest->sumItemSize;
+ // pAllocationRequest->item unused.
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool VmaBlockMetadata_Linear::MakeRequestedAllocationsLost(
+ uint32_t currentFrameIndex,
+ uint32_t frameInUseCount,
+ VmaAllocationRequest* pAllocationRequest)
+{
+ if(pAllocationRequest->itemsToMakeLostCount == 0)
+ {
+ return true;
+ }
+
+ VMA_ASSERT(m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER);
+
+ SuballocationVectorType& suballocations1st = AccessSuballocations1st();
+ size_t index1st = m_1stNullItemsBeginCount;
+ size_t madeLostCount = 0;
+ while(madeLostCount < pAllocationRequest->itemsToMakeLostCount)
+ {
+ VMA_ASSERT(index1st < suballocations1st.size());
+ VmaSuballocation& suballoc = suballocations1st[index1st];
+ if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ VMA_ASSERT(suballoc.hAllocation != VK_NULL_HANDLE);
+ VMA_ASSERT(suballoc.hAllocation->CanBecomeLost());
+ if(suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
+ {
+ suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
+ suballoc.hAllocation = VK_NULL_HANDLE;
+ m_SumFreeSize += suballoc.size;
+ ++m_1stNullItemsMiddleCount;
+ ++madeLostCount;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ ++index1st;
+ }
+
+ CleanupAfterFree();
+ //VMA_HEAVY_ASSERT(Validate()); // Already called by ClanupAfterFree().
+
+ return true;
+}
+
+uint32_t VmaBlockMetadata_Linear::MakeAllocationsLost(uint32_t currentFrameIndex, uint32_t frameInUseCount)
+{
+ uint32_t lostAllocationCount = 0;
+
+ SuballocationVectorType& suballocations1st = AccessSuballocations1st();
+ for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
+ {
+ VmaSuballocation& suballoc = suballocations1st[i];
+ if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
+ suballoc.hAllocation->CanBecomeLost() &&
+ suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
+ {
+ suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
+ suballoc.hAllocation = VK_NULL_HANDLE;
+ ++m_1stNullItemsMiddleCount;
+ m_SumFreeSize += suballoc.size;
+ ++lostAllocationCount;
+ }
+ }
+
+ SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
+ for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
+ {
+ VmaSuballocation& suballoc = suballocations2nd[i];
+ if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE &&
+ suballoc.hAllocation->CanBecomeLost() &&
+ suballoc.hAllocation->MakeLost(currentFrameIndex, frameInUseCount))
+ {
+ suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
+ suballoc.hAllocation = VK_NULL_HANDLE;
+ ++m_2ndNullItemsCount;
+ ++lostAllocationCount;
+ }
+ }
+
+ if(lostAllocationCount)
+ {
+ CleanupAfterFree();
+ }
+
+ return lostAllocationCount;
+}
+
+VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)
+{
+ SuballocationVectorType& suballocations1st = AccessSuballocations1st();
+ for(size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)
+ {
+ const VmaSuballocation& suballoc = suballocations1st[i];
+ if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
+ {
+ VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
+ return VK_ERROR_VALIDATION_FAILED_EXT;
+ }
+ if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
+ {
+ VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
+ return VK_ERROR_VALIDATION_FAILED_EXT;
+ }
+ }
+ }
+
+ SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
+ for(size_t i = 0, count = suballocations2nd.size(); i < count; ++i)
+ {
+ const VmaSuballocation& suballoc = suballocations2nd[i];
+ if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ if(!VmaValidateMagicValue(pBlockData, suballoc.offset - VMA_DEBUG_MARGIN))
+ {
+ VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE VALIDATED ALLOCATION!");
+ return VK_ERROR_VALIDATION_FAILED_EXT;
+ }
+ if(!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))
+ {
+ VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");
+ return VK_ERROR_VALIDATION_FAILED_EXT;
+ }
+ }
+ }
+
+ return VK_SUCCESS;
+}
+
+void VmaBlockMetadata_Linear::Alloc(
+ const VmaAllocationRequest& request,
+ VmaSuballocationType type,
+ VkDeviceSize allocSize,
+ bool upperAddress,
+ VmaAllocation hAllocation)
+{
+ const VmaSuballocation newSuballoc = { request.offset, allocSize, hAllocation, type };
+
+ if(upperAddress)
+ {
+ VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&
+ "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");
+ SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
+ suballocations2nd.push_back(newSuballoc);
+ m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;
+ }
+ else
+ {
+ SuballocationVectorType& suballocations1st = AccessSuballocations1st();
+
+ // First allocation.
+ if(suballocations1st.empty())
+ {
+ suballocations1st.push_back(newSuballoc);
+ }
+ else
+ {
+ // New allocation at the end of 1st vector.
+ if(request.offset >= suballocations1st.back().offset + suballocations1st.back().size)
+ {
+ // Check if it fits before the end of the block.
+ VMA_ASSERT(request.offset + allocSize <= GetSize());
+ suballocations1st.push_back(newSuballoc);
+ }
+ // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.
+ else if(request.offset + allocSize <= suballocations1st[m_1stNullItemsBeginCount].offset)
+ {
+ SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
+
+ switch(m_2ndVectorMode)
+ {
+ case SECOND_VECTOR_EMPTY:
+ // First allocation from second part ring buffer.
+ VMA_ASSERT(suballocations2nd.empty());
+ m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;
+ break;
+ case SECOND_VECTOR_RING_BUFFER:
+ // 2-part ring buffer is already started.
+ VMA_ASSERT(!suballocations2nd.empty());
+ break;
+ case SECOND_VECTOR_DOUBLE_STACK:
+ VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");
+ break;
+ default:
+ VMA_ASSERT(0);
+ }
+
+ suballocations2nd.push_back(newSuballoc);
+ }
+ else
+ {
+ VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");
+ }
+ }
+ }
+
+ m_SumFreeSize -= newSuballoc.size;
+}
+
+void VmaBlockMetadata_Linear::Free(const VmaAllocation allocation)
+{
+ FreeAtOffset(allocation->GetOffset());
+}
+
+void VmaBlockMetadata_Linear::FreeAtOffset(VkDeviceSize offset)
+{
+ SuballocationVectorType& suballocations1st = AccessSuballocations1st();
+ SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
+
+ if(!suballocations1st.empty())
+ {
+ // First allocation: Mark it as next empty at the beginning.
+ VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];
+ if(firstSuballoc.offset == offset)
+ {
+ firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
+ firstSuballoc.hAllocation = VK_NULL_HANDLE;
+ m_SumFreeSize += firstSuballoc.size;
+ ++m_1stNullItemsBeginCount;
+ CleanupAfterFree();
+ return;
+ }
+ }
+
+ // Last allocation in 2-part ring buffer or top of upper stack (same logic).
+ if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||
+ m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)
+ {
+ VmaSuballocation& lastSuballoc = suballocations2nd.back();
+ if(lastSuballoc.offset == offset)
+ {
+ m_SumFreeSize += lastSuballoc.size;
+ suballocations2nd.pop_back();
+ CleanupAfterFree();
+ return;
+ }
+ }
+ // Last allocation in 1st vector.
+ else if(m_2ndVectorMode == SECOND_VECTOR_EMPTY)
+ {
+ VmaSuballocation& lastSuballoc = suballocations1st.back();
+ if(lastSuballoc.offset == offset)
+ {
+ m_SumFreeSize += lastSuballoc.size;
+ suballocations1st.pop_back();
+ CleanupAfterFree();
+ return;
+ }
+ }
+
+ // Item from the middle of 1st vector.
+ {
+ VmaSuballocation refSuballoc;
+ refSuballoc.offset = offset;
+ // Rest of members stays uninitialized intentionally for better performance.
+ SuballocationVectorType::iterator it = VmaVectorFindSorted<VmaSuballocationOffsetLess>(
+ suballocations1st.begin() + m_1stNullItemsBeginCount,
+ suballocations1st.end(),
+ refSuballoc);
+ if(it != suballocations1st.end())
+ {
+ it->type = VMA_SUBALLOCATION_TYPE_FREE;
+ it->hAllocation = VK_NULL_HANDLE;
+ ++m_1stNullItemsMiddleCount;
+ m_SumFreeSize += it->size;
+ CleanupAfterFree();
+ return;
+ }
+ }
+
+ if(m_2ndVectorMode != SECOND_VECTOR_EMPTY)
+ {
+ // Item from the middle of 2nd vector.
+ VmaSuballocation refSuballoc;
+ refSuballoc.offset = offset;
+ // Rest of members stays uninitialized intentionally for better performance.
+ SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?
+ VmaVectorFindSorted<VmaSuballocationOffsetLess>(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc) :
+ VmaVectorFindSorted<VmaSuballocationOffsetGreater>(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc);
+ if(it != suballocations2nd.end())
+ {
+ it->type = VMA_SUBALLOCATION_TYPE_FREE;
+ it->hAllocation = VK_NULL_HANDLE;
+ ++m_2ndNullItemsCount;
+ m_SumFreeSize += it->size;
+ CleanupAfterFree();
+ return;
+ }
+ }
+
+ VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");
+}
+
+bool VmaBlockMetadata_Linear::ShouldCompact1st() const
+{
+ const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
+ const size_t suballocCount = AccessSuballocations1st().size();
+ return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;
+}
+
+void VmaBlockMetadata_Linear::CleanupAfterFree()
+{
+ SuballocationVectorType& suballocations1st = AccessSuballocations1st();
+ SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();
+
+ if(IsEmpty())
+ {
+ suballocations1st.clear();
+ suballocations2nd.clear();
+ m_1stNullItemsBeginCount = 0;
+ m_1stNullItemsMiddleCount = 0;
+ m_2ndNullItemsCount = 0;
+ m_2ndVectorMode = SECOND_VECTOR_EMPTY;
+ }
+ else
+ {
+ const size_t suballoc1stCount = suballocations1st.size();
+ const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;
+ VMA_ASSERT(nullItem1stCount <= suballoc1stCount);
+
+ // Find more null items at the beginning of 1st vector.
+ while(m_1stNullItemsBeginCount < suballoc1stCount &&
+ suballocations1st[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
+ {
+ ++m_1stNullItemsBeginCount;
+ --m_1stNullItemsMiddleCount;
+ }
+
+ // Find more null items at the end of 1st vector.
+ while(m_1stNullItemsMiddleCount > 0 &&
+ suballocations1st.back().hAllocation == VK_NULL_HANDLE)
+ {
+ --m_1stNullItemsMiddleCount;
+ suballocations1st.pop_back();
+ }
+
+ // Find more null items at the end of 2nd vector.
+ while(m_2ndNullItemsCount > 0 &&
+ suballocations2nd.back().hAllocation == VK_NULL_HANDLE)
+ {
+ --m_2ndNullItemsCount;
+ suballocations2nd.pop_back();
+ }
+
+ if(ShouldCompact1st())
+ {
+ const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;
+ size_t srcIndex = m_1stNullItemsBeginCount;
+ for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)
+ {
+ while(suballocations1st[srcIndex].hAllocation == VK_NULL_HANDLE)
+ {
+ ++srcIndex;
+ }
+ if(dstIndex != srcIndex)
+ {
+ suballocations1st[dstIndex] = suballocations1st[srcIndex];
+ }
+ ++srcIndex;
+ }
+ suballocations1st.resize(nonNullItemCount);
+ m_1stNullItemsBeginCount = 0;
+ m_1stNullItemsMiddleCount = 0;
+ }
+
+ // 2nd vector became empty.
+ if(suballocations2nd.empty())
+ {
+ m_2ndVectorMode = SECOND_VECTOR_EMPTY;
+ }
+
+ // 1st vector became empty.
+ if(suballocations1st.size() - m_1stNullItemsBeginCount == 0)
+ {
+ suballocations1st.clear();
+ m_1stNullItemsBeginCount = 0;
+
+ if(!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)
+ {
+ // Swap 1st with 2nd. Now 2nd is empty.
+ m_2ndVectorMode = SECOND_VECTOR_EMPTY;
+ m_1stNullItemsMiddleCount = m_2ndNullItemsCount;
+ while(m_1stNullItemsBeginCount < suballocations2nd.size() &&
+ suballocations2nd[m_1stNullItemsBeginCount].hAllocation == VK_NULL_HANDLE)
+ {
+ ++m_1stNullItemsBeginCount;
+ --m_1stNullItemsMiddleCount;
+ }
+ m_2ndNullItemsCount = 0;
+ m_1stVectorIndex ^= 1;
+ }
+ }
+ }
+
+ VMA_HEAVY_ASSERT(Validate());
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// class VmaBlockMetadata_Buddy
+
+VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(VmaAllocator hAllocator) :
+ VmaBlockMetadata(hAllocator),
+ m_Root(VMA_NULL),
+ m_AllocationCount(0),
+ m_FreeCount(1),
+ m_SumFreeSize(0)
+{
+ memset(m_FreeList, 0, sizeof(m_FreeList));
+}
+
+VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()
+{
+ DeleteNode(m_Root);
+}
+
+void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)
+{
+ VmaBlockMetadata::Init(size);
+
+ m_UsableSize = VmaPrevPow2(size);
+ m_SumFreeSize = m_UsableSize;
+
+ // Calculate m_LevelCount.
+ m_LevelCount = 1;
+ while(m_LevelCount < MAX_LEVELS &&
+ LevelToNodeSize(m_LevelCount) >= MIN_NODE_SIZE)
+ {
+ ++m_LevelCount;
+ }
+
+ Node* rootNode = vma_new(GetAllocationCallbacks(), Node)();
+ rootNode->offset = 0;
+ rootNode->type = Node::TYPE_FREE;
+ rootNode->parent = VMA_NULL;
+ rootNode->buddy = VMA_NULL;
+
+ m_Root = rootNode;
+ AddToFreeListFront(0, rootNode);
+}
+
+bool VmaBlockMetadata_Buddy::Validate() const
+{
+ // Validate tree.
+ ValidationContext ctx;
+ if(!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))
+ {
+ VMA_VALIDATE(false && "ValidateNode failed.");
+ }
+ VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);
+ VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);
+
+ // Validate free node lists.
+ for(uint32_t level = 0; level < m_LevelCount; ++level)
+ {
+ VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||
+ m_FreeList[level].front->free.prev == VMA_NULL);
+
+ for(Node* node = m_FreeList[level].front;
+ node != VMA_NULL;
+ node = node->free.next)
+ {
+ VMA_VALIDATE(node->type == Node::TYPE_FREE);
+
+ if(node->free.next == VMA_NULL)
+ {
+ VMA_VALIDATE(m_FreeList[level].back == node);
+ }
+ else
+ {
+ VMA_VALIDATE(node->free.next->free.prev == node);
+ }
+ }
+ }
+
+ // Validate that free lists ar higher levels are empty.
+ for(uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)
+ {
+ VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);
+ }
+
+ return true;
+}
+
+VkDeviceSize VmaBlockMetadata_Buddy::GetUnusedRangeSizeMax() const
+{
+ for(uint32_t level = 0; level < m_LevelCount; ++level)
+ {
+ if(m_FreeList[level].front != VMA_NULL)
+ {
+ return LevelToNodeSize(level);
+ }
+ }
+ return 0;
+}
+
+void VmaBlockMetadata_Buddy::CalcAllocationStatInfo(VmaStatInfo& outInfo) const
+{
+ const VkDeviceSize unusableSize = GetUnusableSize();
+
+ outInfo.blockCount = 1;
+
+ outInfo.allocationCount = outInfo.unusedRangeCount = 0;
+ outInfo.usedBytes = outInfo.unusedBytes = 0;
+
+ outInfo.allocationSizeMax = outInfo.unusedRangeSizeMax = 0;
+ outInfo.allocationSizeMin = outInfo.unusedRangeSizeMin = UINT64_MAX;
+ outInfo.allocationSizeAvg = outInfo.unusedRangeSizeAvg = 0; // Unused.
+
+ CalcAllocationStatInfoNode(outInfo, m_Root, LevelToNodeSize(0));
+
+ if(unusableSize > 0)
+ {
+ ++outInfo.unusedRangeCount;
+ outInfo.unusedBytes += unusableSize;
+ outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusableSize);
+ outInfo.unusedRangeSizeMin = VMA_MIN(outInfo.unusedRangeSizeMin, unusableSize);
+ }
+}
+
+void VmaBlockMetadata_Buddy::AddPoolStats(VmaPoolStats& inoutStats) const
+{
+ const VkDeviceSize unusableSize = GetUnusableSize();
+
+ inoutStats.size += GetSize();
+ inoutStats.unusedSize += m_SumFreeSize + unusableSize;
+ inoutStats.allocationCount += m_AllocationCount;
+ inoutStats.unusedRangeCount += m_FreeCount;
+ inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, GetUnusedRangeSizeMax());
+
+ if(unusableSize > 0)
+ {
+ ++inoutStats.unusedRangeCount;
+ // Not updating inoutStats.unusedRangeSizeMax with unusableSize because this space is not available for allocations.
+ }
+}
+
+#if VMA_STATS_STRING_ENABLED
+
+void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json) const
+{
+ // TODO optimize
+ VmaStatInfo stat;
+ CalcAllocationStatInfo(stat);
+
+ PrintDetailedMap_Begin(
+ json,
+ stat.unusedBytes,
+ stat.allocationCount,
+ stat.unusedRangeCount);
+
+ PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));
+
+ const VkDeviceSize unusableSize = GetUnusableSize();
+ if(unusableSize > 0)
+ {
+ PrintDetailedMap_UnusedRange(json,
+ m_UsableSize, // offset
+ unusableSize); // size
+ }
+
+ PrintDetailedMap_End(json);
+}
+
+#endif // #if VMA_STATS_STRING_ENABLED
+
+bool VmaBlockMetadata_Buddy::CreateAllocationRequest(
+ uint32_t /*currentFrameIndex*/,
+ uint32_t /*frameInUseCount*/,
+ VkDeviceSize bufferImageGranularity,
+ VkDeviceSize allocSize,
+ VkDeviceSize allocAlignment,
+ bool upperAddress,
+ VmaSuballocationType allocType,
+ bool /*canMakeOtherLost*/,
+ uint32_t /*strategy*/,
+ VmaAllocationRequest* pAllocationRequest)
+{
+ VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");
+ (void) upperAddress;
+
+ // Simple way to respect bufferImageGranularity. May be optimized some day.
+ // Whenever it might be an OPTIMAL image...
+ if(allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||
+ allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
+ allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)
+ {
+ allocAlignment = VMA_MAX(allocAlignment, bufferImageGranularity);
+ allocSize = VMA_MAX(allocSize, bufferImageGranularity);
+ }
+
+ if(allocSize > m_UsableSize)
+ {
+ return false;
+ }
+
+ const uint32_t targetLevel = AllocSizeToLevel(allocSize);
+ for(uint32_t level = targetLevel + 1; level--; )
+ {
+ for(Node* freeNode = m_FreeList[level].front;
+ freeNode != VMA_NULL;
+ freeNode = freeNode->free.next)
+ {
+ if(freeNode->offset % allocAlignment == 0)
+ {
+ pAllocationRequest->offset = freeNode->offset;
+ pAllocationRequest->sumFreeSize = LevelToNodeSize(level);
+ pAllocationRequest->sumItemSize = 0;
+ pAllocationRequest->itemsToMakeLostCount = 0;
+ pAllocationRequest->customData = (void*)(uintptr_t)level;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool VmaBlockMetadata_Buddy::MakeRequestedAllocationsLost(
+ uint32_t /*currentFrameIndex*/,
+ uint32_t /*frameInUseCount*/,
+ VmaAllocationRequest* pAllocationRequest)
+{
+ /*
+ Lost allocations are not supported in buddy allocator at the moment.
+ Support might be added in the future.
+ */
+ return pAllocationRequest->itemsToMakeLostCount == 0;
+}
+
+uint32_t VmaBlockMetadata_Buddy::MakeAllocationsLost(uint32_t /*currentFrameIndex*/, uint32_t /*frameInUseCount*/)
+{
+ /*
+ Lost allocations are not supported in buddy allocator at the moment.
+ Support might be added in the future.
+ */
+ return 0;
+}
+
+void VmaBlockMetadata_Buddy::Alloc(
+ const VmaAllocationRequest& request,
+ VmaSuballocationType /*type*/,
+ VkDeviceSize allocSize,
+ bool /*upperAddress*/,
+ VmaAllocation hAllocation)
+{
+ const uint32_t targetLevel = AllocSizeToLevel(allocSize);
+ uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;
+
+ Node* currNode = m_FreeList[currLevel].front;
+ VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
+ while(currNode->offset != request.offset)
+ {
+ currNode = currNode->free.next;
+ VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);
+ }
+
+ // Go down, splitting free nodes.
+ while(currLevel < targetLevel)
+ {
+ // currNode is already first free node at currLevel.
+ // Remove it from list of free nodes at this currLevel.
+ RemoveFromFreeList(currLevel, currNode);
+
+ const uint32_t childrenLevel = currLevel + 1;
+
+ // Create two free sub-nodes.
+ Node* leftChild = vma_new(GetAllocationCallbacks(), Node)();
+ Node* rightChild = vma_new(GetAllocationCallbacks(), Node)();
+
+ leftChild->offset = currNode->offset;
+ leftChild->type = Node::TYPE_FREE;
+ leftChild->parent = currNode;
+ leftChild->buddy = rightChild;
+
+ rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);
+ rightChild->type = Node::TYPE_FREE;
+ rightChild->parent = currNode;
+ rightChild->buddy = leftChild;
+
+ // Convert current currNode to split type.
+ currNode->type = Node::TYPE_SPLIT;
+ currNode->split.leftChild = leftChild;
+
+ // Add child nodes to free list. Order is important!
+ AddToFreeListFront(childrenLevel, rightChild);
+ AddToFreeListFront(childrenLevel, leftChild);
+
+ ++m_FreeCount;
+ //m_SumFreeSize -= LevelToNodeSize(currLevel) % 2; // Useful only when level node sizes can be non power of 2.
+ ++currLevel;
+ currNode = m_FreeList[currLevel].front;
+
+ /*
+ We can be sure that currNode, as left child of node previously split,
+ also fullfills the alignment requirement.
+ */
+ }
+
+ // Remove from free list.
+ VMA_ASSERT(currLevel == targetLevel &&
+ currNode != VMA_NULL &&
+ currNode->type == Node::TYPE_FREE);
+ RemoveFromFreeList(currLevel, currNode);
+
+ // Convert to allocation node.
+ currNode->type = Node::TYPE_ALLOCATION;
+ currNode->allocation.alloc = hAllocation;
+
+ ++m_AllocationCount;
+ --m_FreeCount;
+ m_SumFreeSize -= allocSize;
+}
+
+void VmaBlockMetadata_Buddy::DeleteNode(Node* node)
+{
+ if(node->type == Node::TYPE_SPLIT)
+ {
+ DeleteNode(node->split.leftChild->buddy);
+ DeleteNode(node->split.leftChild);
+ }
+
+ vma_delete(GetAllocationCallbacks(), node);
+}
+
+bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const
+{
+ VMA_VALIDATE(level < m_LevelCount);
+ VMA_VALIDATE(curr->parent == parent);
+ VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));
+ VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);
+ switch(curr->type)
+ {
+ case Node::TYPE_FREE:
+ // curr->free.prev, next are validated separately.
+ ctx.calculatedSumFreeSize += levelNodeSize;
+ ++ctx.calculatedFreeCount;
+ break;
+ case Node::TYPE_ALLOCATION:
+ ++ctx.calculatedAllocationCount;
+ ctx.calculatedSumFreeSize += levelNodeSize - curr->allocation.alloc->GetSize();
+ VMA_VALIDATE(curr->allocation.alloc != VK_NULL_HANDLE);
+ break;
+ case Node::TYPE_SPLIT:
+ {
+ const uint32_t childrenLevel = level + 1;
+ const VkDeviceSize childrenLevelNodeSize = levelNodeSize / 2;
+ const Node* const leftChild = curr->split.leftChild;
+ VMA_VALIDATE(leftChild != VMA_NULL);
+ VMA_VALIDATE(leftChild->offset == curr->offset);
+ if(!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))
+ {
+ VMA_VALIDATE(false && "ValidateNode for left child failed.");
+ }
+ const Node* const rightChild = leftChild->buddy;
+ VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);
+ if(!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))
+ {
+ VMA_VALIDATE(false && "ValidateNode for right child failed.");
+ }
+ }
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const
+{
+ // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.
+ uint32_t level = 0;
+ VkDeviceSize currLevelNodeSize = m_UsableSize;
+ VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;
+ while(allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)
+ {
+ ++level;
+ currLevelNodeSize = nextLevelNodeSize;
+ nextLevelNodeSize = currLevelNodeSize >> 1;
+ }
+ return level;
+}
+
+void VmaBlockMetadata_Buddy::FreeAtOffset(VmaAllocation alloc, VkDeviceSize offset)
+{
+ // Find node and level.
+ Node* node = m_Root;
+ VkDeviceSize nodeOffset = 0;
+ uint32_t level = 0;
+ VkDeviceSize levelNodeSize = LevelToNodeSize(0);
+ while(node->type == Node::TYPE_SPLIT)
+ {
+ const VkDeviceSize nextLevelSize = levelNodeSize >> 1;
+ if(offset < nodeOffset + nextLevelSize)
+ {
+ node = node->split.leftChild;
+ }
+ else
+ {
+ node = node->split.leftChild->buddy;
+ nodeOffset += nextLevelSize;
+ }
+ ++level;
+ levelNodeSize = nextLevelSize;
+ }
+
+ VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);
+ VMA_ASSERT(alloc == VK_NULL_HANDLE || node->allocation.alloc == alloc);
+
+ ++m_FreeCount;
+ --m_AllocationCount;
+ m_SumFreeSize += alloc->GetSize();
+
+ node->type = Node::TYPE_FREE;
+
+ // Join free nodes if possible.
+ while(level > 0 && node->buddy->type == Node::TYPE_FREE)
+ {
+ RemoveFromFreeList(level, node->buddy);
+ Node* const parent = node->parent;
+
+ vma_delete(GetAllocationCallbacks(), node->buddy);
+ vma_delete(GetAllocationCallbacks(), node);
+ parent->type = Node::TYPE_FREE;
+
+ node = parent;
+ --level;
+ //m_SumFreeSize += LevelToNodeSize(level) % 2; // Useful only when level node sizes can be non power of 2.
+ --m_FreeCount;
+ }
+
+ AddToFreeListFront(level, node);
+}
+
+void VmaBlockMetadata_Buddy::CalcAllocationStatInfoNode(VmaStatInfo& outInfo, const Node* node, VkDeviceSize levelNodeSize) const
+{
+ switch(node->type)
+ {
+ case Node::TYPE_FREE:
+ ++outInfo.unusedRangeCount;
+ outInfo.unusedBytes += levelNodeSize;
+ outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, levelNodeSize);
+ outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, levelNodeSize);
+ break;
+ case Node::TYPE_ALLOCATION:
+ {
+ const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
+ ++outInfo.allocationCount;
+ outInfo.usedBytes += allocSize;
+ outInfo.allocationSizeMax = VMA_MAX(outInfo.allocationSizeMax, allocSize);
+ outInfo.allocationSizeMin = VMA_MAX(outInfo.allocationSizeMin, allocSize);
+
+ const VkDeviceSize unusedRangeSize = levelNodeSize - allocSize;
+ if(unusedRangeSize > 0)
+ {
+ ++outInfo.unusedRangeCount;
+ outInfo.unusedBytes += unusedRangeSize;
+ outInfo.unusedRangeSizeMax = VMA_MAX(outInfo.unusedRangeSizeMax, unusedRangeSize);
+ outInfo.unusedRangeSizeMin = VMA_MAX(outInfo.unusedRangeSizeMin, unusedRangeSize);
+ }
+ }
+ break;
+ case Node::TYPE_SPLIT:
+ {
+ const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
+ const Node* const leftChild = node->split.leftChild;
+ CalcAllocationStatInfoNode(outInfo, leftChild, childrenNodeSize);
+ const Node* const rightChild = leftChild->buddy;
+ CalcAllocationStatInfoNode(outInfo, rightChild, childrenNodeSize);
+ }
+ break;
+ default:
+ VMA_ASSERT(0);
+ }
+}
+
+void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)
+{
+ VMA_ASSERT(node->type == Node::TYPE_FREE);
+
+ // List is empty.
+ Node* const frontNode = m_FreeList[level].front;
+ if(frontNode == VMA_NULL)
+ {
+ VMA_ASSERT(m_FreeList[level].back == VMA_NULL);
+ node->free.prev = node->free.next = VMA_NULL;
+ m_FreeList[level].front = m_FreeList[level].back = node;
+ }
+ else
+ {
+ VMA_ASSERT(frontNode->free.prev == VMA_NULL);
+ node->free.prev = VMA_NULL;
+ node->free.next = frontNode;
+ frontNode->free.prev = node;
+ m_FreeList[level].front = node;
+ }
+}
+
+void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)
+{
+ VMA_ASSERT(m_FreeList[level].front != VMA_NULL);
+
+ // It is at the front.
+ if(node->free.prev == VMA_NULL)
+ {
+ VMA_ASSERT(m_FreeList[level].front == node);
+ m_FreeList[level].front = node->free.next;
+ }
+ else
+ {
+ Node* const prevFreeNode = node->free.prev;
+ VMA_ASSERT(prevFreeNode->free.next == node);
+ prevFreeNode->free.next = node->free.next;
+ }
+
+ // It is at the back.
+ if(node->free.next == VMA_NULL)
+ {
+ VMA_ASSERT(m_FreeList[level].back == node);
+ m_FreeList[level].back = node->free.prev;
+ }
+ else
+ {
+ Node* const nextFreeNode = node->free.next;
+ VMA_ASSERT(nextFreeNode->free.prev == node);
+ nextFreeNode->free.prev = node->free.prev;
+ }
+}
+
+#if VMA_STATS_STRING_ENABLED
+void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const
+{
+ switch(node->type)
+ {
+ case Node::TYPE_FREE:
+ PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);
+ break;
+ case Node::TYPE_ALLOCATION:
+ {
+ PrintDetailedMap_Allocation(json, node->offset, node->allocation.alloc);
+ const VkDeviceSize allocSize = node->allocation.alloc->GetSize();
+ if(allocSize < levelNodeSize)
+ {
+ PrintDetailedMap_UnusedRange(json, node->offset + allocSize, levelNodeSize - allocSize);
+ }
+ }
+ break;
+ case Node::TYPE_SPLIT:
+ {
+ const VkDeviceSize childrenNodeSize = levelNodeSize / 2;
+ const Node* const leftChild = node->split.leftChild;
+ PrintDetailedMapNode(json, leftChild, childrenNodeSize);
+ const Node* const rightChild = leftChild->buddy;
+ PrintDetailedMapNode(json, rightChild, childrenNodeSize);
+ }
+ break;
+ default:
+ VMA_ASSERT(0);
+ }
+}
+#endif // #if VMA_STATS_STRING_ENABLED
+
+
+////////////////////////////////////////////////////////////////////////////////
+// class VmaDeviceMemoryBlock
+
+VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator /*hAllocator*/) :
+ m_pMetadata(VMA_NULL),
+ m_MemoryTypeIndex(UINT32_MAX),
+ m_Id(0),
+ m_hMemory(VK_NULL_HANDLE),
+ m_MapCount(0),
+ m_pMappedData(VMA_NULL)
+{
+}
+
+void VmaDeviceMemoryBlock::Init(
+ VmaAllocator hAllocator,
+ uint32_t newMemoryTypeIndex,
+ VkDeviceMemory newMemory,
+ VkDeviceSize newSize,
+ uint32_t id,
+ uint32_t algorithm)
+{
+ VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
+
+ m_MemoryTypeIndex = newMemoryTypeIndex;
+ m_Id = id;
+ m_hMemory = newMemory;
+
+ switch(algorithm)
+ {
+ case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:
+ m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator);
+ break;
+ case VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT:
+ m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Buddy)(hAllocator);
+ break;
+ default:
+ VMA_ASSERT(0);
+ // Fall-through.
+ case 0:
+ m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Generic)(hAllocator);
+ }
+ m_pMetadata->Init(newSize);
+}
+
+void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)
+{
+ // This is the most important assert in the entire library.
+ // Hitting it means you have some memory leak - unreleased VmaAllocation objects.
+ VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");
+
+ VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
+ allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);
+ m_hMemory = VK_NULL_HANDLE;
+
+ vma_delete(allocator, m_pMetadata);
+ m_pMetadata = VMA_NULL;
+}
+
+bool VmaDeviceMemoryBlock::Validate() const
+{
+ VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&
+ (m_pMetadata->GetSize() != 0));
+
+ return m_pMetadata->Validate();
+}
+
+VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
+{
+ void* pData = nullptr;
+ VkResult res = Map(hAllocator, 1, &pData);
+ if(res != VK_SUCCESS)
+ {
+ return res;
+ }
+
+ res = m_pMetadata->CheckCorruption(pData);
+
+ Unmap(hAllocator, 1);
+
+ return res;
+}
+
+VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)
+{
+ if(count == 0)
+ {
+ return VK_SUCCESS;
+ }
+
+ VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
+ if(m_MapCount != 0)
+ {
+ m_MapCount += count;
+ VMA_ASSERT(m_pMappedData != VMA_NULL);
+ if(ppData != VMA_NULL)
+ {
+ *ppData = m_pMappedData;
+ }
+ return VK_SUCCESS;
+ }
+ else
+ {
+ VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(
+ hAllocator->m_hDevice,
+ m_hMemory,
+ 0, // offset
+ VK_WHOLE_SIZE,
+ 0, // flags
+ &m_pMappedData);
+ if(result == VK_SUCCESS)
+ {
+ if(ppData != VMA_NULL)
+ {
+ *ppData = m_pMappedData;
+ }
+ m_MapCount = count;
+ }
+ return result;
+ }
+}
+
+void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)
+{
+ if(count == 0)
+ {
+ return;
+ }
+
+ VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
+ if(m_MapCount >= count)
+ {
+ m_MapCount -= count;
+ if(m_MapCount == 0)
+ {
+ m_pMappedData = VMA_NULL;
+ (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);
+ }
+ }
+ else
+ {
+ VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");
+ }
+}
+
+VkResult VmaDeviceMemoryBlock::WriteMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
+{
+ VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
+ VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
+
+ void* pData;
+ VkResult res = Map(hAllocator, 1, &pData);
+ if(res != VK_SUCCESS)
+ {
+ return res;
+ }
+
+ VmaWriteMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN);
+ VmaWriteMagicValue(pData, allocOffset + allocSize);
+
+ Unmap(hAllocator, 1);
+
+ return VK_SUCCESS;
+}
+
+VkResult VmaDeviceMemoryBlock::ValidateMagicValueAroundAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)
+{
+ VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);
+ VMA_ASSERT(allocOffset >= VMA_DEBUG_MARGIN);
+
+ void* pData;
+ VkResult res = Map(hAllocator, 1, &pData);
+ if(res != VK_SUCCESS)
+ {
+ return res;
+ }
+
+ if(!VmaValidateMagicValue(pData, allocOffset - VMA_DEBUG_MARGIN))
+ {
+ VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED BEFORE FREED ALLOCATION!");
+ }
+ else if(!VmaValidateMagicValue(pData, allocOffset + allocSize))
+ {
+ VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");
+ }
+
+ Unmap(hAllocator, 1);
+
+ return VK_SUCCESS;
+}
+
+VkResult VmaDeviceMemoryBlock::BindBufferMemory(
+ const VmaAllocator hAllocator,
+ const VmaAllocation hAllocation,
+ VkBuffer hBuffer)
+{
+ VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
+ hAllocation->GetBlock() == this);
+ // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
+ VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
+ return hAllocator->GetVulkanFunctions().vkBindBufferMemory(
+ hAllocator->m_hDevice,
+ hBuffer,
+ m_hMemory,
+ hAllocation->GetOffset());
+}
+
+VkResult VmaDeviceMemoryBlock::BindImageMemory(
+ const VmaAllocator hAllocator,
+ const VmaAllocation hAllocation,
+ VkImage hImage)
+{
+ VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&
+ hAllocation->GetBlock() == this);
+ // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.
+ VmaMutexLock lock(m_Mutex, hAllocator->m_UseMutex);
+ return hAllocator->GetVulkanFunctions().vkBindImageMemory(
+ hAllocator->m_hDevice,
+ hImage,
+ m_hMemory,
+ hAllocation->GetOffset());
+}
+
+static void InitStatInfo(VmaStatInfo& outInfo)
+{
+ memset(&outInfo, 0, sizeof(outInfo));
+ outInfo.allocationSizeMin = UINT64_MAX;
+ outInfo.unusedRangeSizeMin = UINT64_MAX;
+}
+
+// Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
+static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
+{
+ inoutInfo.blockCount += srcInfo.blockCount;
+ inoutInfo.allocationCount += srcInfo.allocationCount;
+ inoutInfo.unusedRangeCount += srcInfo.unusedRangeCount;
+ inoutInfo.usedBytes += srcInfo.usedBytes;
+ inoutInfo.unusedBytes += srcInfo.unusedBytes;
+ inoutInfo.allocationSizeMin = VMA_MIN(inoutInfo.allocationSizeMin, srcInfo.allocationSizeMin);
+ inoutInfo.allocationSizeMax = VMA_MAX(inoutInfo.allocationSizeMax, srcInfo.allocationSizeMax);
+ inoutInfo.unusedRangeSizeMin = VMA_MIN(inoutInfo.unusedRangeSizeMin, srcInfo.unusedRangeSizeMin);
+ inoutInfo.unusedRangeSizeMax = VMA_MAX(inoutInfo.unusedRangeSizeMax, srcInfo.unusedRangeSizeMax);
+}
+
+static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
+{
+ inoutInfo.allocationSizeAvg = (inoutInfo.allocationCount > 0) ?
+ VmaRoundDiv<VkDeviceSize>(inoutInfo.usedBytes, inoutInfo.allocationCount) : 0;
+ inoutInfo.unusedRangeSizeAvg = (inoutInfo.unusedRangeCount > 0) ?
+ VmaRoundDiv<VkDeviceSize>(inoutInfo.unusedBytes, inoutInfo.unusedRangeCount) : 0;
+}
+
+VmaPool_T::VmaPool_T(
+ VmaAllocator hAllocator,
+ const VmaPoolCreateInfo& createInfo,
+ VkDeviceSize preferredBlockSize) :
+ m_BlockVector(
+ hAllocator,
+ createInfo.memoryTypeIndex,
+ createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
+ createInfo.minBlockCount,
+ createInfo.maxBlockCount,
+ (createInfo.flags & VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
+ createInfo.frameInUseCount,
+ true, // isCustomPool
+ createInfo.blockSize != 0, // explicitBlockSize
+ createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK), // algorithm
+ m_Id(0)
+{
+}
+
+VmaPool_T::~VmaPool_T()
+{
+}
+
+#if VMA_STATS_STRING_ENABLED
+
+#endif // #if VMA_STATS_STRING_ENABLED
+
+VmaBlockVector::VmaBlockVector(
+ VmaAllocator hAllocator,
+ uint32_t memoryTypeIndex,
+ VkDeviceSize preferredBlockSize,
+ size_t minBlockCount,
+ size_t maxBlockCount,
+ VkDeviceSize bufferImageGranularity,
+ uint32_t frameInUseCount,
+ bool isCustomPool,
+ bool explicitBlockSize,
+ uint32_t algorithm) :
+ m_hAllocator(hAllocator),
+ m_MemoryTypeIndex(memoryTypeIndex),
+ m_PreferredBlockSize(preferredBlockSize),
+ m_MinBlockCount(minBlockCount),
+ m_MaxBlockCount(maxBlockCount),
+ m_BufferImageGranularity(bufferImageGranularity),
+ m_FrameInUseCount(frameInUseCount),
+ m_IsCustomPool(isCustomPool),
+ m_ExplicitBlockSize(explicitBlockSize),
+ m_Algorithm(algorithm),
+ m_HasEmptyBlock(false),
+ m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
+ m_NextBlockId(0)
+{
+}
+
+VmaBlockVector::~VmaBlockVector()
+{
+ for(size_t i = m_Blocks.size(); i--; )
+ {
+ m_Blocks[i]->Destroy(m_hAllocator);
+ vma_delete(m_hAllocator, m_Blocks[i]);
+ }
+}
+
+VkResult VmaBlockVector::CreateMinBlocks()
+{
+ for(size_t i = 0; i < m_MinBlockCount; ++i)
+ {
+ VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);
+ if(res != VK_SUCCESS)
+ {
+ return res;
+ }
+ }
+ return VK_SUCCESS;
+}
+
+void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
+{
+ VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
+
+ const size_t blockCount = m_Blocks.size();
+
+ pStats->size = 0;
+ pStats->unusedSize = 0;
+ pStats->allocationCount = 0;
+ pStats->unusedRangeCount = 0;
+ pStats->unusedRangeSizeMax = 0;
+ pStats->blockCount = blockCount;
+
+ for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
+ {
+ const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
+ VMA_ASSERT(pBlock);
+ VMA_HEAVY_ASSERT(pBlock->Validate());
+ pBlock->m_pMetadata->AddPoolStats(*pStats);
+ }
+}
+
+bool VmaBlockVector::IsCorruptionDetectionEnabled() const
+{
+ const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
+ return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&
+ (VMA_DEBUG_MARGIN > 0) &&
+ (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;
+}
+
+static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;
+
+VkResult VmaBlockVector::Allocate(
+ VmaPool hCurrentPool,
+ uint32_t currentFrameIndex,
+ VkDeviceSize size,
+ VkDeviceSize alignment,
+ const VmaAllocationCreateInfo& createInfo,
+ VmaSuballocationType suballocType,
+ size_t allocationCount,
+ VmaAllocation* pAllocations)
+{
+ size_t allocIndex;
+ VkResult res = VK_SUCCESS;
+
+ {
+ VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
+ for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
+ {
+ res = AllocatePage(
+ hCurrentPool,
+ currentFrameIndex,
+ size,
+ alignment,
+ createInfo,
+ suballocType,
+ pAllocations + allocIndex);
+ if(res != VK_SUCCESS)
+ {
+ break;
+ }
+ }
+ }
+
+ if(res != VK_SUCCESS)
+ {
+ // Free all already created allocations.
+ while(allocIndex--)
+ {
+ Free(pAllocations[allocIndex]);
+ }
+ memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
+ }
+
+ return res;
+}
+
+VkResult VmaBlockVector::AllocatePage(
+ VmaPool hCurrentPool,
+ uint32_t currentFrameIndex,
+ VkDeviceSize size,
+ VkDeviceSize alignment,
+ const VmaAllocationCreateInfo& createInfo,
+ VmaSuballocationType suballocType,
+ VmaAllocation* pAllocation)
+{
+ const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
+ bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
+ const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
+ const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
+ const bool canCreateNewBlock =
+ ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
+ (m_Blocks.size() < m_MaxBlockCount);
+ uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;
+
+ // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
+ // Which in turn is available only when maxBlockCount = 1.
+ if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT && m_MaxBlockCount > 1)
+ {
+ canMakeOtherLost = false;
+ }
+
+ // Upper address can only be used with linear allocator and within single memory block.
+ if(isUpperAddress &&
+ (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))
+ {
+ return VK_ERROR_FEATURE_NOT_PRESENT;
+ }
+
+ // Validate strategy.
+ switch(strategy)
+ {
+ case 0:
+ strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
+ break;
+ case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT:
+ case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT:
+ case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT:
+ break;
+ default:
+ return VK_ERROR_FEATURE_NOT_PRESENT;
+ }
+
+ // Early reject: requested allocation size is larger that maximum block size for this block vector.
+ if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
+ {
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ }
+
+ /*
+ Under certain condition, this whole section can be skipped for optimization, so
+ we move on directly to trying to allocate with canMakeOtherLost. That's the case
+ e.g. for custom pools with linear algorithm.
+ */
+ if(!canMakeOtherLost || canCreateNewBlock)
+ {
+ // 1. Search existing allocations. Try to allocate without making other allocations lost.
+ VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
+ allocFlagsCopy &= ~VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
+
+ if(m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
+ {
+ // Use only last block.
+ if(!m_Blocks.empty())
+ {
+ VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
+ VMA_ASSERT(pCurrBlock);
+ VkResult res = AllocateFromBlock(
+ pCurrBlock,
+ hCurrentPool,
+ currentFrameIndex,
+ size,
+ alignment,
+ allocFlagsCopy,
+ createInfo.pUserData,
+ suballocType,
+ strategy,
+ pAllocation);
+ if(res == VK_SUCCESS)
+ {
+ VMA_DEBUG_LOG(" Returned from last block #%u", (uint32_t)(m_Blocks.size() - 1));
+ return VK_SUCCESS;
+ }
+ }
+ }
+ else
+ {
+ if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
+ {
+ // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
+ for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
+ {
+ VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
+ VMA_ASSERT(pCurrBlock);
+ VkResult res = AllocateFromBlock(
+ pCurrBlock,
+ hCurrentPool,
+ currentFrameIndex,
+ size,
+ alignment,
+ allocFlagsCopy,
+ createInfo.pUserData,
+ suballocType,
+ strategy,
+ pAllocation);
+ if(res == VK_SUCCESS)
+ {
+ VMA_DEBUG_LOG(" Returned from existing block #%u", (uint32_t)blockIndex);
+ return VK_SUCCESS;
+ }
+ }
+ }
+ else // WORST_FIT, FIRST_FIT
+ {
+ // Backward order in m_Blocks - prefer blocks with largest amount of free space.
+ for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
+ {
+ VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
+ VMA_ASSERT(pCurrBlock);
+ VkResult res = AllocateFromBlock(
+ pCurrBlock,
+ hCurrentPool,
+ currentFrameIndex,
+ size,
+ alignment,
+ allocFlagsCopy,
+ createInfo.pUserData,
+ suballocType,
+ strategy,
+ pAllocation);
+ if(res == VK_SUCCESS)
+ {
+ VMA_DEBUG_LOG(" Returned from existing block #%u", (uint32_t)blockIndex);
+ return VK_SUCCESS;
+ }
+ }
+ }
+ }
+
+ // 2. Try to create new block.
+ if(canCreateNewBlock)
+ {
+ // Calculate optimal size for new block.
+ VkDeviceSize newBlockSize = m_PreferredBlockSize;
+ uint32_t newBlockSizeShift = 0;
+ const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
+
+ if(!m_ExplicitBlockSize)
+ {
+ // Allocate 1/8, 1/4, 1/2 as first blocks.
+ const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
+ for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
+ {
+ const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
+ if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
+ {
+ newBlockSize = smallerNewBlockSize;
+ ++newBlockSizeShift;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ size_t newBlockIndex = 0;
+ VkResult res = CreateBlock(newBlockSize, &newBlockIndex);
+ // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
+ if(!m_ExplicitBlockSize)
+ {
+ while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
+ {
+ const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
+ if(smallerNewBlockSize >= size)
+ {
+ newBlockSize = smallerNewBlockSize;
+ ++newBlockSizeShift;
+ res = CreateBlock(newBlockSize, &newBlockIndex);
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ if(res == VK_SUCCESS)
+ {
+ VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
+ VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
+
+ res = AllocateFromBlock(
+ pBlock,
+ hCurrentPool,
+ currentFrameIndex,
+ size,
+ alignment,
+ allocFlagsCopy,
+ createInfo.pUserData,
+ suballocType,
+ strategy,
+ pAllocation);
+ if(res == VK_SUCCESS)
+ {
+ VMA_DEBUG_LOG(" Created new block Size=%llu", newBlockSize);
+ return VK_SUCCESS;
+ }
+ else
+ {
+ // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ }
+ }
+ }
+ }
+
+ // 3. Try to allocate from existing blocks with making other allocations lost.
+ if(canMakeOtherLost)
+ {
+ uint32_t tryIndex = 0;
+ for(; tryIndex < VMA_ALLOCATION_TRY_COUNT; ++tryIndex)
+ {
+ VmaDeviceMemoryBlock* pBestRequestBlock = VMA_NULL;
+ VmaAllocationRequest bestRequest = {};
+ VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
+
+ // 1. Search existing allocations.
+ if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
+ {
+ // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
+ for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
+ {
+ VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
+ VMA_ASSERT(pCurrBlock);
+ VmaAllocationRequest currRequest = {};
+ if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
+ currentFrameIndex,
+ m_FrameInUseCount,
+ m_BufferImageGranularity,
+ size,
+ alignment,
+ (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
+ suballocType,
+ canMakeOtherLost,
+ strategy,
+ &currRequest))
+ {
+ const VkDeviceSize currRequestCost = currRequest.CalcCost();
+ if(pBestRequestBlock == VMA_NULL ||
+ currRequestCost < bestRequestCost)
+ {
+ pBestRequestBlock = pCurrBlock;
+ bestRequest = currRequest;
+ bestRequestCost = currRequestCost;
+
+ if(bestRequestCost == 0)
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+ else // WORST_FIT, FIRST_FIT
+ {
+ // Backward order in m_Blocks - prefer blocks with largest amount of free space.
+ for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
+ {
+ VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
+ VMA_ASSERT(pCurrBlock);
+ VmaAllocationRequest currRequest = {};
+ if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
+ currentFrameIndex,
+ m_FrameInUseCount,
+ m_BufferImageGranularity,
+ size,
+ alignment,
+ (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
+ suballocType,
+ canMakeOtherLost,
+ strategy,
+ &currRequest))
+ {
+ const VkDeviceSize currRequestCost = currRequest.CalcCost();
+ if(pBestRequestBlock == VMA_NULL ||
+ currRequestCost < bestRequestCost ||
+ strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
+ {
+ pBestRequestBlock = pCurrBlock;
+ bestRequest = currRequest;
+ bestRequestCost = currRequestCost;
+
+ if(bestRequestCost == 0 ||
+ strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if(pBestRequestBlock != VMA_NULL)
+ {
+ if(mapped)
+ {
+ VkResult res = pBestRequestBlock->Map(m_hAllocator, 1, VMA_NULL);
+ if(res != VK_SUCCESS)
+ {
+ return res;
+ }
+ }
+
+ if(pBestRequestBlock->m_pMetadata->MakeRequestedAllocationsLost(
+ currentFrameIndex,
+ m_FrameInUseCount,
+ &bestRequest))
+ {
+ // We no longer have an empty Allocation.
+ if(pBestRequestBlock->m_pMetadata->IsEmpty())
+ {
+ m_HasEmptyBlock = false;
+ }
+ // Allocate from this pBlock.
+ *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
+ pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, isUpperAddress, *pAllocation);
+ (*pAllocation)->InitBlockAllocation(
+ hCurrentPool,
+ pBestRequestBlock,
+ bestRequest.offset,
+ alignment,
+ size,
+ suballocType,
+ mapped,
+ (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
+ VMA_HEAVY_ASSERT(pBestRequestBlock->Validate());
+ VMA_DEBUG_LOG(" Returned from existing allocation #%u", (uint32_t)blockIndex);
+ (*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
+ if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
+ {
+ m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
+ }
+ if(IsCorruptionDetectionEnabled())
+ {
+ VkResult res = pBestRequestBlock->WriteMagicValueAroundAllocation(m_hAllocator, bestRequest.offset, size);
+ (void) res;
+ VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
+ }
+ return VK_SUCCESS;
+ }
+ // else: Some allocations must have been touched while we are here. Next try.
+ }
+ else
+ {
+ // Could not find place in any of the blocks - break outer loop.
+ break;
+ }
+ }
+ /* Maximum number of tries exceeded - a very unlike event when many other
+ threads are simultaneously touching allocations making it impossible to make
+ lost at the same time as we try to allocate. */
+ if(tryIndex == VMA_ALLOCATION_TRY_COUNT)
+ {
+ return VK_ERROR_TOO_MANY_OBJECTS;
+ }
+ }
+
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+}
+
+void VmaBlockVector::Free(
+ VmaAllocation hAllocation)
+{
+ VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;
+
+ // Scope for lock.
+ {
+ VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
+
+ VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
+
+ if(IsCorruptionDetectionEnabled())
+ {
+ VkResult res = pBlock->ValidateMagicValueAroundAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());
+ (void) res;
+ VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");
+ }
+
+ if(hAllocation->IsPersistentMap())
+ {
+ pBlock->Unmap(m_hAllocator, 1);
+ }
+
+ pBlock->m_pMetadata->Free(hAllocation);
+ VMA_HEAVY_ASSERT(pBlock->Validate());
+
+ VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", memTypeIndex);
+
+ // pBlock became empty after this deallocation.
+ if(pBlock->m_pMetadata->IsEmpty())
+ {
+ // Already has empty Allocation. We don't want to have two, so delete this one.
+ if(m_HasEmptyBlock && m_Blocks.size() > m_MinBlockCount)
+ {
+ pBlockToDelete = pBlock;
+ Remove(pBlock);
+ }
+ // We now have first empty block.
+ else
+ {
+ m_HasEmptyBlock = true;
+ }
+ }
+ // pBlock didn't become empty, but we have another empty block - find and free that one.
+ // (This is optional, heuristics.)
+ else if(m_HasEmptyBlock)
+ {
+ VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
+ if(pLastBlock->m_pMetadata->IsEmpty() && m_Blocks.size() > m_MinBlockCount)
+ {
+ pBlockToDelete = pLastBlock;
+ m_Blocks.pop_back();
+ m_HasEmptyBlock = false;
+ }
+ }
+
+ IncrementallySortBlocks();
+ }
+
+ // Destruction of a free Allocation. Deferred until this point, outside of mutex
+ // lock, for performance reason.
+ if(pBlockToDelete != VMA_NULL)
+ {
+ VMA_DEBUG_LOG(" Deleted empty allocation");
+ pBlockToDelete->Destroy(m_hAllocator);
+ vma_delete(m_hAllocator, pBlockToDelete);
+ }
+}
+
+VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const
+{
+ VkDeviceSize result = 0;
+ for(size_t i = m_Blocks.size(); i--; )
+ {
+ result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());
+ if(result >= m_PreferredBlockSize)
+ {
+ break;
+ }
+ }
+ return result;
+}
+
+void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
+{
+ for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
+ {
+ if(m_Blocks[blockIndex] == pBlock)
+ {
+ VmaVectorRemove(m_Blocks, blockIndex);
+ return;
+ }
+ }
+ VMA_ASSERT(0);
+}
+
+void VmaBlockVector::IncrementallySortBlocks()
+{
+ if(m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)
+ {
+ // Bubble sort only until first swap.
+ for(size_t i = 1; i < m_Blocks.size(); ++i)
+ {
+ if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
+ {
+ VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
+ return;
+ }
+ }
+ }
+}
+
+VkResult VmaBlockVector::AllocateFromBlock(
+ VmaDeviceMemoryBlock* pBlock,
+ VmaPool hCurrentPool,
+ uint32_t currentFrameIndex,
+ VkDeviceSize size,
+ VkDeviceSize alignment,
+ VmaAllocationCreateFlags allocFlags,
+ void* pUserData,
+ VmaSuballocationType suballocType,
+ uint32_t strategy,
+ VmaAllocation* pAllocation)
+{
+ VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
+ const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
+ const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
+ const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
+
+ VmaAllocationRequest currRequest = {};
+ if(pBlock->m_pMetadata->CreateAllocationRequest(
+ currentFrameIndex,
+ m_FrameInUseCount,
+ m_BufferImageGranularity,
+ size,
+ alignment,
+ isUpperAddress,
+ suballocType,
+ false, // canMakeOtherLost
+ strategy,
+ &currRequest))
+ {
+ // Allocate from pCurrBlock.
+ VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
+
+ if(mapped)
+ {
+ VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
+ if(res != VK_SUCCESS)
+ {
+ return res;
+ }
+ }
+
+ // We no longer have an empty Allocation.
+ if(pBlock->m_pMetadata->IsEmpty())
+ {
+ m_HasEmptyBlock = false;
+ }
+
+ *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
+ pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, isUpperAddress, *pAllocation);
+ (*pAllocation)->InitBlockAllocation(
+ hCurrentPool,
+ pBlock,
+ currRequest.offset,
+ alignment,
+ size,
+ suballocType,
+ mapped,
+ (allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
+ VMA_HEAVY_ASSERT(pBlock->Validate());
+ (*pAllocation)->SetUserData(m_hAllocator, pUserData);
+ if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
+ {
+ m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
+ }
+ if(IsCorruptionDetectionEnabled())
+ {
+ VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
+ (void) res;
+ VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
+ }
+ return VK_SUCCESS;
+ }
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+}
+
+VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
+{
+ VkMemoryAllocateInfo allocInfo = {};
+ allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ allocInfo.memoryTypeIndex = m_MemoryTypeIndex;
+ allocInfo.allocationSize = blockSize;
+ VkDeviceMemory mem = VK_NULL_HANDLE;
+ VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);
+ if(res < 0)
+ {
+ return res;
+ }
+
+ // New VkDeviceMemory successfully created.
+
+ // Create new Allocation for it.
+ VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);
+ pBlock->Init(
+ m_hAllocator,
+ m_MemoryTypeIndex,
+ mem,
+ allocInfo.allocationSize,
+ m_NextBlockId++,
+ m_Algorithm);
+
+ m_Blocks.push_back(pBlock);
+ if(pNewBlockIndex != VMA_NULL)
+ {
+ *pNewBlockIndex = m_Blocks.size() - 1;
+ }
+
+ return VK_SUCCESS;
+}
+
+void VmaBlockVector::ApplyDefragmentationMovesCpu(
+ class VmaBlockVectorDefragmentationContext* pDefragCtx,
+ const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves)
+{
+ const size_t blockCount = m_Blocks.size();
+ const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
+
+ enum BLOCK_FLAG
+ {
+ BLOCK_FLAG_USED = 0x00000001,
+ BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
+ };
+
+ struct BlockInfo
+ {
+ uint32_t flags;
+ void* pMappedData;
+ };
+ VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
+ blockInfo(blockCount, VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
+ memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
+
+ // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
+ const size_t moveCount = moves.size();
+ for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
+ {
+ const VmaDefragmentationMove& move = moves[moveIndex];
+ blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
+ blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
+ }
+
+ VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
+
+ // Go over all blocks. Get mapped pointer or map if necessary.
+ for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
+ {
+ BlockInfo& currBlockInfo = blockInfo[blockIndex];
+ VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
+ if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
+ {
+ currBlockInfo.pMappedData = pBlock->GetMappedData();
+ // It is not originally mapped - map it.
+ if(currBlockInfo.pMappedData == VMA_NULL)
+ {
+ pDefragCtx->res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
+ if(pDefragCtx->res == VK_SUCCESS)
+ {
+ currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
+ }
+ }
+ }
+ }
+
+ // Go over all moves. Do actual data transfer.
+ if(pDefragCtx->res == VK_SUCCESS)
+ {
+ const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
+ VkMappedMemoryRange memRange = {};
+ memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
+
+ for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
+ {
+ const VmaDefragmentationMove& move = moves[moveIndex];
+
+ const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
+ const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
+
+ VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
+
+ // Invalidate source.
+ if(isNonCoherent)
+ {
+ VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
+ memRange.memory = pSrcBlock->GetDeviceMemory();
+ memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
+ memRange.size = VMA_MIN(
+ VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
+ pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
+ (*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
+ }
+
+ // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
+ memmove(
+ reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
+ reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
+ static_cast<size_t>(move.size));
+
+ if(IsCorruptionDetectionEnabled())
+ {
+ VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
+ VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
+ }
+
+ // Flush destination.
+ if(isNonCoherent)
+ {
+ VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
+ memRange.memory = pDstBlock->GetDeviceMemory();
+ memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
+ memRange.size = VMA_MIN(
+ VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
+ pDstBlock->m_pMetadata->GetSize() - memRange.offset);
+ (*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
+ }
+ }
+ }
+
+ // Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
+ // Regardless of pCtx->res == VK_SUCCESS.
+ for(size_t blockIndex = blockCount; blockIndex--; )
+ {
+ const BlockInfo& currBlockInfo = blockInfo[blockIndex];
+ if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
+ {
+ VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
+ pBlock->Unmap(m_hAllocator, 1);
+ }
+ }
+}
+
+void VmaBlockVector::ApplyDefragmentationMovesGpu(
+ class VmaBlockVectorDefragmentationContext* pDefragCtx,
+ const VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
+ VkCommandBuffer commandBuffer)
+{
+ const size_t blockCount = m_Blocks.size();
+
+ pDefragCtx->blockContexts.resize(blockCount);
+ for (size_t i = 0; i < blockCount; ++i)
+ pDefragCtx->blockContexts[i] = VmaBlockDefragmentationContext();
+
+ // Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
+ const size_t moveCount = moves.size();
+ for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
+ {
+ const VmaDefragmentationMove& move = moves[moveIndex];
+ pDefragCtx->blockContexts[move.srcBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
+ pDefragCtx->blockContexts[move.dstBlockIndex].flags |= VmaBlockDefragmentationContext::BLOCK_FLAG_USED;
+ }
+
+ VMA_ASSERT(pDefragCtx->res == VK_SUCCESS);
+
+ // Go over all blocks. Create and bind buffer for whole block if necessary.
+ {
+ VkBufferCreateInfo bufCreateInfo = {};
+ bufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
+ VK_BUFFER_USAGE_TRANSFER_DST_BIT;
+
+ for(size_t blockIndex = 0; pDefragCtx->res == VK_SUCCESS && blockIndex < blockCount; ++blockIndex)
+ {
+ VmaBlockDefragmentationContext& currBlockCtx = pDefragCtx->blockContexts[blockIndex];
+ VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
+ if((currBlockCtx.flags & VmaBlockDefragmentationContext::BLOCK_FLAG_USED) != 0)
+ {
+ bufCreateInfo.size = pBlock->m_pMetadata->GetSize();
+ pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkCreateBuffer)(
+ m_hAllocator->m_hDevice, &bufCreateInfo, m_hAllocator->GetAllocationCallbacks(), &currBlockCtx.hBuffer);
+ if(pDefragCtx->res == VK_SUCCESS)
+ {
+ pDefragCtx->res = (*m_hAllocator->GetVulkanFunctions().vkBindBufferMemory)(
+ m_hAllocator->m_hDevice, currBlockCtx.hBuffer, pBlock->GetDeviceMemory(), 0);
+ }
+ }
+ }
+ }
+
+ // Go over all moves. Post data transfer commands to command buffer.
+ if(pDefragCtx->res == VK_SUCCESS)
+ {
+ /*const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
+ VkMappedMemoryRange memRange = {};
+ memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;*/
+
+ for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
+ {
+ const VmaDefragmentationMove& move = moves[moveIndex];
+
+ const VmaBlockDefragmentationContext& srcBlockCtx = pDefragCtx->blockContexts[move.srcBlockIndex];
+ const VmaBlockDefragmentationContext& dstBlockCtx = pDefragCtx->blockContexts[move.dstBlockIndex];
+
+ VMA_ASSERT(srcBlockCtx.hBuffer && dstBlockCtx.hBuffer);
+
+ VkBufferCopy region = {
+ move.srcOffset,
+ move.dstOffset,
+ move.size };
+ (*m_hAllocator->GetVulkanFunctions().vkCmdCopyBuffer)(
+ commandBuffer, srcBlockCtx.hBuffer, dstBlockCtx.hBuffer, 1, &region);
+ }
+ }
+
+ // Save buffers to defrag context for later destruction.
+ if(pDefragCtx->res == VK_SUCCESS && moveCount > 0)
+ {
+ pDefragCtx->res = VK_NOT_READY;
+ }
+}
+
+void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
+{
+ m_HasEmptyBlock = false;
+ for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
+ {
+ VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
+ if(pBlock->m_pMetadata->IsEmpty())
+ {
+ if(m_Blocks.size() > m_MinBlockCount)
+ {
+ if(pDefragmentationStats != VMA_NULL)
+ {
+ ++pDefragmentationStats->deviceMemoryBlocksFreed;
+ pDefragmentationStats->bytesFreed += pBlock->m_pMetadata->GetSize();
+ }
+
+ VmaVectorRemove(m_Blocks, blockIndex);
+ pBlock->Destroy(m_hAllocator);
+ vma_delete(m_hAllocator, pBlock);
+ }
+ else
+ {
+ m_HasEmptyBlock = true;
+ }
+ }
+ }
+}
+
+#if VMA_STATS_STRING_ENABLED
+
+void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
+{
+ VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
+
+ json.BeginObject();
+
+ if(m_IsCustomPool)
+ {
+ json.WriteString("MemoryTypeIndex");
+ json.WriteNumber(m_MemoryTypeIndex);
+
+ json.WriteString("BlockSize");
+ json.WriteNumber(m_PreferredBlockSize);
+
+ json.WriteString("BlockCount");
+ json.BeginObject(true);
+ if(m_MinBlockCount > 0)
+ {
+ json.WriteString("Min");
+ json.WriteNumber((uint64_t)m_MinBlockCount);
+ }
+ if(m_MaxBlockCount < SIZE_MAX)
+ {
+ json.WriteString("Max");
+ json.WriteNumber((uint64_t)m_MaxBlockCount);
+ }
+ json.WriteString("Cur");
+ json.WriteNumber((uint64_t)m_Blocks.size());
+ json.EndObject();
+
+ if(m_FrameInUseCount > 0)
+ {
+ json.WriteString("FrameInUseCount");
+ json.WriteNumber(m_FrameInUseCount);
+ }
+
+ if(m_Algorithm != 0)
+ {
+ json.WriteString("Algorithm");
+ json.WriteString(VmaAlgorithmToStr(m_Algorithm));
+ }
+ }
+ else
+ {
+ json.WriteString("PreferredBlockSize");
+ json.WriteNumber(m_PreferredBlockSize);
+ }
+
+ json.WriteString("Blocks");
+ json.BeginObject();
+ for(size_t i = 0; i < m_Blocks.size(); ++i)
+ {
+ json.BeginString();
+ json.ContinueString(m_Blocks[i]->GetId());
+ json.EndString();
+
+ m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);
+ }
+ json.EndObject();
+
+ json.EndObject();
+}
+
+#endif // #if VMA_STATS_STRING_ENABLED
+
+void VmaBlockVector::Defragment(
+ class VmaBlockVectorDefragmentationContext* pCtx,
+ VmaDefragmentationStats* pStats,
+ VkDeviceSize& maxCpuBytesToMove, uint32_t& maxCpuAllocationsToMove,
+ VkDeviceSize& maxGpuBytesToMove, uint32_t& maxGpuAllocationsToMove,
+ VkCommandBuffer commandBuffer)
+{
+ pCtx->res = VK_SUCCESS;
+
+ const VkMemoryPropertyFlags memPropFlags =
+ m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags;
+ const bool isHostVisible = (memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;
+ const bool isHostCoherent = (memPropFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0;
+
+ const bool canDefragmentOnCpu = maxCpuBytesToMove > 0 && maxCpuAllocationsToMove > 0 &&
+ isHostVisible;
+ const bool canDefragmentOnGpu = maxGpuBytesToMove > 0 && maxGpuAllocationsToMove > 0 &&
+ (VMA_DEBUG_DETECT_CORRUPTION == 0 || !(isHostVisible && isHostCoherent));
+
+ // There are options to defragment this memory type.
+ if(canDefragmentOnCpu || canDefragmentOnGpu)
+ {
+ bool defragmentOnGpu;
+ // There is only one option to defragment this memory type.
+ if(canDefragmentOnGpu != canDefragmentOnCpu)
+ {
+ defragmentOnGpu = canDefragmentOnGpu;
+ }
+ // Both options are available: Heuristics to choose the best one.
+ else
+ {
+ defragmentOnGpu = (memPropFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0 ||
+ m_hAllocator->IsIntegratedGpu();
+ }
+
+ bool overlappingMoveSupported = !defragmentOnGpu;
+
+ if(m_hAllocator->m_UseMutex)
+ {
+ m_Mutex.LockWrite();
+ pCtx->mutexLocked = true;
+ }
+
+ pCtx->Begin(overlappingMoveSupported);
+
+ // Defragment.
+
+ const VkDeviceSize maxBytesToMove = defragmentOnGpu ? maxGpuBytesToMove : maxCpuBytesToMove;
+ const uint32_t maxAllocationsToMove = defragmentOnGpu ? maxGpuAllocationsToMove : maxCpuAllocationsToMove;
+ VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > moves =
+ VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >(VmaStlAllocator<VmaDefragmentationMove>(m_hAllocator->GetAllocationCallbacks()));
+ pCtx->res = pCtx->GetAlgorithm()->Defragment(moves, maxBytesToMove, maxAllocationsToMove);
+
+ // Accumulate statistics.
+ if(pStats != VMA_NULL)
+ {
+ const VkDeviceSize bytesMoved = pCtx->GetAlgorithm()->GetBytesMoved();
+ const uint32_t allocationsMoved = pCtx->GetAlgorithm()->GetAllocationsMoved();
+ pStats->bytesMoved += bytesMoved;
+ pStats->allocationsMoved += allocationsMoved;
+ VMA_ASSERT(bytesMoved <= maxBytesToMove);
+ VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
+ if(defragmentOnGpu)
+ {
+ maxGpuBytesToMove -= bytesMoved;
+ maxGpuAllocationsToMove -= allocationsMoved;
+ }
+ else
+ {
+ maxCpuBytesToMove -= bytesMoved;
+ maxCpuAllocationsToMove -= allocationsMoved;
+ }
+ }
+
+ if(pCtx->res >= VK_SUCCESS)
+ {
+ if(defragmentOnGpu)
+ {
+ ApplyDefragmentationMovesGpu(pCtx, moves, commandBuffer);
+ }
+ else
+ {
+ ApplyDefragmentationMovesCpu(pCtx, moves);
+ }
+ }
+ }
+}
+
+void VmaBlockVector::DefragmentationEnd(
+ class VmaBlockVectorDefragmentationContext* pCtx,
+ VmaDefragmentationStats* pStats)
+{
+ // Destroy buffers.
+ for(size_t blockIndex = pCtx->blockContexts.size(); blockIndex--; )
+ {
+ VmaBlockDefragmentationContext& blockCtx = pCtx->blockContexts[blockIndex];
+ if(blockCtx.hBuffer)
+ {
+ (*m_hAllocator->GetVulkanFunctions().vkDestroyBuffer)(
+ m_hAllocator->m_hDevice, blockCtx.hBuffer, m_hAllocator->GetAllocationCallbacks());
+ }
+ }
+
+ if(pCtx->res >= VK_SUCCESS)
+ {
+ FreeEmptyBlocks(pStats);
+ }
+
+ if(pCtx->mutexLocked)
+ {
+ VMA_ASSERT(m_hAllocator->m_UseMutex);
+ m_Mutex.UnlockWrite();
+ }
+}
+
+size_t VmaBlockVector::CalcAllocationCount() const
+{
+ size_t result = 0;
+ for(size_t i = 0; i < m_Blocks.size(); ++i)
+ {
+ result += m_Blocks[i]->m_pMetadata->GetAllocationCount();
+ }
+ return result;
+}
+
+bool VmaBlockVector::IsBufferImageGranularityConflictPossible() const
+{
+ if(m_BufferImageGranularity == 1)
+ {
+ return false;
+ }
+ VmaSuballocationType lastSuballocType = VMA_SUBALLOCATION_TYPE_FREE;
+ for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
+ {
+ VmaDeviceMemoryBlock* const pBlock = m_Blocks[i];
+ VMA_ASSERT(m_Algorithm == 0);
+ VmaBlockMetadata_Generic* const pMetadata = (VmaBlockMetadata_Generic*)pBlock->m_pMetadata;
+ if(pMetadata->IsBufferImageGranularityConflictPossible(m_BufferImageGranularity, lastSuballocType))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+void VmaBlockVector::MakePoolAllocationsLost(
+ uint32_t currentFrameIndex,
+ size_t* pLostAllocationCount)
+{
+ VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
+ size_t lostAllocationCount = 0;
+ for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
+ {
+ VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
+ VMA_ASSERT(pBlock);
+ lostAllocationCount += pBlock->m_pMetadata->MakeAllocationsLost(currentFrameIndex, m_FrameInUseCount);
+ }
+ if(pLostAllocationCount != VMA_NULL)
+ {
+ *pLostAllocationCount = lostAllocationCount;
+ }
+}
+
+VkResult VmaBlockVector::CheckCorruption()
+{
+ if(!IsCorruptionDetectionEnabled())
+ {
+ return VK_ERROR_FEATURE_NOT_PRESENT;
+ }
+
+ VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
+ for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
+ {
+ VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
+ VMA_ASSERT(pBlock);
+ VkResult res = pBlock->CheckCorruption(m_hAllocator);
+ if(res != VK_SUCCESS)
+ {
+ return res;
+ }
+ }
+ return VK_SUCCESS;
+}
+
+void VmaBlockVector::AddStats(VmaStats* pStats)
+{
+ const uint32_t memTypeIndex = m_MemoryTypeIndex;
+ const uint32_t memHeapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(memTypeIndex);
+
+ VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);
+
+ for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
+ {
+ const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
+ VMA_ASSERT(pBlock);
+ VMA_HEAVY_ASSERT(pBlock->Validate());
+ VmaStatInfo allocationStatInfo;
+ pBlock->m_pMetadata->CalcAllocationStatInfo(allocationStatInfo);
+ VmaAddStatInfo(pStats->total, allocationStatInfo);
+ VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
+ VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// VmaDefragmentationAlgorithm_Generic members definition
+
+VmaDefragmentationAlgorithm_Generic::VmaDefragmentationAlgorithm_Generic(
+ VmaAllocator hAllocator,
+ VmaBlockVector* pBlockVector,
+ uint32_t currentFrameIndex,
+ bool /*overlappingMoveSupported*/) :
+ VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
+ m_AllocationCount(0),
+ m_AllAllocations(false),
+ m_BytesMoved(0),
+ m_AllocationsMoved(0),
+ m_Blocks(VmaStlAllocator<BlockInfo*>(hAllocator->GetAllocationCallbacks()))
+{
+ // Create block info for each block.
+ const size_t blockCount = m_pBlockVector->m_Blocks.size();
+ for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
+ {
+ BlockInfo* pBlockInfo = vma_new(m_hAllocator, BlockInfo)(m_hAllocator->GetAllocationCallbacks());
+ pBlockInfo->m_OriginalBlockIndex = blockIndex;
+ pBlockInfo->m_pBlock = m_pBlockVector->m_Blocks[blockIndex];
+ m_Blocks.push_back(pBlockInfo);
+ }
+
+ // Sort them by m_pBlock pointer value.
+ VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
+}
+
+VmaDefragmentationAlgorithm_Generic::~VmaDefragmentationAlgorithm_Generic()
+{
+ for(size_t i = m_Blocks.size(); i--; )
+ {
+ vma_delete(m_hAllocator, m_Blocks[i]);
+ }
+}
+
+void VmaDefragmentationAlgorithm_Generic::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
+{
+ // Now as we are inside VmaBlockVector::m_Mutex, we can make final check if this allocation was not lost.
+ if(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)
+ {
+ VmaDeviceMemoryBlock* pBlock = hAlloc->GetBlock();
+ BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
+ if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
+ {
+ AllocationInfo allocInfo = AllocationInfo(hAlloc, pChanged);
+ (*it)->m_Allocations.push_back(allocInfo);
+ }
+ else
+ {
+ VMA_ASSERT(0);
+ }
+
+ ++m_AllocationCount;
+ }
+}
+
+VkResult VmaDefragmentationAlgorithm_Generic::DefragmentRound(
+ VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
+ VkDeviceSize maxBytesToMove,
+ uint32_t maxAllocationsToMove)
+{
+ if(m_Blocks.empty())
+ {
+ return VK_SUCCESS;
+ }
+
+ // This is a choice based on research.
+ // Option 1:
+ uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT;
+ // Option 2:
+ //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT;
+ // Option 3:
+ //uint32_t strategy = VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT;
+
+ size_t srcBlockMinIndex = 0;
+ // When FAST_ALGORITHM, move allocations from only last out of blocks that contain non-movable allocations.
+ /*
+ if(m_AlgorithmFlags & VMA_DEFRAGMENTATION_FAST_ALGORITHM_BIT)
+ {
+ const size_t blocksWithNonMovableCount = CalcBlocksWithNonMovableCount();
+ if(blocksWithNonMovableCount > 0)
+ {
+ srcBlockMinIndex = blocksWithNonMovableCount - 1;
+ }
+ }
+ */
+
+ size_t srcBlockIndex = m_Blocks.size() - 1;
+ size_t srcAllocIndex = SIZE_MAX;
+ for(;;)
+ {
+ // 1. Find next allocation to move.
+ // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
+ // 1.2. Then start from last to first m_Allocations.
+ while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
+ {
+ if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
+ {
+ // Finished: no more allocations to process.
+ if(srcBlockIndex == srcBlockMinIndex)
+ {
+ return VK_SUCCESS;
+ }
+ else
+ {
+ --srcBlockIndex;
+ srcAllocIndex = SIZE_MAX;
+ }
+ }
+ else
+ {
+ srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
+ }
+ }
+
+ BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
+ AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
+
+ const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
+ const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
+ const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
+ const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
+
+ // 2. Try to find new place for this allocation in preceding or current block.
+ for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
+ {
+ BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
+ VmaAllocationRequest dstAllocRequest;
+ if(pDstBlockInfo->m_pBlock->m_pMetadata->CreateAllocationRequest(
+ m_CurrentFrameIndex,
+ m_pBlockVector->GetFrameInUseCount(),
+ m_pBlockVector->GetBufferImageGranularity(),
+ size,
+ alignment,
+ false, // upperAddress
+ suballocType,
+ false, // canMakeOtherLost
+ strategy,
+ &dstAllocRequest) &&
+ MoveMakesSense(
+ dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
+ {
+ VMA_ASSERT(dstAllocRequest.itemsToMakeLostCount == 0);
+
+ // Reached limit on number of allocations or bytes to move.
+ if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
+ (m_BytesMoved + size > maxBytesToMove))
+ {
+ return VK_SUCCESS;
+ }
+
+ VmaDefragmentationMove move;
+ move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
+ move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
+ move.srcOffset = srcOffset;
+ move.dstOffset = dstAllocRequest.offset;
+ move.size = size;
+ moves.push_back(move);
+
+ pDstBlockInfo->m_pBlock->m_pMetadata->Alloc(
+ dstAllocRequest,
+ suballocType,
+ size,
+ false, // upperAddress
+ allocInfo.m_hAllocation);
+ pSrcBlockInfo->m_pBlock->m_pMetadata->FreeAtOffset(srcOffset);
+
+ allocInfo.m_hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
+
+ if(allocInfo.m_pChanged != VMA_NULL)
+ {
+ *allocInfo.m_pChanged = VK_TRUE;
+ }
+
+ ++m_AllocationsMoved;
+ m_BytesMoved += size;
+
+ VmaVectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
+
+ break;
+ }
+ }
+
+ // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
+
+ if(srcAllocIndex > 0)
+ {
+ --srcAllocIndex;
+ }
+ else
+ {
+ if(srcBlockIndex > 0)
+ {
+ --srcBlockIndex;
+ srcAllocIndex = SIZE_MAX;
+ }
+ else
+ {
+ return VK_SUCCESS;
+ }
+ }
+ }
+}
+
+size_t VmaDefragmentationAlgorithm_Generic::CalcBlocksWithNonMovableCount() const
+{
+ size_t result = 0;
+ for(size_t i = 0; i < m_Blocks.size(); ++i)
+ {
+ if(m_Blocks[i]->m_HasNonMovableAllocations)
+ {
+ ++result;
+ }
+ }
+ return result;
+}
+
+VkResult VmaDefragmentationAlgorithm_Generic::Defragment(
+ VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
+ VkDeviceSize maxBytesToMove,
+ uint32_t maxAllocationsToMove)
+{
+ if(!m_AllAllocations && m_AllocationCount == 0)
+ {
+ return VK_SUCCESS;
+ }
+
+ const size_t blockCount = m_Blocks.size();
+ for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
+ {
+ BlockInfo* pBlockInfo = m_Blocks[blockIndex];
+
+ if(m_AllAllocations)
+ {
+ VmaBlockMetadata_Generic* pMetadata = (VmaBlockMetadata_Generic*)pBlockInfo->m_pBlock->m_pMetadata;
+ for(VmaSuballocationList::const_iterator it = pMetadata->m_Suballocations.begin();
+ it != pMetadata->m_Suballocations.end();
+ ++it)
+ {
+ if(it->type != VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ AllocationInfo allocInfo = AllocationInfo(it->hAllocation, VMA_NULL);
+ pBlockInfo->m_Allocations.push_back(allocInfo);
+ }
+ }
+ }
+
+ pBlockInfo->CalcHasNonMovableAllocations();
+
+ // This is a choice based on research.
+ // Option 1:
+ pBlockInfo->SortAllocationsByOffsetDescending();
+ // Option 2:
+ //pBlockInfo->SortAllocationsBySizeDescending();
+ }
+
+ // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
+ VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
+
+ // This is a choice based on research.
+ const uint32_t roundCount = 2;
+
+ // Execute defragmentation rounds (the main part).
+ VkResult result = VK_SUCCESS;
+ for(uint32_t round = 0; (round < roundCount) && (result == VK_SUCCESS); ++round)
+ {
+ result = DefragmentRound(moves, maxBytesToMove, maxAllocationsToMove);
+ }
+
+ return result;
+}
+
+bool VmaDefragmentationAlgorithm_Generic::MoveMakesSense(
+ size_t dstBlockIndex, VkDeviceSize dstOffset,
+ size_t srcBlockIndex, VkDeviceSize srcOffset)
+{
+ if(dstBlockIndex < srcBlockIndex)
+ {
+ return true;
+ }
+ if(dstBlockIndex > srcBlockIndex)
+ {
+ return false;
+ }
+ if(dstOffset < srcOffset)
+ {
+ return true;
+ }
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// VmaDefragmentationAlgorithm_Fast
+
+VmaDefragmentationAlgorithm_Fast::VmaDefragmentationAlgorithm_Fast(
+ VmaAllocator hAllocator,
+ VmaBlockVector* pBlockVector,
+ uint32_t currentFrameIndex,
+ bool overlappingMoveSupported) :
+ VmaDefragmentationAlgorithm(hAllocator, pBlockVector, currentFrameIndex),
+ m_OverlappingMoveSupported(overlappingMoveSupported),
+ m_AllocationCount(0),
+ m_AllAllocations(false),
+ m_BytesMoved(0),
+ m_AllocationsMoved(0),
+ m_BlockInfos(VmaStlAllocator<BlockInfo>(hAllocator->GetAllocationCallbacks()))
+{
+ VMA_ASSERT(VMA_DEBUG_MARGIN == 0);
+
+}
+
+VmaDefragmentationAlgorithm_Fast::~VmaDefragmentationAlgorithm_Fast()
+{
+}
+
+VkResult VmaDefragmentationAlgorithm_Fast::Defragment(
+ VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
+ VkDeviceSize maxBytesToMove,
+ uint32_t maxAllocationsToMove)
+{
+ VMA_ASSERT(m_AllAllocations || m_pBlockVector->CalcAllocationCount() == m_AllocationCount);
+
+ const size_t blockCount = m_pBlockVector->GetBlockCount();
+ if(blockCount == 0 || maxBytesToMove == 0 || maxAllocationsToMove == 0)
+ {
+ return VK_SUCCESS;
+ }
+
+ PreprocessMetadata();
+
+ // Sort blocks in order from most destination.
+
+ m_BlockInfos.resize(blockCount);
+ for(size_t i = 0; i < blockCount; ++i)
+ {
+ m_BlockInfos[i].origBlockIndex = i;
+ }
+
+ VMA_SORT(m_BlockInfos.begin(), m_BlockInfos.end(), [this](const BlockInfo& lhs, const BlockInfo& rhs) -> bool {
+ return m_pBlockVector->GetBlock(lhs.origBlockIndex)->m_pMetadata->GetSumFreeSize() <
+ m_pBlockVector->GetBlock(rhs.origBlockIndex)->m_pMetadata->GetSumFreeSize();
+ });
+
+ // THE MAIN ALGORITHM
+
+ FreeSpaceDatabase freeSpaceDb;
+
+ size_t dstBlockInfoIndex = 0;
+ size_t dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
+ VmaDeviceMemoryBlock* pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
+ VmaBlockMetadata_Generic* pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
+ VkDeviceSize dstBlockSize = pDstMetadata->GetSize();
+ VkDeviceSize dstOffset = 0;
+
+ bool end = false;
+ for(size_t srcBlockInfoIndex = 0; !end && srcBlockInfoIndex < blockCount; ++srcBlockInfoIndex)
+ {
+ const size_t srcOrigBlockIndex = m_BlockInfos[srcBlockInfoIndex].origBlockIndex;
+ VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->GetBlock(srcOrigBlockIndex);
+ VmaBlockMetadata_Generic* const pSrcMetadata = (VmaBlockMetadata_Generic*)pSrcBlock->m_pMetadata;
+ for(VmaSuballocationList::iterator srcSuballocIt = pSrcMetadata->m_Suballocations.begin();
+ !end && srcSuballocIt != pSrcMetadata->m_Suballocations.end(); )
+ {
+ VmaAllocation_T* const pAlloc = srcSuballocIt->hAllocation;
+ const VkDeviceSize srcAllocAlignment = pAlloc->GetAlignment();
+ const VkDeviceSize srcAllocSize = srcSuballocIt->size;
+ if(m_AllocationsMoved == maxAllocationsToMove ||
+ m_BytesMoved + srcAllocSize > maxBytesToMove)
+ {
+ end = true;
+ break;
+ }
+ const VkDeviceSize srcAllocOffset = srcSuballocIt->offset;
+
+ // Try to place it in one of free spaces from the database.
+ size_t freeSpaceInfoIndex;
+ VkDeviceSize dstAllocOffset;
+ if(freeSpaceDb.Fetch(srcAllocAlignment, srcAllocSize,
+ freeSpaceInfoIndex, dstAllocOffset))
+ {
+ size_t freeSpaceOrigBlockIndex = m_BlockInfos[freeSpaceInfoIndex].origBlockIndex;
+ VmaDeviceMemoryBlock* pFreeSpaceBlock = m_pBlockVector->GetBlock(freeSpaceOrigBlockIndex);
+ VmaBlockMetadata_Generic* pFreeSpaceMetadata = (VmaBlockMetadata_Generic*)pFreeSpaceBlock->m_pMetadata;
+ /*VkDeviceSize freeSpaceBlockSize = pFreeSpaceMetadata->GetSize();*/
+
+ // Same block
+ if(freeSpaceInfoIndex == srcBlockInfoIndex)
+ {
+ VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
+
+ // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
+
+ VmaSuballocation suballoc = *srcSuballocIt;
+ suballoc.offset = dstAllocOffset;
+ suballoc.hAllocation->ChangeOffset(dstAllocOffset);
+ m_BytesMoved += srcAllocSize;
+ ++m_AllocationsMoved;
+
+ VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
+ ++nextSuballocIt;
+ pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
+ srcSuballocIt = nextSuballocIt;
+
+ InsertSuballoc(pFreeSpaceMetadata, suballoc);
+
+ VmaDefragmentationMove move = {
+ srcOrigBlockIndex, freeSpaceOrigBlockIndex,
+ srcAllocOffset, dstAllocOffset,
+ srcAllocSize };
+ moves.push_back(move);
+ }
+ // Different block
+ else
+ {
+ // MOVE OPTION 2: Move the allocation to a different block.
+
+ VMA_ASSERT(freeSpaceInfoIndex < srcBlockInfoIndex);
+
+ VmaSuballocation suballoc = *srcSuballocIt;
+ suballoc.offset = dstAllocOffset;
+ suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pFreeSpaceBlock, dstAllocOffset);
+ m_BytesMoved += srcAllocSize;
+ ++m_AllocationsMoved;
+
+ VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
+ ++nextSuballocIt;
+ pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
+ srcSuballocIt = nextSuballocIt;
+
+ InsertSuballoc(pFreeSpaceMetadata, suballoc);
+
+ VmaDefragmentationMove move = {
+ srcOrigBlockIndex, freeSpaceOrigBlockIndex,
+ srcAllocOffset, dstAllocOffset,
+ srcAllocSize };
+ moves.push_back(move);
+ }
+ }
+ else
+ {
+ dstAllocOffset = VmaAlignUp(dstOffset, srcAllocAlignment);
+
+ // If the allocation doesn't fit before the end of dstBlock, forward to next block.
+ while(dstBlockInfoIndex < srcBlockInfoIndex &&
+ dstAllocOffset + srcAllocSize > dstBlockSize)
+ {
+ // But before that, register remaining free space at the end of dst block.
+ freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, dstBlockSize - dstOffset);
+
+ ++dstBlockInfoIndex;
+ dstOrigBlockIndex = m_BlockInfos[dstBlockInfoIndex].origBlockIndex;
+ pDstBlock = m_pBlockVector->GetBlock(dstOrigBlockIndex);
+ pDstMetadata = (VmaBlockMetadata_Generic*)pDstBlock->m_pMetadata;
+ dstBlockSize = pDstMetadata->GetSize();
+ dstOffset = 0;
+ dstAllocOffset = 0;
+ }
+
+ // Same block
+ if(dstBlockInfoIndex == srcBlockInfoIndex)
+ {
+ VMA_ASSERT(dstAllocOffset <= srcAllocOffset);
+
+ const bool overlap = dstAllocOffset + srcAllocSize > srcAllocOffset;
+
+ bool skipOver = overlap;
+ if(overlap && m_OverlappingMoveSupported && dstAllocOffset < srcAllocOffset)
+ {
+ // If destination and source place overlap, skip if it would move it
+ // by only < 1/64 of its size.
+ skipOver = (srcAllocOffset - dstAllocOffset) * 64 < srcAllocSize;
+ }
+
+ if(skipOver)
+ {
+ freeSpaceDb.Register(dstBlockInfoIndex, dstOffset, srcAllocOffset - dstOffset);
+
+ dstOffset = srcAllocOffset + srcAllocSize;
+ ++srcSuballocIt;
+ }
+ // MOVE OPTION 1: Move the allocation inside the same block by decreasing offset.
+ else
+ {
+ srcSuballocIt->offset = dstAllocOffset;
+ srcSuballocIt->hAllocation->ChangeOffset(dstAllocOffset);
+ dstOffset = dstAllocOffset + srcAllocSize;
+ m_BytesMoved += srcAllocSize;
+ ++m_AllocationsMoved;
+ ++srcSuballocIt;
+ VmaDefragmentationMove move = {
+ srcOrigBlockIndex, dstOrigBlockIndex,
+ srcAllocOffset, dstAllocOffset,
+ srcAllocSize };
+ moves.push_back(move);
+ }
+ }
+ // Different block
+ else
+ {
+ // MOVE OPTION 2: Move the allocation to a different block.
+
+ VMA_ASSERT(dstBlockInfoIndex < srcBlockInfoIndex);
+ VMA_ASSERT(dstAllocOffset + srcAllocSize <= dstBlockSize);
+
+ VmaSuballocation suballoc = *srcSuballocIt;
+ suballoc.offset = dstAllocOffset;
+ suballoc.hAllocation->ChangeBlockAllocation(m_hAllocator, pDstBlock, dstAllocOffset);
+ dstOffset = dstAllocOffset + srcAllocSize;
+ m_BytesMoved += srcAllocSize;
+ ++m_AllocationsMoved;
+
+ VmaSuballocationList::iterator nextSuballocIt = srcSuballocIt;
+ ++nextSuballocIt;
+ pSrcMetadata->m_Suballocations.erase(srcSuballocIt);
+ srcSuballocIt = nextSuballocIt;
+
+ pDstMetadata->m_Suballocations.push_back(suballoc);
+
+ VmaDefragmentationMove move = {
+ srcOrigBlockIndex, dstOrigBlockIndex,
+ srcAllocOffset, dstAllocOffset,
+ srcAllocSize };
+ moves.push_back(move);
+ }
+ }
+ }
+ }
+
+ m_BlockInfos.clear();
+
+ PostprocessMetadata();
+
+ return VK_SUCCESS;
+}
+
+void VmaDefragmentationAlgorithm_Fast::PreprocessMetadata()
+{
+ const size_t blockCount = m_pBlockVector->GetBlockCount();
+ for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
+ {
+ VmaBlockMetadata_Generic* const pMetadata =
+ (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
+ pMetadata->m_FreeCount = 0;
+ pMetadata->m_SumFreeSize = pMetadata->GetSize();
+ pMetadata->m_FreeSuballocationsBySize.clear();
+ for(VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
+ it != pMetadata->m_Suballocations.end(); )
+ {
+ if(it->type == VMA_SUBALLOCATION_TYPE_FREE)
+ {
+ VmaSuballocationList::iterator nextIt = it;
+ ++nextIt;
+ pMetadata->m_Suballocations.erase(it);
+ it = nextIt;
+ }
+ else
+ {
+ ++it;
+ }
+ }
+ }
+}
+
+void VmaDefragmentationAlgorithm_Fast::PostprocessMetadata()
+{
+ const size_t blockCount = m_pBlockVector->GetBlockCount();
+ for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
+ {
+ VmaBlockMetadata_Generic* const pMetadata =
+ (VmaBlockMetadata_Generic*)m_pBlockVector->GetBlock(blockIndex)->m_pMetadata;
+ const VkDeviceSize blockSize = pMetadata->GetSize();
+
+ // No allocations in this block - entire area is free.
+ if(pMetadata->m_Suballocations.empty())
+ {
+ pMetadata->m_FreeCount = 1;
+ //pMetadata->m_SumFreeSize is already set to blockSize.
+ VmaSuballocation suballoc = {
+ 0, // offset
+ blockSize, // size
+ VMA_NULL, // hAllocation
+ VMA_SUBALLOCATION_TYPE_FREE };
+ pMetadata->m_Suballocations.push_back(suballoc);
+ pMetadata->RegisterFreeSuballocation(pMetadata->m_Suballocations.begin());
+ }
+ // There are some allocations in this block.
+ else
+ {
+ VkDeviceSize offset = 0;
+ VmaSuballocationList::iterator it;
+ for(it = pMetadata->m_Suballocations.begin();
+ it != pMetadata->m_Suballocations.end();
+ ++it)
+ {
+ VMA_ASSERT(it->type != VMA_SUBALLOCATION_TYPE_FREE);
+ VMA_ASSERT(it->offset >= offset);
+
+ // Need to insert preceding free space.
+ if(it->offset > offset)
+ {
+ ++pMetadata->m_FreeCount;
+ const VkDeviceSize freeSize = it->offset - offset;
+ VmaSuballocation suballoc = {
+ offset, // offset
+ freeSize, // size
+ VMA_NULL, // hAllocation
+ VMA_SUBALLOCATION_TYPE_FREE };
+ VmaSuballocationList::iterator precedingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
+ if(freeSize >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
+ {
+ pMetadata->m_FreeSuballocationsBySize.push_back(precedingFreeIt);
+ }
+ }
+
+ pMetadata->m_SumFreeSize -= it->size;
+ offset = it->offset + it->size;
+ }
+
+ // Need to insert trailing free space.
+ if(offset < blockSize)
+ {
+ ++pMetadata->m_FreeCount;
+ const VkDeviceSize freeSize = blockSize - offset;
+ VmaSuballocation suballoc = {
+ offset, // offset
+ freeSize, // size
+ VMA_NULL, // hAllocation
+ VMA_SUBALLOCATION_TYPE_FREE };
+ VMA_ASSERT(it == pMetadata->m_Suballocations.end());
+ VmaSuballocationList::iterator trailingFreeIt = pMetadata->m_Suballocations.insert(it, suballoc);
+ if(freeSize > VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
+ {
+ pMetadata->m_FreeSuballocationsBySize.push_back(trailingFreeIt);
+ }
+ }
+
+ VMA_SORT(
+ pMetadata->m_FreeSuballocationsBySize.begin(),
+ pMetadata->m_FreeSuballocationsBySize.end(),
+ VmaSuballocationItemSizeLess());
+ }
+
+ VMA_HEAVY_ASSERT(pMetadata->Validate());
+ }
+}
+
+void VmaDefragmentationAlgorithm_Fast::InsertSuballoc(VmaBlockMetadata_Generic* pMetadata, const VmaSuballocation& suballoc)
+{
+ // TODO: Optimize somehow. Remember iterator instead of searching for it linearly.
+ VmaSuballocationList::iterator it = pMetadata->m_Suballocations.begin();
+ while(it != pMetadata->m_Suballocations.end())
+ {
+ if(it->offset < suballoc.offset)
+ {
+ ++it;
+ }
+ }
+ pMetadata->m_Suballocations.insert(it, suballoc);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// VmaBlockVectorDefragmentationContext
+
+VmaBlockVectorDefragmentationContext::VmaBlockVectorDefragmentationContext(
+ VmaAllocator hAllocator,
+ VmaPool hCustomPool,
+ VmaBlockVector* pBlockVector,
+ uint32_t currFrameIndex,
+ uint32_t /*algorithmFlags*/) :
+ res(VK_SUCCESS),
+ mutexLocked(false),
+ blockContexts(VmaStlAllocator<VmaBlockDefragmentationContext>(hAllocator->GetAllocationCallbacks())),
+ m_hAllocator(hAllocator),
+ m_hCustomPool(hCustomPool),
+ m_pBlockVector(pBlockVector),
+ m_CurrFrameIndex(currFrameIndex),
+ /*m_AlgorithmFlags(algorithmFlags),*/
+ m_pAlgorithm(VMA_NULL),
+ m_Allocations(VmaStlAllocator<AllocInfo>(hAllocator->GetAllocationCallbacks())),
+ m_AllAllocations(false)
+{
+}
+
+VmaBlockVectorDefragmentationContext::~VmaBlockVectorDefragmentationContext()
+{
+ vma_delete(m_hAllocator, m_pAlgorithm);
+}
+
+void VmaBlockVectorDefragmentationContext::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
+{
+ AllocInfo info = { hAlloc, pChanged };
+ m_Allocations.push_back(info);
+}
+
+void VmaBlockVectorDefragmentationContext::Begin(bool overlappingMoveSupported)
+{
+ const bool allAllocations = m_AllAllocations ||
+ m_Allocations.size() == m_pBlockVector->CalcAllocationCount();
+
+ /********************************
+ HERE IS THE CHOICE OF DEFRAGMENTATION ALGORITHM.
+ ********************************/
+
+ /*
+ Fast algorithm is supported only when certain criteria are met:
+ - VMA_DEBUG_MARGIN is 0.
+ - All allocations in this block vector are moveable.
+ - There is no possibility of image/buffer granularity conflict.
+ */
+ if(VMA_DEBUG_MARGIN == 0 &&
+ allAllocations &&
+ !m_pBlockVector->IsBufferImageGranularityConflictPossible())
+ {
+ m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Fast)(
+ m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
+ }
+ else
+ {
+ m_pAlgorithm = vma_new(m_hAllocator, VmaDefragmentationAlgorithm_Generic)(
+ m_hAllocator, m_pBlockVector, m_CurrFrameIndex, overlappingMoveSupported);
+ }
+
+ if(allAllocations)
+ {
+ m_pAlgorithm->AddAll();
+ }
+ else
+ {
+ for(size_t i = 0, count = m_Allocations.size(); i < count; ++i)
+ {
+ m_pAlgorithm->AddAllocation(m_Allocations[i].hAlloc, m_Allocations[i].pChanged);
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// VmaDefragmentationContext
+
+VmaDefragmentationContext_T::VmaDefragmentationContext_T(
+ VmaAllocator hAllocator,
+ uint32_t currFrameIndex,
+ uint32_t flags,
+ VmaDefragmentationStats* pStats) :
+ m_hAllocator(hAllocator),
+ m_CurrFrameIndex(currFrameIndex),
+ m_Flags(flags),
+ m_pStats(pStats),
+ m_CustomPoolContexts(VmaStlAllocator<VmaBlockVectorDefragmentationContext*>(hAllocator->GetAllocationCallbacks()))
+{
+ memset(m_DefaultPoolContexts, 0, sizeof(m_DefaultPoolContexts));
+}
+
+VmaDefragmentationContext_T::~VmaDefragmentationContext_T()
+{
+ for(size_t i = m_CustomPoolContexts.size(); i--; )
+ {
+ VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[i];
+ pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_pStats);
+ vma_delete(m_hAllocator, pBlockVectorCtx);
+ }
+ for(size_t i = m_hAllocator->m_MemProps.memoryTypeCount; i--; )
+ {
+ VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[i];
+ if(pBlockVectorCtx)
+ {
+ pBlockVectorCtx->GetBlockVector()->DefragmentationEnd(pBlockVectorCtx, m_pStats);
+ vma_delete(m_hAllocator, pBlockVectorCtx);
+ }
+ }
+}
+
+void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, VmaPool* pPools)
+{
+ for(uint32_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
+ {
+ VmaPool pool = pPools[poolIndex];
+ VMA_ASSERT(pool);
+ // Pools with algorithm other than default are not defragmented.
+ if(pool->m_BlockVector.GetAlgorithm() == 0)
+ {
+ VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
+
+ for(size_t i = m_CustomPoolContexts.size(); i--; )
+ {
+ if(m_CustomPoolContexts[i]->GetCustomPool() == pool)
+ {
+ pBlockVectorDefragCtx = m_CustomPoolContexts[i];
+ break;
+ }
+ }
+
+ if(!pBlockVectorDefragCtx)
+ {
+ pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
+ m_hAllocator,
+ pool,
+ &pool->m_BlockVector,
+ m_CurrFrameIndex,
+ m_Flags);
+ m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
+ }
+
+ pBlockVectorDefragCtx->AddAll();
+ }
+ }
+}
+
+void VmaDefragmentationContext_T::AddAllocations(
+ uint32_t allocationCount,
+ VmaAllocation* pAllocations,
+ VkBool32* pAllocationsChanged)
+{
+ // Dispatch pAllocations among defragmentators. Create them when necessary.
+ for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
+ {
+ const VmaAllocation hAlloc = pAllocations[allocIndex];
+ VMA_ASSERT(hAlloc);
+ // DedicatedAlloc cannot be defragmented.
+ if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
+ // Lost allocation cannot be defragmented.
+ (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
+ {
+ VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
+
+ const VmaPool hAllocPool = hAlloc->GetPool();
+ // This allocation belongs to custom pool.
+ if(hAllocPool != VK_NULL_HANDLE)
+ {
+ // Pools with algorithm other than default are not defragmented.
+ if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
+ {
+ for(size_t i = m_CustomPoolContexts.size(); i--; )
+ {
+ if(m_CustomPoolContexts[i]->GetCustomPool() == hAllocPool)
+ {
+ pBlockVectorDefragCtx = m_CustomPoolContexts[i];
+ break;
+ }
+ }
+ if(!pBlockVectorDefragCtx)
+ {
+ pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
+ m_hAllocator,
+ hAllocPool,
+ &hAllocPool->m_BlockVector,
+ m_CurrFrameIndex,
+ m_Flags);
+ m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
+ }
+ }
+ }
+ // This allocation belongs to default pool.
+ else
+ {
+ const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
+ pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
+ if(!pBlockVectorDefragCtx)
+ {
+ pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
+ m_hAllocator,
+ VMA_NULL, // hCustomPool
+ m_hAllocator->m_pBlockVectors[memTypeIndex],
+ m_CurrFrameIndex,
+ m_Flags);
+ m_DefaultPoolContexts[memTypeIndex] = pBlockVectorDefragCtx;
+ }
+ }
+
+ if(pBlockVectorDefragCtx)
+ {
+ VkBool32* const pChanged = (pAllocationsChanged != VMA_NULL) ?
+ &pAllocationsChanged[allocIndex] : VMA_NULL;
+ pBlockVectorDefragCtx->AddAllocation(hAlloc, pChanged);
+ }
+ }
+ }
+}
+
+VkResult VmaDefragmentationContext_T::Defragment(
+ VkDeviceSize maxCpuBytesToMove, uint32_t maxCpuAllocationsToMove,
+ VkDeviceSize maxGpuBytesToMove, uint32_t maxGpuAllocationsToMove,
+ VkCommandBuffer commandBuffer, VmaDefragmentationStats* pStats)
+{
+ if(pStats)
+ {
+ memset(pStats, 0, sizeof(VmaDefragmentationStats));
+ }
+
+ if(commandBuffer == VK_NULL_HANDLE)
+ {
+ maxGpuBytesToMove = 0;
+ maxGpuAllocationsToMove = 0;
+ }
+
+ VkResult res = VK_SUCCESS;
+
+ // Process default pools.
+ for(uint32_t memTypeIndex = 0;
+ memTypeIndex < m_hAllocator->GetMemoryTypeCount() && res >= VK_SUCCESS;
+ ++memTypeIndex)
+ {
+ VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_DefaultPoolContexts[memTypeIndex];
+ if(pBlockVectorCtx)
+ {
+ VMA_ASSERT(pBlockVectorCtx->GetBlockVector());
+ pBlockVectorCtx->GetBlockVector()->Defragment(
+ pBlockVectorCtx,
+ pStats,
+ maxCpuBytesToMove, maxCpuAllocationsToMove,
+ maxGpuBytesToMove, maxGpuAllocationsToMove,
+ commandBuffer);
+ if(pBlockVectorCtx->res != VK_SUCCESS)
+ {
+ res = pBlockVectorCtx->res;
+ }
+ }
+ }
+
+ // Process custom pools.
+ for(size_t customCtxIndex = 0, customCtxCount = m_CustomPoolContexts.size();
+ customCtxIndex < customCtxCount && res >= VK_SUCCESS;
+ ++customCtxIndex)
+ {
+ VmaBlockVectorDefragmentationContext* pBlockVectorCtx = m_CustomPoolContexts[customCtxIndex];
+ VMA_ASSERT(pBlockVectorCtx && pBlockVectorCtx->GetBlockVector());
+ pBlockVectorCtx->GetBlockVector()->Defragment(
+ pBlockVectorCtx,
+ pStats,
+ maxCpuBytesToMove, maxCpuAllocationsToMove,
+ maxGpuBytesToMove, maxGpuAllocationsToMove,
+ commandBuffer);
+ if(pBlockVectorCtx->res != VK_SUCCESS)
+ {
+ res = pBlockVectorCtx->res;
+ }
+ }
+
+ return res;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// VmaRecorder
+
+#if VMA_RECORDING_ENABLED
+
+VmaRecorder::VmaRecorder() :
+ m_UseMutex(true),
+ m_Flags(0),
+ m_File(VMA_NULL),
+ m_Freq(INT64_MAX),
+ m_StartCounter(INT64_MAX)
+{
+}
+
+VkResult VmaRecorder::Init(const VmaRecordSettings& settings, bool useMutex)
+{
+ m_UseMutex = useMutex;
+ m_Flags = settings.flags;
+
+ QueryPerformanceFrequency((LARGE_INTEGER*)&m_Freq);
+ QueryPerformanceCounter((LARGE_INTEGER*)&m_StartCounter);
+
+ // Open file for writing.
+ errno_t err = fopen_s(&m_File, settings.pFilePath, "wb");
+ if(err != 0)
+ {
+ return VK_ERROR_INITIALIZATION_FAILED;
+ }
+
+ // Write header.
+ fprintf(m_File, "%s\n", "Vulkan Memory Allocator,Calls recording");
+ fprintf(m_File, "%s\n", "1,5");
+
+ return VK_SUCCESS;
+}
+
+VmaRecorder::~VmaRecorder()
+{
+ if(m_File != VMA_NULL)
+ {
+ fclose(m_File);
+ }
+}
+
+void VmaRecorder::RecordCreateAllocator(uint32_t frameIndex)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaCreateAllocator\n", callParams.threadId, callParams.time, frameIndex);
+ Flush();
+}
+
+void VmaRecorder::RecordDestroyAllocator(uint32_t frameIndex)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaDestroyAllocator\n", callParams.threadId, callParams.time, frameIndex);
+ Flush();
+}
+
+void VmaRecorder::RecordCreatePool(uint32_t frameIndex, const VmaPoolCreateInfo& createInfo, VmaPool pool)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaCreatePool,%u,%u,%llu,%llu,%llu,%u,%p\n", callParams.threadId, callParams.time, frameIndex,
+ createInfo.memoryTypeIndex,
+ createInfo.flags,
+ createInfo.blockSize,
+ (uint64_t)createInfo.minBlockCount,
+ (uint64_t)createInfo.maxBlockCount,
+ createInfo.frameInUseCount,
+ pool);
+ Flush();
+}
+
+void VmaRecorder::RecordDestroyPool(uint32_t frameIndex, VmaPool pool)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaDestroyPool,%p\n", callParams.threadId, callParams.time, frameIndex,
+ pool);
+ Flush();
+}
+
+void VmaRecorder::RecordAllocateMemory(uint32_t frameIndex,
+ const VkMemoryRequirements& vkMemReq,
+ const VmaAllocationCreateInfo& createInfo,
+ VmaAllocation allocation)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
+ fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemory,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
+ vkMemReq.size,
+ vkMemReq.alignment,
+ vkMemReq.memoryTypeBits,
+ createInfo.flags,
+ createInfo.usage,
+ createInfo.requiredFlags,
+ createInfo.preferredFlags,
+ createInfo.memoryTypeBits,
+ createInfo.pool,
+ allocation,
+ userDataStr.GetString());
+ Flush();
+}
+
+void VmaRecorder::RecordAllocateMemoryPages(uint32_t frameIndex,
+ const VkMemoryRequirements& vkMemReq,
+ const VmaAllocationCreateInfo& createInfo,
+ uint64_t allocationCount,
+ const VmaAllocation* pAllocations)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
+ fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryPages,%llu,%llu,%u,%u,%u,%u,%u,%u,%p,", callParams.threadId, callParams.time, frameIndex,
+ vkMemReq.size,
+ vkMemReq.alignment,
+ vkMemReq.memoryTypeBits,
+ createInfo.flags,
+ createInfo.usage,
+ createInfo.requiredFlags,
+ createInfo.preferredFlags,
+ createInfo.memoryTypeBits,
+ createInfo.pool);
+ PrintPointerList(allocationCount, pAllocations);
+ fprintf(m_File, ",%s\n", userDataStr.GetString());
+ Flush();
+}
+
+void VmaRecorder::RecordAllocateMemoryForBuffer(uint32_t frameIndex,
+ const VkMemoryRequirements& vkMemReq,
+ bool requiresDedicatedAllocation,
+ bool prefersDedicatedAllocation,
+ const VmaAllocationCreateInfo& createInfo,
+ VmaAllocation allocation)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
+ fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryForBuffer,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
+ vkMemReq.size,
+ vkMemReq.alignment,
+ vkMemReq.memoryTypeBits,
+ requiresDedicatedAllocation ? 1 : 0,
+ prefersDedicatedAllocation ? 1 : 0,
+ createInfo.flags,
+ createInfo.usage,
+ createInfo.requiredFlags,
+ createInfo.preferredFlags,
+ createInfo.memoryTypeBits,
+ createInfo.pool,
+ allocation,
+ userDataStr.GetString());
+ Flush();
+}
+
+void VmaRecorder::RecordAllocateMemoryForImage(uint32_t frameIndex,
+ const VkMemoryRequirements& vkMemReq,
+ bool requiresDedicatedAllocation,
+ bool prefersDedicatedAllocation,
+ const VmaAllocationCreateInfo& createInfo,
+ VmaAllocation allocation)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ UserDataString userDataStr(createInfo.flags, createInfo.pUserData);
+ fprintf(m_File, "%u,%.3f,%u,vmaAllocateMemoryForImage,%llu,%llu,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
+ vkMemReq.size,
+ vkMemReq.alignment,
+ vkMemReq.memoryTypeBits,
+ requiresDedicatedAllocation ? 1 : 0,
+ prefersDedicatedAllocation ? 1 : 0,
+ createInfo.flags,
+ createInfo.usage,
+ createInfo.requiredFlags,
+ createInfo.preferredFlags,
+ createInfo.memoryTypeBits,
+ createInfo.pool,
+ allocation,
+ userDataStr.GetString());
+ Flush();
+}
+
+void VmaRecorder::RecordFreeMemory(uint32_t frameIndex,
+ VmaAllocation allocation)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaFreeMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
+ allocation);
+ Flush();
+}
+
+void VmaRecorder::RecordFreeMemoryPages(uint32_t frameIndex,
+ uint64_t allocationCount,
+ const VmaAllocation* pAllocations)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaFreeMemoryPages,", callParams.threadId, callParams.time, frameIndex);
+ PrintPointerList(allocationCount, pAllocations);
+ fprintf(m_File, "\n");
+ Flush();
+}
+
+void VmaRecorder::RecordResizeAllocation(
+ uint32_t frameIndex,
+ VmaAllocation allocation,
+ VkDeviceSize newSize)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaResizeAllocation,%p,%llu\n", callParams.threadId, callParams.time, frameIndex,
+ allocation, newSize);
+ Flush();
+}
+
+void VmaRecorder::RecordSetAllocationUserData(uint32_t frameIndex,
+ VmaAllocation allocation,
+ const void* pUserData)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ UserDataString userDataStr(
+ allocation->IsUserDataString() ? VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT : 0,
+ pUserData);
+ fprintf(m_File, "%u,%.3f,%u,vmaSetAllocationUserData,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
+ allocation,
+ userDataStr.GetString());
+ Flush();
+}
+
+void VmaRecorder::RecordCreateLostAllocation(uint32_t frameIndex,
+ VmaAllocation allocation)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaCreateLostAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
+ allocation);
+ Flush();
+}
+
+void VmaRecorder::RecordMapMemory(uint32_t frameIndex,
+ VmaAllocation allocation)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaMapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
+ allocation);
+ Flush();
+}
+
+void VmaRecorder::RecordUnmapMemory(uint32_t frameIndex,
+ VmaAllocation allocation)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaUnmapMemory,%p\n", callParams.threadId, callParams.time, frameIndex,
+ allocation);
+ Flush();
+}
+
+void VmaRecorder::RecordFlushAllocation(uint32_t frameIndex,
+ VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaFlushAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
+ allocation,
+ offset,
+ size);
+ Flush();
+}
+
+void VmaRecorder::RecordInvalidateAllocation(uint32_t frameIndex,
+ VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaInvalidateAllocation,%p,%llu,%llu\n", callParams.threadId, callParams.time, frameIndex,
+ allocation,
+ offset,
+ size);
+ Flush();
+}
+
+void VmaRecorder::RecordCreateBuffer(uint32_t frameIndex,
+ const VkBufferCreateInfo& bufCreateInfo,
+ const VmaAllocationCreateInfo& allocCreateInfo,
+ VmaAllocation allocation)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
+ fprintf(m_File, "%u,%.3f,%u,vmaCreateBuffer,%u,%llu,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
+ bufCreateInfo.flags,
+ bufCreateInfo.size,
+ bufCreateInfo.usage,
+ bufCreateInfo.sharingMode,
+ allocCreateInfo.flags,
+ allocCreateInfo.usage,
+ allocCreateInfo.requiredFlags,
+ allocCreateInfo.preferredFlags,
+ allocCreateInfo.memoryTypeBits,
+ allocCreateInfo.pool,
+ allocation,
+ userDataStr.GetString());
+ Flush();
+}
+
+void VmaRecorder::RecordCreateImage(uint32_t frameIndex,
+ const VkImageCreateInfo& imageCreateInfo,
+ const VmaAllocationCreateInfo& allocCreateInfo,
+ VmaAllocation allocation)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ UserDataString userDataStr(allocCreateInfo.flags, allocCreateInfo.pUserData);
+ fprintf(m_File, "%u,%.3f,%u,vmaCreateImage,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%p,%p,%s\n", callParams.threadId, callParams.time, frameIndex,
+ imageCreateInfo.flags,
+ imageCreateInfo.imageType,
+ imageCreateInfo.format,
+ imageCreateInfo.extent.width,
+ imageCreateInfo.extent.height,
+ imageCreateInfo.extent.depth,
+ imageCreateInfo.mipLevels,
+ imageCreateInfo.arrayLayers,
+ imageCreateInfo.samples,
+ imageCreateInfo.tiling,
+ imageCreateInfo.usage,
+ imageCreateInfo.sharingMode,
+ imageCreateInfo.initialLayout,
+ allocCreateInfo.flags,
+ allocCreateInfo.usage,
+ allocCreateInfo.requiredFlags,
+ allocCreateInfo.preferredFlags,
+ allocCreateInfo.memoryTypeBits,
+ allocCreateInfo.pool,
+ allocation,
+ userDataStr.GetString());
+ Flush();
+}
+
+void VmaRecorder::RecordDestroyBuffer(uint32_t frameIndex,
+ VmaAllocation allocation)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaDestroyBuffer,%p\n", callParams.threadId, callParams.time, frameIndex,
+ allocation);
+ Flush();
+}
+
+void VmaRecorder::RecordDestroyImage(uint32_t frameIndex,
+ VmaAllocation allocation)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaDestroyImage,%p\n", callParams.threadId, callParams.time, frameIndex,
+ allocation);
+ Flush();
+}
+
+void VmaRecorder::RecordTouchAllocation(uint32_t frameIndex,
+ VmaAllocation allocation)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaTouchAllocation,%p\n", callParams.threadId, callParams.time, frameIndex,
+ allocation);
+ Flush();
+}
+
+void VmaRecorder::RecordGetAllocationInfo(uint32_t frameIndex,
+ VmaAllocation allocation)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaGetAllocationInfo,%p\n", callParams.threadId, callParams.time, frameIndex,
+ allocation);
+ Flush();
+}
+
+void VmaRecorder::RecordMakePoolAllocationsLost(uint32_t frameIndex,
+ VmaPool pool)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaMakePoolAllocationsLost,%p\n", callParams.threadId, callParams.time, frameIndex,
+ pool);
+ Flush();
+}
+
+void VmaRecorder::RecordDefragmentationBegin(uint32_t frameIndex,
+ const VmaDefragmentationInfo2& info,
+ VmaDefragmentationContext ctx)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationBegin,%u,", callParams.threadId, callParams.time, frameIndex,
+ info.flags);
+ PrintPointerList(info.allocationCount, info.pAllocations);
+ fprintf(m_File, ",");
+ PrintPointerList(info.poolCount, info.pPools);
+ fprintf(m_File, ",%llu,%u,%llu,%u,%p,%p\n",
+ info.maxCpuBytesToMove,
+ info.maxCpuAllocationsToMove,
+ info.maxGpuBytesToMove,
+ info.maxGpuAllocationsToMove,
+ info.commandBuffer,
+ ctx);
+ Flush();
+}
+
+void VmaRecorder::RecordDefragmentationEnd(uint32_t frameIndex,
+ VmaDefragmentationContext ctx)
+{
+ CallParams callParams;
+ GetBasicParams(callParams);
+
+ VmaMutexLock lock(m_FileMutex, m_UseMutex);
+ fprintf(m_File, "%u,%.3f,%u,vmaDefragmentationEnd,%p\n", callParams.threadId, callParams.time, frameIndex,
+ ctx);
+ Flush();
+}
+
+VmaRecorder::UserDataString::UserDataString(VmaAllocationCreateFlags allocFlags, const void* pUserData)
+{
+ if(pUserData != VMA_NULL)
+ {
+ if((allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0)
+ {
+ m_Str = (const char*)pUserData;
+ }
+ else
+ {
+ sprintf_s(m_PtrStr, "%p", pUserData);
+ m_Str = m_PtrStr;
+ }
+ }
+ else
+ {
+ m_Str = "";
+ }
+}
+
+void VmaRecorder::WriteConfiguration(
+ const VkPhysicalDeviceProperties& devProps,
+ const VkPhysicalDeviceMemoryProperties& memProps,
+ bool dedicatedAllocationExtensionEnabled)
+{
+ fprintf(m_File, "Config,Begin\n");
+
+ fprintf(m_File, "PhysicalDevice,apiVersion,%u\n", devProps.apiVersion);
+ fprintf(m_File, "PhysicalDevice,driverVersion,%u\n", devProps.driverVersion);
+ fprintf(m_File, "PhysicalDevice,vendorID,%u\n", devProps.vendorID);
+ fprintf(m_File, "PhysicalDevice,deviceID,%u\n", devProps.deviceID);
+ fprintf(m_File, "PhysicalDevice,deviceType,%u\n", devProps.deviceType);
+ fprintf(m_File, "PhysicalDevice,deviceName,%s\n", devProps.deviceName);
+
+ fprintf(m_File, "PhysicalDeviceLimits,maxMemoryAllocationCount,%u\n", devProps.limits.maxMemoryAllocationCount);
+ fprintf(m_File, "PhysicalDeviceLimits,bufferImageGranularity,%llu\n", devProps.limits.bufferImageGranularity);
+ fprintf(m_File, "PhysicalDeviceLimits,nonCoherentAtomSize,%llu\n", devProps.limits.nonCoherentAtomSize);
+
+ fprintf(m_File, "PhysicalDeviceMemory,HeapCount,%u\n", memProps.memoryHeapCount);
+ for(uint32_t i = 0; i < memProps.memoryHeapCount; ++i)
+ {
+ fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,size,%llu\n", i, memProps.memoryHeaps[i].size);
+ fprintf(m_File, "PhysicalDeviceMemory,Heap,%u,flags,%u\n", i, memProps.memoryHeaps[i].flags);
+ }
+ fprintf(m_File, "PhysicalDeviceMemory,TypeCount,%u\n", memProps.memoryTypeCount);
+ for(uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
+ {
+ fprintf(m_File, "PhysicalDeviceMemory,Type,%u,heapIndex,%u\n", i, memProps.memoryTypes[i].heapIndex);
+ fprintf(m_File, "PhysicalDeviceMemory,Type,%u,propertyFlags,%u\n", i, memProps.memoryTypes[i].propertyFlags);
+ }
+
+ fprintf(m_File, "Extension,VK_KHR_dedicated_allocation,%u\n", dedicatedAllocationExtensionEnabled ? 1 : 0);
+
+ fprintf(m_File, "Macro,VMA_DEBUG_ALWAYS_DEDICATED_MEMORY,%u\n", VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ? 1 : 0);
+ fprintf(m_File, "Macro,VMA_DEBUG_ALIGNMENT,%llu\n", (VkDeviceSize)VMA_DEBUG_ALIGNMENT);
+ fprintf(m_File, "Macro,VMA_DEBUG_MARGIN,%llu\n", (VkDeviceSize)VMA_DEBUG_MARGIN);
+ fprintf(m_File, "Macro,VMA_DEBUG_INITIALIZE_ALLOCATIONS,%u\n", VMA_DEBUG_INITIALIZE_ALLOCATIONS ? 1 : 0);
+ fprintf(m_File, "Macro,VMA_DEBUG_DETECT_CORRUPTION,%u\n", VMA_DEBUG_DETECT_CORRUPTION ? 1 : 0);
+ fprintf(m_File, "Macro,VMA_DEBUG_GLOBAL_MUTEX,%u\n", VMA_DEBUG_GLOBAL_MUTEX ? 1 : 0);
+ fprintf(m_File, "Macro,VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,%llu\n", (VkDeviceSize)VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY);
+ fprintf(m_File, "Macro,VMA_SMALL_HEAP_MAX_SIZE,%llu\n", (VkDeviceSize)VMA_SMALL_HEAP_MAX_SIZE);
+ fprintf(m_File, "Macro,VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE,%llu\n", (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
+
+ fprintf(m_File, "Config,End\n");
+}
+
+void VmaRecorder::GetBasicParams(CallParams& outParams)
+{
+ outParams.threadId = GetCurrentThreadId();
+
+ LARGE_INTEGER counter;
+ QueryPerformanceCounter(&counter);
+ outParams.time = (double)(counter.QuadPart - m_StartCounter) / (double)m_Freq;
+}
+
+void VmaRecorder::PrintPointerList(uint64_t count, const VmaAllocation* pItems)
+{
+ if(count)
+ {
+ fprintf(m_File, "%p", pItems[0]);
+ for(uint64_t i = 1; i < count; ++i)
+ {
+ fprintf(m_File, " %p", pItems[i]);
+ }
+ }
+}
+
+void VmaRecorder::Flush()
+{
+ if((m_Flags & VMA_RECORD_FLUSH_AFTER_CALL_BIT) != 0)
+ {
+ fflush(m_File);
+ }
+}
+
+#endif // #if VMA_RECORDING_ENABLED
+
+////////////////////////////////////////////////////////////////////////////////
+// VmaAllocator_T
+
+VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
+ m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
+ m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),
+ m_hDevice(pCreateInfo->device),
+ m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
+ m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
+ *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
+ m_PreferredLargeHeapBlockSize(0),
+ m_PhysicalDevice(pCreateInfo->physicalDevice),
+ m_CurrentFrameIndex(0),
+ m_Pools(VmaStlAllocator<VmaPool>(GetAllocationCallbacks())),
+ m_NextPoolId(0)
+#if VMA_RECORDING_ENABLED
+ ,m_pRecorder(VMA_NULL)
+#endif
+{
+ if(VMA_DEBUG_DETECT_CORRUPTION)
+ {
+ // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.
+ VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);
+ }
+
+ VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device);
+
+#if !(VMA_DEDICATED_ALLOCATION)
+ if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)
+ {
+ VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");
+ }
+#endif
+
+ memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
+ memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
+ memset(&m_MemProps, 0, sizeof(m_MemProps));
+
+ memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
+ memset(&m_pDedicatedAllocations, 0, sizeof(m_pDedicatedAllocations));
+
+ for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
+ {
+ m_HeapSizeLimit[i] = VK_WHOLE_SIZE;
+ }
+
+ if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
+ {
+ m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
+ m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
+ }
+
+ ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);
+
+ (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);
+ (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);
+
+ VMA_ASSERT(VmaIsPow2(VMA_DEBUG_ALIGNMENT));
+ VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));
+ VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));
+ VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));
+
+ m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
+ pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
+
+ if(pCreateInfo->pHeapSizeLimit != VMA_NULL)
+ {
+ for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)
+ {
+ const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];
+ if(limit != VK_WHOLE_SIZE)
+ {
+ m_HeapSizeLimit[heapIndex] = limit;
+ if(limit < m_MemProps.memoryHeaps[heapIndex].size)
+ {
+ m_MemProps.memoryHeaps[heapIndex].size = limit;
+ }
+ }
+ }
+ }
+
+ for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
+ {
+ const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);
+
+ m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(
+ this,
+ memTypeIndex,
+ preferredBlockSize,
+ 0,
+ SIZE_MAX,
+ GetBufferImageGranularity(),
+ pCreateInfo->frameInUseCount,
+ false, // isCustomPool
+ false, // explicitBlockSize
+ false); // linearAlgorithm
+ // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,
+ // becase minBlockCount is 0.
+ m_pDedicatedAllocations[memTypeIndex] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks()));
+
+ }
+}
+
+VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)
+{
+ VkResult res = VK_SUCCESS;
+
+ if(pCreateInfo->pRecordSettings != VMA_NULL &&
+ !VmaStrIsEmpty(pCreateInfo->pRecordSettings->pFilePath))
+ {
+#if VMA_RECORDING_ENABLED
+ m_pRecorder = vma_new(this, VmaRecorder)();
+ res = m_pRecorder->Init(*pCreateInfo->pRecordSettings, m_UseMutex);
+ if(res != VK_SUCCESS)
+ {
+ return res;
+ }
+ m_pRecorder->WriteConfiguration(
+ m_PhysicalDeviceProperties,
+ m_MemProps,
+ m_UseKhrDedicatedAllocation);
+ m_pRecorder->RecordCreateAllocator(GetCurrentFrameIndex());
+#else
+ VMA_ASSERT(0 && "VmaAllocatorCreateInfo::pRecordSettings used, but not supported due to VMA_RECORDING_ENABLED not defined to 1.");
+ return VK_ERROR_FEATURE_NOT_PRESENT;
+#endif
+ }
+
+ return res;
+}
+
+VmaAllocator_T::~VmaAllocator_T()
+{
+#if VMA_RECORDING_ENABLED
+ if(m_pRecorder != VMA_NULL)
+ {
+ m_pRecorder->RecordDestroyAllocator(GetCurrentFrameIndex());
+ vma_delete(this, m_pRecorder);
+ }
+#endif
+
+ VMA_ASSERT(m_Pools.empty());
+
+ for(size_t i = GetMemoryTypeCount(); i--; )
+ {
+ vma_delete(this, m_pDedicatedAllocations[i]);
+ vma_delete(this, m_pBlockVectors[i]);
+ }
+}
+
+void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)
+{
+#if VMA_STATIC_VULKAN_FUNCTIONS == 1
+ m_VulkanFunctions.vkGetPhysicalDeviceProperties = &vkGetPhysicalDeviceProperties;
+ m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = &vkGetPhysicalDeviceMemoryProperties;
+ m_VulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
+ m_VulkanFunctions.vkFreeMemory = &vkFreeMemory;
+ m_VulkanFunctions.vkMapMemory = &vkMapMemory;
+ m_VulkanFunctions.vkUnmapMemory = &vkUnmapMemory;
+ m_VulkanFunctions.vkFlushMappedMemoryRanges = &vkFlushMappedMemoryRanges;
+ m_VulkanFunctions.vkInvalidateMappedMemoryRanges = &vkInvalidateMappedMemoryRanges;
+ m_VulkanFunctions.vkBindBufferMemory = &vkBindBufferMemory;
+ m_VulkanFunctions.vkBindImageMemory = &vkBindImageMemory;
+ m_VulkanFunctions.vkGetBufferMemoryRequirements = &vkGetBufferMemoryRequirements;
+ m_VulkanFunctions.vkGetImageMemoryRequirements = &vkGetImageMemoryRequirements;
+ m_VulkanFunctions.vkCreateBuffer = &vkCreateBuffer;
+ m_VulkanFunctions.vkDestroyBuffer = &vkDestroyBuffer;
+ m_VulkanFunctions.vkCreateImage = &vkCreateImage;
+ m_VulkanFunctions.vkDestroyImage = &vkDestroyImage;
+ m_VulkanFunctions.vkCmdCopyBuffer = &vkCmdCopyBuffer;
+#if VMA_DEDICATED_ALLOCATION
+ if(m_UseKhrDedicatedAllocation)
+ {
+ m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR =
+ (PFN_vkGetBufferMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetBufferMemoryRequirements2KHR");
+ m_VulkanFunctions.vkGetImageMemoryRequirements2KHR =
+ (PFN_vkGetImageMemoryRequirements2KHR)vkGetDeviceProcAddr(m_hDevice, "vkGetImageMemoryRequirements2KHR");
+ }
+#endif // #if VMA_DEDICATED_ALLOCATION
+#endif // #if VMA_STATIC_VULKAN_FUNCTIONS == 1
+
+#define VMA_COPY_IF_NOT_NULL(funcName) \
+ if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;
+
+ if(pVulkanFunctions != VMA_NULL)
+ {
+ VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);
+ VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);
+ VMA_COPY_IF_NOT_NULL(vkAllocateMemory);
+ VMA_COPY_IF_NOT_NULL(vkFreeMemory);
+ VMA_COPY_IF_NOT_NULL(vkMapMemory);
+ VMA_COPY_IF_NOT_NULL(vkUnmapMemory);
+ VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);
+ VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);
+ VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);
+ VMA_COPY_IF_NOT_NULL(vkBindImageMemory);
+ VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);
+ VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);
+ VMA_COPY_IF_NOT_NULL(vkCreateBuffer);
+ VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);
+ VMA_COPY_IF_NOT_NULL(vkCreateImage);
+ VMA_COPY_IF_NOT_NULL(vkDestroyImage);
+ VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);
+#if VMA_DEDICATED_ALLOCATION
+ VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);
+ VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);
+#endif
+ }
+
+#undef VMA_COPY_IF_NOT_NULL
+
+ // If these asserts are hit, you must either #define VMA_STATIC_VULKAN_FUNCTIONS 1
+ // or pass valid pointers as VmaAllocatorCreateInfo::pVulkanFunctions.
+ VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);
+#if VMA_DEDICATED_ALLOCATION
+ if(m_UseKhrDedicatedAllocation)
+ {
+ VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);
+ VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);
+ }
+#endif
+}
+
+VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)
+{
+ const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
+ const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;
+ const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;
+ return isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize;
+}
+
+VkResult VmaAllocator_T::AllocateMemoryOfType(
+ VkDeviceSize size,
+ VkDeviceSize alignment,
+ bool dedicatedAllocation,
+ VkBuffer dedicatedBuffer,
+ VkImage dedicatedImage,
+ const VmaAllocationCreateInfo& createInfo,
+ uint32_t memTypeIndex,
+ VmaSuballocationType suballocType,
+ size_t allocationCount,
+ VmaAllocation* pAllocations)
+{
+ VMA_ASSERT(pAllocations != VMA_NULL);
+ VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, vkMemReq.size);
+
+ VmaAllocationCreateInfo finalCreateInfo = createInfo;
+
+ // If memory type is not HOST_VISIBLE, disable MAPPED.
+ if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
+ (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
+ {
+ finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;
+ }
+
+ VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex];
+ VMA_ASSERT(blockVector);
+
+ const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
+ bool preferDedicatedMemory =
+ VMA_DEBUG_ALWAYS_DEDICATED_MEMORY ||
+ dedicatedAllocation ||
+ // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.
+ size > preferredBlockSize / 2;
+
+ if(preferDedicatedMemory &&
+ (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
+ finalCreateInfo.pool == VK_NULL_HANDLE)
+ {
+ finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
+ }
+
+ if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
+ {
+ if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
+ {
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ }
+ else
+ {
+ return AllocateDedicatedMemory(
+ size,
+ suballocType,
+ memTypeIndex,
+ (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
+ (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
+ finalCreateInfo.pUserData,
+ dedicatedBuffer,
+ dedicatedImage,
+ allocationCount,
+ pAllocations);
+ }
+ }
+ else
+ {
+ VkResult res = blockVector->Allocate(
+ VK_NULL_HANDLE, // hCurrentPool
+ m_CurrentFrameIndex.load(),
+ size,
+ alignment,
+ finalCreateInfo,
+ suballocType,
+ allocationCount,
+ pAllocations);
+ if(res == VK_SUCCESS)
+ {
+ return res;
+ }
+
+ // 5. Try dedicated memory.
+ if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
+ {
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ }
+ else
+ {
+ res = AllocateDedicatedMemory(
+ size,
+ suballocType,
+ memTypeIndex,
+ (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,
+ (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,
+ finalCreateInfo.pUserData,
+ dedicatedBuffer,
+ dedicatedImage,
+ allocationCount,
+ pAllocations);
+ if(res == VK_SUCCESS)
+ {
+ // Succeeded: AllocateDedicatedMemory function already filld pMemory, nothing more to do here.
+ VMA_DEBUG_LOG(" Allocated as DedicatedMemory");
+ return VK_SUCCESS;
+ }
+ else
+ {
+ // Everything failed: Return error code.
+ VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
+ return res;
+ }
+ }
+ }
+}
+
+VkResult VmaAllocator_T::AllocateDedicatedMemory(
+ VkDeviceSize size,
+ VmaSuballocationType suballocType,
+ uint32_t memTypeIndex,
+ bool map,
+ bool isUserDataString,
+ void* pUserData,
+ VkBuffer /*dedicatedBuffer*/,
+ VkImage /*dedicatedImage*/,
+ size_t allocationCount,
+ VmaAllocation* pAllocations)
+{
+ VMA_ASSERT(allocationCount > 0 && pAllocations);
+
+ VkMemoryAllocateInfo allocInfo = {};
+ allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ allocInfo.memoryTypeIndex = memTypeIndex;
+ allocInfo.allocationSize = size;
+
+#if VMA_DEDICATED_ALLOCATION
+ VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = {};
+ dedicatedAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR;
+ if(m_UseKhrDedicatedAllocation)
+ {
+ if(dedicatedBuffer != VK_NULL_HANDLE)
+ {
+ VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);
+ dedicatedAllocInfo.buffer = dedicatedBuffer;
+ allocInfo.pNext = &dedicatedAllocInfo;
+ }
+ else if(dedicatedImage != VK_NULL_HANDLE)
+ {
+ dedicatedAllocInfo.image = dedicatedImage;
+ allocInfo.pNext = &dedicatedAllocInfo;
+ }
+ }
+#endif // #if VMA_DEDICATED_ALLOCATION
+
+ size_t allocIndex;
+ VkResult res = VK_SUCCESS;
+ for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
+ {
+ res = AllocateDedicatedMemoryPage(
+ size,
+ suballocType,
+ memTypeIndex,
+ allocInfo,
+ map,
+ isUserDataString,
+ pUserData,
+ pAllocations + allocIndex);
+ if(res != VK_SUCCESS)
+ {
+ break;
+ }
+ }
+
+ if(res == VK_SUCCESS)
+ {
+ // Register them in m_pDedicatedAllocations.
+ {
+ VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
+ AllocationVectorType* pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
+ VMA_ASSERT(pDedicatedAllocations);
+ for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
+ {
+ VmaVectorInsertSorted<VmaPointerLess>(*pDedicatedAllocations, pAllocations[allocIndex]);
+ }
+ }
+
+ VMA_DEBUG_LOG(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);
+ }
+ else
+ {
+ // Free all already created allocations.
+ while(allocIndex--)
+ {
+ VmaAllocation currAlloc = pAllocations[allocIndex];
+ VkDeviceMemory hMemory = currAlloc->GetMemory();
+
+ /*
+ There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
+ before vkFreeMemory.
+
+ if(currAlloc->GetMappedData() != VMA_NULL)
+ {
+ (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
+ }
+ */
+
+ FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);
+
+ currAlloc->SetUserData(this, VMA_NULL);
+ vma_delete(this, currAlloc);
+ }
+
+ memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
+ }
+
+ return res;
+}
+
+VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(
+ VkDeviceSize size,
+ VmaSuballocationType suballocType,
+ uint32_t memTypeIndex,
+ const VkMemoryAllocateInfo& allocInfo,
+ bool map,
+ bool isUserDataString,
+ void* pUserData,
+ VmaAllocation* pAllocation)
+{
+ VkDeviceMemory hMemory = VK_NULL_HANDLE;
+ VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);
+ if(res < 0)
+ {
+ VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
+ return res;
+ }
+
+ void* pMappedData = VMA_NULL;
+ if(map)
+ {
+ res = (*m_VulkanFunctions.vkMapMemory)(
+ m_hDevice,
+ hMemory,
+ 0,
+ VK_WHOLE_SIZE,
+ 0,
+ &pMappedData);
+ if(res < 0)
+ {
+ VMA_DEBUG_LOG(" vkMapMemory FAILED");
+ FreeVulkanMemory(memTypeIndex, size, hMemory);
+ return res;
+ }
+ }
+
+ *pAllocation = vma_new(this, VmaAllocation_T)(m_CurrentFrameIndex.load(), isUserDataString);
+ (*pAllocation)->InitDedicatedAllocation(memTypeIndex, hMemory, suballocType, pMappedData, size);
+ (*pAllocation)->SetUserData(this, pUserData);
+ if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
+ {
+ FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
+ }
+
+ return VK_SUCCESS;
+}
+
+void VmaAllocator_T::GetBufferMemoryRequirements(
+ VkBuffer hBuffer,
+ VkMemoryRequirements& memReq,
+ bool& requiresDedicatedAllocation,
+ bool& prefersDedicatedAllocation) const
+{
+#if VMA_DEDICATED_ALLOCATION
+ if(m_UseKhrDedicatedAllocation)
+ {
+ VkBufferMemoryRequirementsInfo2KHR memReqInfo = {};
+ memReqInfo.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR;
+ memReqInfo.buffer = hBuffer;
+
+ VkMemoryDedicatedRequirementsKHR memDedicatedReq = {};
+ memDedicatedReq.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR;
+
+ VkMemoryRequirements2KHR memReq2 = {};
+ memReq2.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR;
+ memReq2.pNext = &memDedicatedReq;
+
+ (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
+
+ memReq = memReq2.memoryRequirements;
+ requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
+ prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
+ }
+ else
+#endif // #if VMA_DEDICATED_ALLOCATION
+ {
+ (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);
+ requiresDedicatedAllocation = false;
+ prefersDedicatedAllocation = false;
+ }
+}
+
+void VmaAllocator_T::GetImageMemoryRequirements(
+ VkImage hImage,
+ VkMemoryRequirements& memReq,
+ bool& requiresDedicatedAllocation,
+ bool& prefersDedicatedAllocation) const
+{
+#if VMA_DEDICATED_ALLOCATION
+ if(m_UseKhrDedicatedAllocation)
+ {
+ VkImageMemoryRequirementsInfo2KHR memReqInfo = {};
+ memReqInfo.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR;
+ memReqInfo.image = hImage;
+
+ VkMemoryDedicatedRequirementsKHR memDedicatedReq = {};
+ memDedicatedReq.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR;
+
+ VkMemoryRequirements2KHR memReq2 = {};
+ memReq2.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR;
+ memReq2.pNext = &memDedicatedReq;
+
+ (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);
+
+ memReq = memReq2.memoryRequirements;
+ requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);
+ prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE);
+ }
+ else
+#endif // #if VMA_DEDICATED_ALLOCATION
+ {
+ (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);
+ requiresDedicatedAllocation = false;
+ prefersDedicatedAllocation = false;
+ }
+}
+
+VkResult VmaAllocator_T::AllocateMemory(
+ const VkMemoryRequirements& vkMemReq,
+ bool requiresDedicatedAllocation,
+ bool prefersDedicatedAllocation,
+ VkBuffer dedicatedBuffer,
+ VkImage dedicatedImage,
+ const VmaAllocationCreateInfo& createInfo,
+ VmaSuballocationType suballocType,
+ size_t allocationCount,
+ VmaAllocation* pAllocations)
+{
+ memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);
+
+ VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));
+
+ if(vkMemReq.size == 0)
+ {
+ return VK_ERROR_VALIDATION_FAILED_EXT;
+ }
+ if((createInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
+ (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
+ {
+ VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ }
+ if((createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&
+ (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0)
+ {
+ VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_MAPPED_BIT together with VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT is invalid.");
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ }
+ if(requiresDedicatedAllocation)
+ {
+ if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
+ {
+ VMA_ASSERT(0 && "VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT specified while dedicated allocation is required.");
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ }
+ if(createInfo.pool != VK_NULL_HANDLE)
+ {
+ VMA_ASSERT(0 && "Pool specified while dedicated allocation is required.");
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ }
+ }
+ if((createInfo.pool != VK_NULL_HANDLE) &&
+ ((createInfo.flags & (VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)) != 0))
+ {
+ VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT when pool != null is invalid.");
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ }
+
+ if(createInfo.pool != VK_NULL_HANDLE)
+ {
+ const VkDeviceSize alignmentForPool = VMA_MAX(
+ vkMemReq.alignment,
+ GetMemoryTypeMinAlignment(createInfo.pool->m_BlockVector.GetMemoryTypeIndex()));
+ return createInfo.pool->m_BlockVector.Allocate(
+ createInfo.pool,
+ m_CurrentFrameIndex.load(),
+ vkMemReq.size,
+ alignmentForPool,
+ createInfo,
+ suballocType,
+ allocationCount,
+ pAllocations);
+ }
+ else
+ {
+ // Bit mask of memory Vulkan types acceptable for this allocation.
+ uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
+ uint32_t memTypeIndex = UINT32_MAX;
+ VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
+ if(res == VK_SUCCESS)
+ {
+ VkDeviceSize alignmentForMemType = VMA_MAX(
+ vkMemReq.alignment,
+ GetMemoryTypeMinAlignment(memTypeIndex));
+
+ res = AllocateMemoryOfType(
+ vkMemReq.size,
+ alignmentForMemType,
+ requiresDedicatedAllocation || prefersDedicatedAllocation,
+ dedicatedBuffer,
+ dedicatedImage,
+ createInfo,
+ memTypeIndex,
+ suballocType,
+ allocationCount,
+ pAllocations);
+ // Succeeded on first try.
+ if(res == VK_SUCCESS)
+ {
+ return res;
+ }
+ // Allocation from this memory type failed. Try other compatible memory types.
+ else
+ {
+ for(;;)
+ {
+ // Remove old memTypeIndex from list of possibilities.
+ memoryTypeBits &= ~(1u << memTypeIndex);
+ // Find alternative memTypeIndex.
+ res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfo, &memTypeIndex);
+ if(res == VK_SUCCESS)
+ {
+ alignmentForMemType = VMA_MAX(
+ vkMemReq.alignment,
+ GetMemoryTypeMinAlignment(memTypeIndex));
+
+ res = AllocateMemoryOfType(
+ vkMemReq.size,
+ alignmentForMemType,
+ requiresDedicatedAllocation || prefersDedicatedAllocation,
+ dedicatedBuffer,
+ dedicatedImage,
+ createInfo,
+ memTypeIndex,
+ suballocType,
+ allocationCount,
+ pAllocations);
+ // Allocation from this alternative memory type succeeded.
+ if(res == VK_SUCCESS)
+ {
+ return res;
+ }
+ // else: Allocation from this memory type failed. Try next one - next loop iteration.
+ }
+ // No other matching memory type index could be found.
+ else
+ {
+ // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
+ return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ }
+ }
+ }
+ }
+ // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
+ else
+ return res;
+ }
+}
+
+void VmaAllocator_T::FreeMemory(
+ size_t allocationCount,
+ const VmaAllocation* pAllocations)
+{
+ VMA_ASSERT(pAllocations);
+
+ for(size_t allocIndex = allocationCount; allocIndex--; )
+ {
+ VmaAllocation allocation = pAllocations[allocIndex];
+
+ if(allocation != VK_NULL_HANDLE)
+ {
+ if(TouchAllocation(allocation))
+ {
+ if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
+ {
+ FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);
+ }
+
+ switch(allocation->GetType())
+ {
+ case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
+ {
+ VmaBlockVector* pBlockVector = VMA_NULL;
+ VmaPool hPool = allocation->GetPool();
+ if(hPool != VK_NULL_HANDLE)
+ {
+ pBlockVector = &hPool->m_BlockVector;
+ }
+ else
+ {
+ const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
+ pBlockVector = m_pBlockVectors[memTypeIndex];
+ }
+ pBlockVector->Free(allocation);
+ }
+ break;
+ case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
+ FreeDedicatedMemory(allocation);
+ break;
+ default:
+ VMA_ASSERT(0);
+ }
+ }
+
+ allocation->SetUserData(this, VMA_NULL);
+ vma_delete(this, allocation);
+ }
+ }
+}
+
+VkResult VmaAllocator_T::ResizeAllocation(
+ const VmaAllocation alloc,
+ VkDeviceSize newSize)
+{
+ if(newSize == 0 || alloc->GetLastUseFrameIndex() == VMA_FRAME_INDEX_LOST)
+ {
+ return VK_ERROR_VALIDATION_FAILED_EXT;
+ }
+ if(newSize == alloc->GetSize())
+ {
+ return VK_SUCCESS;
+ }
+
+ switch(alloc->GetType())
+ {
+ case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
+ return VK_ERROR_FEATURE_NOT_PRESENT;
+ case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
+ if(alloc->GetBlock()->m_pMetadata->ResizeAllocation(alloc, newSize))
+ {
+ alloc->ChangeSize(newSize);
+ VMA_HEAVY_ASSERT(alloc->GetBlock()->m_pMetadata->Validate());
+ return VK_SUCCESS;
+ }
+ else
+ {
+ return VkResult(-1000069000); // VK_ERROR_OUT_OF_POOL_MEMORY
+ }
+ default:
+ VMA_ASSERT(0);
+ return VK_ERROR_VALIDATION_FAILED_EXT;
+ }
+}
+
+void VmaAllocator_T::CalculateStats(VmaStats* pStats)
+{
+ // Initialize.
+ InitStatInfo(pStats->total);
+ for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
+ InitStatInfo(pStats->memoryType[i]);
+ for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
+ InitStatInfo(pStats->memoryHeap[i]);
+
+ // Process default pools.
+ for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
+ {
+ VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
+ VMA_ASSERT(pBlockVector);
+ pBlockVector->AddStats(pStats);
+ }
+
+ // Process custom pools.
+ {
+ VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
+ for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
+ {
+ m_Pools[poolIndex]->m_BlockVector.AddStats(pStats);
+ }
+ }
+
+ // Process dedicated allocations.
+ for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
+ {
+ const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
+ VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
+ AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
+ VMA_ASSERT(pDedicatedAllocVector);
+ for(size_t allocIndex = 0, allocCount = pDedicatedAllocVector->size(); allocIndex < allocCount; ++allocIndex)
+ {
+ VmaStatInfo allocationStatInfo;
+ (*pDedicatedAllocVector)[allocIndex]->DedicatedAllocCalcStatsInfo(allocationStatInfo);
+ VmaAddStatInfo(pStats->total, allocationStatInfo);
+ VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
+ VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
+ }
+ }
+
+ // Postprocess.
+ VmaPostprocessCalcStatInfo(pStats->total);
+ for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
+ VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
+ for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
+ VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
+}
+
+static const uint32_t VMA_VENDOR_ID_AMD = 4098;
+
+VkResult VmaAllocator_T::DefragmentationBegin(
+ const VmaDefragmentationInfo2& info,
+ VmaDefragmentationStats* pStats,
+ VmaDefragmentationContext* pContext)
+{
+ if(info.pAllocationsChanged != VMA_NULL)
+ {
+ memset(info.pAllocationsChanged, 0, info.allocationCount * sizeof(VkBool32));
+ }
+
+ *pContext = vma_new(this, VmaDefragmentationContext_T)(
+ this, m_CurrentFrameIndex.load(), info.flags, pStats);
+
+ (*pContext)->AddPools(info.poolCount, info.pPools);
+ (*pContext)->AddAllocations(
+ info.allocationCount, info.pAllocations, info.pAllocationsChanged);
+
+ VkResult res = (*pContext)->Defragment(
+ info.maxCpuBytesToMove, info.maxCpuAllocationsToMove,
+ info.maxGpuBytesToMove, info.maxGpuAllocationsToMove,
+ info.commandBuffer, pStats);
+
+ if(res != VK_NOT_READY)
+ {
+ vma_delete(this, *pContext);
+ *pContext = VMA_NULL;
+ }
+
+ return res;
+}
+
+VkResult VmaAllocator_T::DefragmentationEnd(
+ VmaDefragmentationContext context)
+{
+ vma_delete(this, context);
+ return VK_SUCCESS;
+}
+
+void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
+{
+ if(hAllocation->CanBecomeLost())
+ {
+ /*
+ Warning: This is a carefully designed algorithm.
+ Do not modify unless you really know what you're doing :)
+ */
+ const uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
+ uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
+ for(;;)
+ {
+ if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
+ {
+ pAllocationInfo->memoryType = UINT32_MAX;
+ pAllocationInfo->deviceMemory = VK_NULL_HANDLE;
+ pAllocationInfo->offset = 0;
+ pAllocationInfo->size = hAllocation->GetSize();
+ pAllocationInfo->pMappedData = VMA_NULL;
+ pAllocationInfo->pUserData = hAllocation->GetUserData();
+ return;
+ }
+ else if(localLastUseFrameIndex == localCurrFrameIndex)
+ {
+ pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
+ pAllocationInfo->deviceMemory = hAllocation->GetMemory();
+ pAllocationInfo->offset = hAllocation->GetOffset();
+ pAllocationInfo->size = hAllocation->GetSize();
+ pAllocationInfo->pMappedData = VMA_NULL;
+ pAllocationInfo->pUserData = hAllocation->GetUserData();
+ return;
+ }
+ else // Last use time earlier than current time.
+ {
+ if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
+ {
+ localLastUseFrameIndex = localCurrFrameIndex;
+ }
+ }
+ }
+ }
+ else
+ {
+#if VMA_STATS_STRING_ENABLED
+ uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
+ uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
+ for(;;)
+ {
+ VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
+ if(localLastUseFrameIndex == localCurrFrameIndex)
+ {
+ break;
+ }
+ else // Last use time earlier than current time.
+ {
+ if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
+ {
+ localLastUseFrameIndex = localCurrFrameIndex;
+ }
+ }
+ }
+#endif
+
+ pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
+ pAllocationInfo->deviceMemory = hAllocation->GetMemory();
+ pAllocationInfo->offset = hAllocation->GetOffset();
+ pAllocationInfo->size = hAllocation->GetSize();
+ pAllocationInfo->pMappedData = hAllocation->GetMappedData();
+ pAllocationInfo->pUserData = hAllocation->GetUserData();
+ }
+}
+
+bool VmaAllocator_T::TouchAllocation(VmaAllocation hAllocation)
+{
+ // This is a stripped-down version of VmaAllocator_T::GetAllocationInfo.
+ if(hAllocation->CanBecomeLost())
+ {
+ uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
+ uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
+ for(;;)
+ {
+ if(localLastUseFrameIndex == VMA_FRAME_INDEX_LOST)
+ {
+ return false;
+ }
+ else if(localLastUseFrameIndex == localCurrFrameIndex)
+ {
+ return true;
+ }
+ else // Last use time earlier than current time.
+ {
+ if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
+ {
+ localLastUseFrameIndex = localCurrFrameIndex;
+ }
+ }
+ }
+ }
+ else
+ {
+#if VMA_STATS_STRING_ENABLED
+ uint32_t localCurrFrameIndex = m_CurrentFrameIndex.load();
+ uint32_t localLastUseFrameIndex = hAllocation->GetLastUseFrameIndex();
+ for(;;)
+ {
+ VMA_ASSERT(localLastUseFrameIndex != VMA_FRAME_INDEX_LOST);
+ if(localLastUseFrameIndex == localCurrFrameIndex)
+ {
+ break;
+ }
+ else // Last use time earlier than current time.
+ {
+ if(hAllocation->CompareExchangeLastUseFrameIndex(localLastUseFrameIndex, localCurrFrameIndex))
+ {
+ localLastUseFrameIndex = localCurrFrameIndex;
+ }
+ }
+ }
+#endif
+
+ return true;
+ }
+}
+
+VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)
+{
+ VMA_DEBUG_LOG(" CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);
+
+ VmaPoolCreateInfo newCreateInfo = *pCreateInfo;
+
+ if(newCreateInfo.maxBlockCount == 0)
+ {
+ newCreateInfo.maxBlockCount = SIZE_MAX;
+ }
+ if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
+ {
+ return VK_ERROR_INITIALIZATION_FAILED;
+ }
+
+ const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
+
+ *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
+
+ VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
+ if(res != VK_SUCCESS)
+ {
+ vma_delete(this, *pPool);
+ *pPool = VMA_NULL;
+ return res;
+ }
+
+ // Add to m_Pools.
+ {
+ VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
+ (*pPool)->SetId(m_NextPoolId++);
+ VmaVectorInsertSorted<VmaPointerLess>(m_Pools, *pPool);
+ }
+
+ return VK_SUCCESS;
+}
+
+void VmaAllocator_T::DestroyPool(VmaPool pool)
+{
+ // Remove from m_Pools.
+ {
+ VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);
+ bool success = VmaVectorRemoveSorted<VmaPointerLess>(m_Pools, pool);
+ (void) success;
+ VMA_ASSERT(success && "Pool not found in Allocator.");
+ }
+
+ vma_delete(this, pool);
+}
+
+void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
+{
+ pool->m_BlockVector.GetPoolStats(pPoolStats);
+}
+
+void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
+{
+ m_CurrentFrameIndex.store(frameIndex);
+}
+
+void VmaAllocator_T::MakePoolAllocationsLost(
+ VmaPool hPool,
+ size_t* pLostAllocationCount)
+{
+ hPool->m_BlockVector.MakePoolAllocationsLost(
+ m_CurrentFrameIndex.load(),
+ pLostAllocationCount);
+}
+
+VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
+{
+ return hPool->m_BlockVector.CheckCorruption();
+}
+
+VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
+{
+ VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;
+
+ // Process default pools.
+ for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
+ {
+ if(((1u << memTypeIndex) & memoryTypeBits) != 0)
+ {
+ VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];
+ VMA_ASSERT(pBlockVector);
+ VkResult localRes = pBlockVector->CheckCorruption();
+ switch(localRes)
+ {
+ case VK_ERROR_FEATURE_NOT_PRESENT:
+ break;
+ case VK_SUCCESS:
+ finalRes = VK_SUCCESS;
+ break;
+ default:
+ return localRes;
+ }
+ }
+ }
+
+ // Process custom pools.
+ {
+ VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
+ for(size_t poolIndex = 0, poolCount = m_Pools.size(); poolIndex < poolCount; ++poolIndex)
+ {
+ if(((1u << m_Pools[poolIndex]->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
+ {
+ VkResult localRes = m_Pools[poolIndex]->m_BlockVector.CheckCorruption();
+ switch(localRes)
+ {
+ case VK_ERROR_FEATURE_NOT_PRESENT:
+ break;
+ case VK_SUCCESS:
+ finalRes = VK_SUCCESS;
+ break;
+ default:
+ return localRes;
+ }
+ }
+ }
+ }
+
+ return finalRes;
+}
+
+void VmaAllocator_T::CreateLostAllocation(VmaAllocation* pAllocation)
+{
+ *pAllocation = vma_new(this, VmaAllocation_T)(VMA_FRAME_INDEX_LOST, false);
+ (*pAllocation)->InitLost();
+}
+
+VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)
+{
+ const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);
+
+ VkResult res;
+ if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE)
+ {
+ VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex);
+ if(m_HeapSizeLimit[heapIndex] >= pAllocateInfo->allocationSize)
+ {
+ res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
+ if(res == VK_SUCCESS)
+ {
+ m_HeapSizeLimit[heapIndex] -= pAllocateInfo->allocationSize;
+ }
+ }
+ else
+ {
+ res = VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ }
+ }
+ else
+ {
+ res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);
+ }
+
+ if(res == VK_SUCCESS && m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
+ {
+ (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize);
+ }
+
+ return res;
+}
+
+void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)
+{
+ if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
+ {
+ (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size);
+ }
+
+ (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());
+
+ const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memoryType);
+ if(m_HeapSizeLimit[heapIndex] != VK_WHOLE_SIZE)
+ {
+ VmaMutexLock lock(m_HeapSizeLimitMutex, m_UseMutex);
+ m_HeapSizeLimit[heapIndex] += size;
+ }
+}
+
+VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)
+{
+ if(hAllocation->CanBecomeLost())
+ {
+ return VK_ERROR_MEMORY_MAP_FAILED;
+ }
+
+ switch(hAllocation->GetType())
+ {
+ case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
+ {
+ VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
+ char *pBytes = VMA_NULL;
+ VkResult res = pBlock->Map(this, 1, (void**)&pBytes);
+ if(res == VK_SUCCESS)
+ {
+ *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();
+ hAllocation->BlockAllocMap();
+ }
+ return res;
+ }
+ case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
+ return hAllocation->DedicatedAllocMap(this, ppData);
+ default:
+ VMA_ASSERT(0);
+ return VK_ERROR_MEMORY_MAP_FAILED;
+ }
+}
+
+void VmaAllocator_T::Unmap(VmaAllocation hAllocation)
+{
+ switch(hAllocation->GetType())
+ {
+ case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
+ {
+ VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();
+ hAllocation->BlockAllocUnmap();
+ pBlock->Unmap(this, 1);
+ }
+ break;
+ case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
+ hAllocation->DedicatedAllocUnmap(this);
+ break;
+ default:
+ VMA_ASSERT(0);
+ }
+}
+
+VkResult VmaAllocator_T::BindBufferMemory(VmaAllocation hAllocation, VkBuffer hBuffer)
+{
+ VkResult res = VK_SUCCESS;
+ switch(hAllocation->GetType())
+ {
+ case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
+ res = GetVulkanFunctions().vkBindBufferMemory(
+ m_hDevice,
+ hBuffer,
+ hAllocation->GetMemory(),
+ 0); //memoryOffset
+ break;
+ case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
+ {
+ VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
+ VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block. Is the allocation lost?");
+ res = pBlock->BindBufferMemory(this, hAllocation, hBuffer);
+ break;
+ }
+ default:
+ VMA_ASSERT(0);
+ }
+ return res;
+}
+
+VkResult VmaAllocator_T::BindImageMemory(VmaAllocation hAllocation, VkImage hImage)
+{
+ VkResult res = VK_SUCCESS;
+ switch(hAllocation->GetType())
+ {
+ case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
+ res = GetVulkanFunctions().vkBindImageMemory(
+ m_hDevice,
+ hImage,
+ hAllocation->GetMemory(),
+ 0); //memoryOffset
+ break;
+ case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
+ {
+ VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();
+ VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block. Is the allocation lost?");
+ res = pBlock->BindImageMemory(this, hAllocation, hImage);
+ break;
+ }
+ default:
+ VMA_ASSERT(0);
+ }
+ return res;
+}
+
+void VmaAllocator_T::FlushOrInvalidateAllocation(
+ VmaAllocation hAllocation,
+ VkDeviceSize offset, VkDeviceSize size,
+ VMA_CACHE_OPERATION op)
+{
+ const uint32_t memTypeIndex = hAllocation->GetMemoryTypeIndex();
+ if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))
+ {
+ const VkDeviceSize allocationSize = hAllocation->GetSize();
+ VMA_ASSERT(offset <= allocationSize);
+
+ const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
+
+ VkMappedMemoryRange memRange = {};
+ memRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
+ memRange.memory = hAllocation->GetMemory();
+
+ switch(hAllocation->GetType())
+ {
+ case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:
+ memRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
+ if(size == VK_WHOLE_SIZE)
+ {
+ memRange.size = allocationSize - memRange.offset;
+ }
+ else
+ {
+ VMA_ASSERT(offset + size <= allocationSize);
+ memRange.size = VMA_MIN(
+ VmaAlignUp(size + (offset - memRange.offset), nonCoherentAtomSize),
+ allocationSize - memRange.offset);
+ }
+ break;
+
+ case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:
+ {
+ // 1. Still within this allocation.
+ memRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);
+ if(size == VK_WHOLE_SIZE)
+ {
+ size = allocationSize - offset;
+ }
+ else
+ {
+ VMA_ASSERT(offset + size <= allocationSize);
+ }
+ memRange.size = VmaAlignUp(size + (offset - memRange.offset), nonCoherentAtomSize);
+
+ // 2. Adjust to whole block.
+ const VkDeviceSize allocationOffset = hAllocation->GetOffset();
+ VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);
+ const VkDeviceSize blockSize = hAllocation->GetBlock()->m_pMetadata->GetSize();
+ memRange.offset += allocationOffset;
+ memRange.size = VMA_MIN(memRange.size, blockSize - memRange.offset);
+
+ break;
+ }
+
+ default:
+ VMA_ASSERT(0);
+ }
+
+ switch(op)
+ {
+ case VMA_CACHE_FLUSH:
+ (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);
+ break;
+ case VMA_CACHE_INVALIDATE:
+ (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);
+ break;
+ default:
+ VMA_ASSERT(0);
+ }
+ }
+ // else: Just ignore this call.
+}
+
+void VmaAllocator_T::FreeDedicatedMemory(VmaAllocation allocation)
+{
+ VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
+
+ const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
+ {
+ VmaMutexLockWrite lock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
+ AllocationVectorType* const pDedicatedAllocations = m_pDedicatedAllocations[memTypeIndex];
+ VMA_ASSERT(pDedicatedAllocations);
+ bool success = VmaVectorRemoveSorted<VmaPointerLess>(*pDedicatedAllocations, allocation);
+ (void) success;
+ VMA_ASSERT(success);
+ }
+
+ VkDeviceMemory hMemory = allocation->GetMemory();
+
+ /*
+ There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory
+ before vkFreeMemory.
+
+ if(allocation->GetMappedData() != VMA_NULL)
+ {
+ (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);
+ }
+ */
+
+ FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);
+
+ VMA_DEBUG_LOG(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);
+}
+
+void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)
+{
+ if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&
+ !hAllocation->CanBecomeLost() &&
+ (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
+ {
+ void* pData = VMA_NULL;
+ VkResult res = Map(hAllocation, &pData);
+ if(res == VK_SUCCESS)
+ {
+ memset(pData, (int)pattern, (size_t)hAllocation->GetSize());
+ FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);
+ Unmap(hAllocation);
+ }
+ else
+ {
+ VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");
+ }
+ }
+}
+
+#if VMA_STATS_STRING_ENABLED
+
+void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
+{
+ bool dedicatedAllocationsStarted = false;
+ for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
+ {
+ VmaMutexLockRead dedicatedAllocationsLock(m_DedicatedAllocationsMutex[memTypeIndex], m_UseMutex);
+ AllocationVectorType* const pDedicatedAllocVector = m_pDedicatedAllocations[memTypeIndex];
+ VMA_ASSERT(pDedicatedAllocVector);
+ if(pDedicatedAllocVector->empty() == false)
+ {
+ if(dedicatedAllocationsStarted == false)
+ {
+ dedicatedAllocationsStarted = true;
+ json.WriteString("DedicatedAllocations");
+ json.BeginObject();
+ }
+
+ json.BeginString("Type ");
+ json.ContinueString(memTypeIndex);
+ json.EndString();
+
+ json.BeginArray();
+
+ for(size_t i = 0; i < pDedicatedAllocVector->size(); ++i)
+ {
+ json.BeginObject(true);
+ const VmaAllocation hAlloc = (*pDedicatedAllocVector)[i];
+ hAlloc->PrintParameters(json);
+ json.EndObject();
+ }
+
+ json.EndArray();
+ }
+ }
+ if(dedicatedAllocationsStarted)
+ {
+ json.EndObject();
+ }
+
+ {
+ bool allocationsStarted = false;
+ for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
+ {
+ if(m_pBlockVectors[memTypeIndex]->IsEmpty() == false)
+ {
+ if(allocationsStarted == false)
+ {
+ allocationsStarted = true;
+ json.WriteString("DefaultPools");
+ json.BeginObject();
+ }
+
+ json.BeginString("Type ");
+ json.ContinueString(memTypeIndex);
+ json.EndString();
+
+ m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
+ }
+ }
+ if(allocationsStarted)
+ {
+ json.EndObject();
+ }
+ }
+
+ // Custom pools
+ {
+ VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
+ const size_t poolCount = m_Pools.size();
+ if(poolCount > 0)
+ {
+ json.WriteString("Pools");
+ json.BeginObject();
+ for(size_t poolIndex = 0; poolIndex < poolCount; ++poolIndex)
+ {
+ json.BeginString();
+ json.ContinueString(m_Pools[poolIndex]->GetId());
+ json.EndString();
+
+ m_Pools[poolIndex]->m_BlockVector.PrintDetailedMap(json);
+ }
+ json.EndObject();
+ }
+ }
+}
+
+#endif // #if VMA_STATS_STRING_ENABLED
+
+////////////////////////////////////////////////////////////////////////////////
+// Public interface
+
+VkResult vmaCreateAllocator(
+ const VmaAllocatorCreateInfo* pCreateInfo,
+ VmaAllocator* pAllocator)
+{
+ VMA_ASSERT(pCreateInfo && pAllocator);
+ VMA_DEBUG_LOG("vmaCreateAllocator");
+ *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
+ return (*pAllocator)->Init(pCreateInfo);
+}
+
+void vmaDestroyAllocator(
+ VmaAllocator allocator)
+{
+ if(allocator != VK_NULL_HANDLE)
+ {
+ VMA_DEBUG_LOG("vmaDestroyAllocator");
+ VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
+ vma_delete(&allocationCallbacks, allocator);
+ }
+}
+
+void vmaGetPhysicalDeviceProperties(
+ VmaAllocator allocator,
+ const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
+{
+ VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
+ *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
+}
+
+void vmaGetMemoryProperties(
+ VmaAllocator allocator,
+ const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
+{
+ VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
+ *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
+}
+
+void vmaGetMemoryTypeProperties(
+ VmaAllocator allocator,
+ uint32_t memoryTypeIndex,
+ VkMemoryPropertyFlags* pFlags)
+{
+ VMA_ASSERT(allocator && pFlags);
+ VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
+ *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
+}
+
+void vmaSetCurrentFrameIndex(
+ VmaAllocator allocator,
+ uint32_t frameIndex)
+{
+ VMA_ASSERT(allocator);
+ VMA_ASSERT(frameIndex != VMA_FRAME_INDEX_LOST);
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ allocator->SetCurrentFrameIndex(frameIndex);
+}
+
+void vmaCalculateStats(
+ VmaAllocator allocator,
+ VmaStats* pStats)
+{
+ VMA_ASSERT(allocator && pStats);
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+ allocator->CalculateStats(pStats);
+}
+
+#if VMA_STATS_STRING_ENABLED
+
+void vmaBuildStatsString(
+ VmaAllocator allocator,
+ char** ppStatsString,
+ VkBool32 detailedMap)
+{
+ VMA_ASSERT(allocator && ppStatsString);
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ VmaStringBuilder sb(allocator);
+ {
+ VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);
+ json.BeginObject();
+
+ VmaStats stats;
+ allocator->CalculateStats(&stats);
+
+ json.WriteString("Total");
+ VmaPrintStatInfo(json, stats.total);
+
+ for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
+ {
+ json.BeginString("Heap ");
+ json.ContinueString(heapIndex);
+ json.EndString();
+ json.BeginObject();
+
+ json.WriteString("Size");
+ json.WriteNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
+
+ json.WriteString("Flags");
+ json.BeginArray(true);
+ if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
+ {
+ json.WriteString("DEVICE_LOCAL");
+ }
+ json.EndArray();
+
+ if(stats.memoryHeap[heapIndex].blockCount > 0)
+ {
+ json.WriteString("Stats");
+ VmaPrintStatInfo(json, stats.memoryHeap[heapIndex]);
+ }
+
+ for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
+ {
+ if(allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)
+ {
+ json.BeginString("Type ");
+ json.ContinueString(typeIndex);
+ json.EndString();
+
+ json.BeginObject();
+
+ json.WriteString("Flags");
+ json.BeginArray(true);
+ VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
+ if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
+ {
+ json.WriteString("DEVICE_LOCAL");
+ }
+ if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
+ {
+ json.WriteString("HOST_VISIBLE");
+ }
+ if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
+ {
+ json.WriteString("HOST_COHERENT");
+ }
+ if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
+ {
+ json.WriteString("HOST_CACHED");
+ }
+ if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
+ {
+ json.WriteString("LAZILY_ALLOCATED");
+ }
+ json.EndArray();
+
+ if(stats.memoryType[typeIndex].blockCount > 0)
+ {
+ json.WriteString("Stats");
+ VmaPrintStatInfo(json, stats.memoryType[typeIndex]);
+ }
+
+ json.EndObject();
+ }
+ }
+
+ json.EndObject();
+ }
+ if(detailedMap == VK_TRUE)
+ {
+ allocator->PrintDetailedMap(json);
+ }
+
+ json.EndObject();
+ }
+
+ const size_t len = sb.GetLength();
+ char* const pChars = vma_new_array(allocator, char, len + 1);
+ if(len > 0)
+ {
+ memcpy(pChars, sb.GetData(), len);
+ }
+ pChars[len] = '\0';
+ *ppStatsString = pChars;
+}
+
+void vmaFreeStatsString(
+ VmaAllocator allocator,
+ char* pStatsString)
+{
+ if(pStatsString != VMA_NULL)
+ {
+ VMA_ASSERT(allocator);
+ size_t len = strlen(pStatsString);
+ vma_delete_array(allocator, pStatsString, len + 1);
+ }
+}
+
+#endif // #if VMA_STATS_STRING_ENABLED
+
+/*
+This function is not protected by any mutex because it just reads immutable data.
+*/
+VkResult vmaFindMemoryTypeIndex(
+ VmaAllocator allocator,
+ uint32_t memoryTypeBits,
+ const VmaAllocationCreateInfo* pAllocationCreateInfo,
+ uint32_t* pMemoryTypeIndex)
+{
+ VMA_ASSERT(allocator != VK_NULL_HANDLE);
+ VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
+ VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
+
+ if(pAllocationCreateInfo->memoryTypeBits != 0)
+ {
+ memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;
+ }
+
+ uint32_t requiredFlags = pAllocationCreateInfo->requiredFlags;
+ uint32_t preferredFlags = pAllocationCreateInfo->preferredFlags;
+
+ const bool mapped = (pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
+ if(mapped)
+ {
+ preferredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
+ }
+
+ // Convert usage to requiredFlags and preferredFlags.
+ switch(pAllocationCreateInfo->usage)
+ {
+ case VMA_MEMORY_USAGE_UNKNOWN:
+ break;
+ case VMA_MEMORY_USAGE_GPU_ONLY:
+ if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
+ {
+ preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
+ }
+ break;
+ case VMA_MEMORY_USAGE_CPU_ONLY:
+ requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
+ break;
+ case VMA_MEMORY_USAGE_CPU_TO_GPU:
+ requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
+ if(!allocator->IsIntegratedGpu() || (preferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
+ {
+ preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
+ }
+ break;
+ case VMA_MEMORY_USAGE_GPU_TO_CPU:
+ requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
+ preferredFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
+ break;
+ default:
+ break;
+ }
+
+ *pMemoryTypeIndex = UINT32_MAX;
+ uint32_t minCost = UINT32_MAX;
+ for(uint32_t memTypeIndex = 0, memTypeBit = 1;
+ memTypeIndex < allocator->GetMemoryTypeCount();
+ ++memTypeIndex, memTypeBit <<= 1)
+ {
+ // This memory type is acceptable according to memoryTypeBits bitmask.
+ if((memTypeBit & memoryTypeBits) != 0)
+ {
+ const VkMemoryPropertyFlags currFlags =
+ allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
+ // This memory type contains requiredFlags.
+ if((requiredFlags & ~currFlags) == 0)
+ {
+ // Calculate cost as number of bits from preferredFlags not present in this memory type.
+ uint32_t currCost = VmaCountBitsSet(preferredFlags & ~currFlags);
+ // Remember memory type with lowest cost.
+ if(currCost < minCost)
+ {
+ *pMemoryTypeIndex = memTypeIndex;
+ if(currCost == 0)
+ {
+ return VK_SUCCESS;
+ }
+ minCost = currCost;
+ }
+ }
+ }
+ }
+ return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
+}
+
+VkResult vmaFindMemoryTypeIndexForBufferInfo(
+ VmaAllocator allocator,
+ const VkBufferCreateInfo* pBufferCreateInfo,
+ const VmaAllocationCreateInfo* pAllocationCreateInfo,
+ uint32_t* pMemoryTypeIndex)
+{
+ VMA_ASSERT(allocator != VK_NULL_HANDLE);
+ VMA_ASSERT(pBufferCreateInfo != VMA_NULL);
+ VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
+ VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
+
+ const VkDevice hDev = allocator->m_hDevice;
+ VkBuffer hBuffer = VK_NULL_HANDLE;
+ VkResult res = allocator->GetVulkanFunctions().vkCreateBuffer(
+ hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);
+ if(res == VK_SUCCESS)
+ {
+ VkMemoryRequirements memReq = {};
+ allocator->GetVulkanFunctions().vkGetBufferMemoryRequirements(
+ hDev, hBuffer, &memReq);
+
+ res = vmaFindMemoryTypeIndex(
+ allocator,
+ memReq.memoryTypeBits,
+ pAllocationCreateInfo,
+ pMemoryTypeIndex);
+
+ allocator->GetVulkanFunctions().vkDestroyBuffer(
+ hDev, hBuffer, allocator->GetAllocationCallbacks());
+ }
+ return res;
+}
+
+VkResult vmaFindMemoryTypeIndexForImageInfo(
+ VmaAllocator allocator,
+ const VkImageCreateInfo* pImageCreateInfo,
+ const VmaAllocationCreateInfo* pAllocationCreateInfo,
+ uint32_t* pMemoryTypeIndex)
+{
+ VMA_ASSERT(allocator != VK_NULL_HANDLE);
+ VMA_ASSERT(pImageCreateInfo != VMA_NULL);
+ VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);
+ VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
+
+ const VkDevice hDev = allocator->m_hDevice;
+ VkImage hImage = VK_NULL_HANDLE;
+ VkResult res = allocator->GetVulkanFunctions().vkCreateImage(
+ hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);
+ if(res == VK_SUCCESS)
+ {
+ VkMemoryRequirements memReq = {};
+ allocator->GetVulkanFunctions().vkGetImageMemoryRequirements(
+ hDev, hImage, &memReq);
+
+ res = vmaFindMemoryTypeIndex(
+ allocator,
+ memReq.memoryTypeBits,
+ pAllocationCreateInfo,
+ pMemoryTypeIndex);
+
+ allocator->GetVulkanFunctions().vkDestroyImage(
+ hDev, hImage, allocator->GetAllocationCallbacks());
+ }
+ return res;
+}
+
+VkResult vmaCreatePool(
+ VmaAllocator allocator,
+ const VmaPoolCreateInfo* pCreateInfo,
+ VmaPool* pPool)
+{
+ VMA_ASSERT(allocator && pCreateInfo && pPool);
+
+ VMA_DEBUG_LOG("vmaCreatePool");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ VkResult res = allocator->CreatePool(pCreateInfo, pPool);
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordCreatePool(allocator->GetCurrentFrameIndex(), *pCreateInfo, *pPool);
+ }
+#endif
+
+ return res;
+}
+
+void vmaDestroyPool(
+ VmaAllocator allocator,
+ VmaPool pool)
+{
+ VMA_ASSERT(allocator);
+
+ if(pool == VK_NULL_HANDLE)
+ {
+ return;
+ }
+
+ VMA_DEBUG_LOG("vmaDestroyPool");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordDestroyPool(allocator->GetCurrentFrameIndex(), pool);
+ }
+#endif
+
+ allocator->DestroyPool(pool);
+}
+
+void vmaGetPoolStats(
+ VmaAllocator allocator,
+ VmaPool pool,
+ VmaPoolStats* pPoolStats)
+{
+ VMA_ASSERT(allocator && pool && pPoolStats);
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ allocator->GetPoolStats(pool, pPoolStats);
+}
+
+void vmaMakePoolAllocationsLost(
+ VmaAllocator allocator,
+ VmaPool pool,
+ size_t* pLostAllocationCount)
+{
+ VMA_ASSERT(allocator && pool);
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordMakePoolAllocationsLost(allocator->GetCurrentFrameIndex(), pool);
+ }
+#endif
+
+ allocator->MakePoolAllocationsLost(pool, pLostAllocationCount);
+}
+
+VkResult vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)
+{
+ VMA_ASSERT(allocator && pool);
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ VMA_DEBUG_LOG("vmaCheckPoolCorruption");
+
+ return allocator->CheckPoolCorruption(pool);
+}
+
+VkResult vmaAllocateMemory(
+ VmaAllocator allocator,
+ const VkMemoryRequirements* pVkMemoryRequirements,
+ const VmaAllocationCreateInfo* pCreateInfo,
+ VmaAllocation* pAllocation,
+ VmaAllocationInfo* pAllocationInfo)
+{
+ VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);
+
+ VMA_DEBUG_LOG("vmaAllocateMemory");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ VkResult result = allocator->AllocateMemory(
+ *pVkMemoryRequirements,
+ false, // requiresDedicatedAllocation
+ false, // prefersDedicatedAllocation
+ VK_NULL_HANDLE, // dedicatedBuffer
+ VK_NULL_HANDLE, // dedicatedImage
+ *pCreateInfo,
+ VMA_SUBALLOCATION_TYPE_UNKNOWN,
+ 1, // allocationCount
+ pAllocation);
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordAllocateMemory(
+ allocator->GetCurrentFrameIndex(),
+ *pVkMemoryRequirements,
+ *pCreateInfo,
+ *pAllocation);
+ }
+#endif
+
+ if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
+ {
+ allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
+ }
+
+ return result;
+}
+
+VkResult vmaAllocateMemoryPages(
+ VmaAllocator allocator,
+ const VkMemoryRequirements* pVkMemoryRequirements,
+ const VmaAllocationCreateInfo* pCreateInfo,
+ size_t allocationCount,
+ VmaAllocation* pAllocations,
+ VmaAllocationInfo* pAllocationInfo)
+{
+ if(allocationCount == 0)
+ {
+ return VK_SUCCESS;
+ }
+
+ VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);
+
+ VMA_DEBUG_LOG("vmaAllocateMemoryPages");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ VkResult result = allocator->AllocateMemory(
+ *pVkMemoryRequirements,
+ false, // requiresDedicatedAllocation
+ false, // prefersDedicatedAllocation
+ VK_NULL_HANDLE, // dedicatedBuffer
+ VK_NULL_HANDLE, // dedicatedImage
+ *pCreateInfo,
+ VMA_SUBALLOCATION_TYPE_UNKNOWN,
+ allocationCount,
+ pAllocations);
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordAllocateMemoryPages(
+ allocator->GetCurrentFrameIndex(),
+ *pVkMemoryRequirements,
+ *pCreateInfo,
+ (uint64_t)allocationCount,
+ pAllocations);
+ }
+#endif
+
+ if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)
+ {
+ for(size_t i = 0; i < allocationCount; ++i)
+ {
+ allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);
+ }
+ }
+
+ return result;
+}
+
+VkResult vmaAllocateMemoryForBuffer(
+ VmaAllocator allocator,
+ VkBuffer buffer,
+ const VmaAllocationCreateInfo* pCreateInfo,
+ VmaAllocation* pAllocation,
+ VmaAllocationInfo* pAllocationInfo)
+{
+ VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);
+
+ VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ VkMemoryRequirements vkMemReq = {};
+ bool requiresDedicatedAllocation = false;
+ bool prefersDedicatedAllocation = false;
+ allocator->GetBufferMemoryRequirements(buffer, vkMemReq,
+ requiresDedicatedAllocation,
+ prefersDedicatedAllocation);
+
+ VkResult result = allocator->AllocateMemory(
+ vkMemReq,
+ requiresDedicatedAllocation,
+ prefersDedicatedAllocation,
+ buffer, // dedicatedBuffer
+ VK_NULL_HANDLE, // dedicatedImage
+ *pCreateInfo,
+ VMA_SUBALLOCATION_TYPE_BUFFER,
+ 1, // allocationCount
+ pAllocation);
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordAllocateMemoryForBuffer(
+ allocator->GetCurrentFrameIndex(),
+ vkMemReq,
+ requiresDedicatedAllocation,
+ prefersDedicatedAllocation,
+ *pCreateInfo,
+ *pAllocation);
+ }
+#endif
+
+ if(pAllocationInfo && result == VK_SUCCESS)
+ {
+ allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
+ }
+
+ return result;
+}
+
+VkResult vmaAllocateMemoryForImage(
+ VmaAllocator allocator,
+ VkImage image,
+ const VmaAllocationCreateInfo* pCreateInfo,
+ VmaAllocation* pAllocation,
+ VmaAllocationInfo* pAllocationInfo)
+{
+ VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);
+
+ VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ VkMemoryRequirements vkMemReq = {};
+ bool requiresDedicatedAllocation = false;
+ bool prefersDedicatedAllocation = false;
+ allocator->GetImageMemoryRequirements(image, vkMemReq,
+ requiresDedicatedAllocation, prefersDedicatedAllocation);
+
+ VkResult result = allocator->AllocateMemory(
+ vkMemReq,
+ requiresDedicatedAllocation,
+ prefersDedicatedAllocation,
+ VK_NULL_HANDLE, // dedicatedBuffer
+ image, // dedicatedImage
+ *pCreateInfo,
+ VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
+ 1, // allocationCount
+ pAllocation);
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordAllocateMemoryForImage(
+ allocator->GetCurrentFrameIndex(),
+ vkMemReq,
+ requiresDedicatedAllocation,
+ prefersDedicatedAllocation,
+ *pCreateInfo,
+ *pAllocation);
+ }
+#endif
+
+ if(pAllocationInfo && result == VK_SUCCESS)
+ {
+ allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
+ }
+
+ return result;
+}
+
+void vmaFreeMemory(
+ VmaAllocator allocator,
+ VmaAllocation allocation)
+{
+ VMA_ASSERT(allocator);
+
+ if(allocation == VK_NULL_HANDLE)
+ {
+ return;
+ }
+
+ VMA_DEBUG_LOG("vmaFreeMemory");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordFreeMemory(
+ allocator->GetCurrentFrameIndex(),
+ allocation);
+ }
+#endif
+
+ allocator->FreeMemory(
+ 1, // allocationCount
+ &allocation);
+}
+
+void vmaFreeMemoryPages(
+ VmaAllocator allocator,
+ size_t allocationCount,
+ VmaAllocation* pAllocations)
+{
+ if(allocationCount == 0)
+ {
+ return;
+ }
+
+ VMA_ASSERT(allocator);
+
+ VMA_DEBUG_LOG("vmaFreeMemoryPages");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordFreeMemoryPages(
+ allocator->GetCurrentFrameIndex(),
+ (uint64_t)allocationCount,
+ pAllocations);
+ }
+#endif
+
+ allocator->FreeMemory(allocationCount, pAllocations);
+}
+
+VkResult vmaResizeAllocation(
+ VmaAllocator allocator,
+ VmaAllocation allocation,
+ VkDeviceSize newSize)
+{
+ VMA_ASSERT(allocator && allocation);
+
+ VMA_DEBUG_LOG("vmaResizeAllocation");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordResizeAllocation(
+ allocator->GetCurrentFrameIndex(),
+ allocation,
+ newSize);
+ }
+#endif
+
+ return allocator->ResizeAllocation(allocation, newSize);
+}
+
+void vmaGetAllocationInfo(
+ VmaAllocator allocator,
+ VmaAllocation allocation,
+ VmaAllocationInfo* pAllocationInfo)
+{
+ VMA_ASSERT(allocator && allocation && pAllocationInfo);
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordGetAllocationInfo(
+ allocator->GetCurrentFrameIndex(),
+ allocation);
+ }
+#endif
+
+ allocator->GetAllocationInfo(allocation, pAllocationInfo);
+}
+
+VkBool32 vmaTouchAllocation(
+ VmaAllocator allocator,
+ VmaAllocation allocation)
+{
+ VMA_ASSERT(allocator && allocation);
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordTouchAllocation(
+ allocator->GetCurrentFrameIndex(),
+ allocation);
+ }
+#endif
+
+ return allocator->TouchAllocation(allocation);
+}
+
+void vmaSetAllocationUserData(
+ VmaAllocator allocator,
+ VmaAllocation allocation,
+ void* pUserData)
+{
+ VMA_ASSERT(allocator && allocation);
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ allocation->SetUserData(allocator, pUserData);
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordSetAllocationUserData(
+ allocator->GetCurrentFrameIndex(),
+ allocation,
+ pUserData);
+ }
+#endif
+}
+
+void vmaCreateLostAllocation(
+ VmaAllocator allocator,
+ VmaAllocation* pAllocation)
+{
+ VMA_ASSERT(allocator && pAllocation);
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK;
+
+ allocator->CreateLostAllocation(pAllocation);
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordCreateLostAllocation(
+ allocator->GetCurrentFrameIndex(),
+ *pAllocation);
+ }
+#endif
+}
+
+VkResult vmaMapMemory(
+ VmaAllocator allocator,
+ VmaAllocation allocation,
+ void** ppData)
+{
+ VMA_ASSERT(allocator && allocation && ppData);
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ VkResult res = allocator->Map(allocation, ppData);
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordMapMemory(
+ allocator->GetCurrentFrameIndex(),
+ allocation);
+ }
+#endif
+
+ return res;
+}
+
+void vmaUnmapMemory(
+ VmaAllocator allocator,
+ VmaAllocation allocation)
+{
+ VMA_ASSERT(allocator && allocation);
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordUnmapMemory(
+ allocator->GetCurrentFrameIndex(),
+ allocation);
+ }
+#endif
+
+ allocator->Unmap(allocation);
+}
+
+void vmaFlushAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
+{
+ VMA_ASSERT(allocator && allocation);
+
+ VMA_DEBUG_LOG("vmaFlushAllocation");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordFlushAllocation(
+ allocator->GetCurrentFrameIndex(),
+ allocation, offset, size);
+ }
+#endif
+}
+
+void vmaInvalidateAllocation(VmaAllocator allocator, VmaAllocation allocation, VkDeviceSize offset, VkDeviceSize size)
+{
+ VMA_ASSERT(allocator && allocation);
+
+ VMA_DEBUG_LOG("vmaInvalidateAllocation");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordInvalidateAllocation(
+ allocator->GetCurrentFrameIndex(),
+ allocation, offset, size);
+ }
+#endif
+}
+
+VkResult vmaCheckCorruption(VmaAllocator allocator, uint32_t memoryTypeBits)
+{
+ VMA_ASSERT(allocator);
+
+ VMA_DEBUG_LOG("vmaCheckCorruption");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ return allocator->CheckCorruption(memoryTypeBits);
+}
+
+VkResult vmaDefragment(
+ VmaAllocator allocator,
+ VmaAllocation* pAllocations,
+ size_t allocationCount,
+ VkBool32* pAllocationsChanged,
+ const VmaDefragmentationInfo *pDefragmentationInfo,
+ VmaDefragmentationStats* pDefragmentationStats)
+{
+ // Deprecated interface, reimplemented using new one.
+
+ VmaDefragmentationInfo2 info2 = {};
+ info2.allocationCount = (uint32_t)allocationCount;
+ info2.pAllocations = pAllocations;
+ info2.pAllocationsChanged = pAllocationsChanged;
+ if(pDefragmentationInfo != VMA_NULL)
+ {
+ info2.maxCpuAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
+ info2.maxCpuBytesToMove = pDefragmentationInfo->maxBytesToMove;
+ }
+ else
+ {
+ info2.maxCpuAllocationsToMove = UINT32_MAX;
+ info2.maxCpuBytesToMove = VK_WHOLE_SIZE;
+ }
+ // info2.flags, maxGpuAllocationsToMove, maxGpuBytesToMove, commandBuffer deliberately left zero.
+
+ VmaDefragmentationContext ctx;
+ VkResult res = vmaDefragmentationBegin(allocator, &info2, pDefragmentationStats, &ctx);
+ if(res == VK_NOT_READY)
+ {
+ res = vmaDefragmentationEnd( allocator, ctx);
+ }
+ return res;
+}
+
+VkResult vmaDefragmentationBegin(
+ VmaAllocator allocator,
+ const VmaDefragmentationInfo2* pInfo,
+ VmaDefragmentationStats* pStats,
+ VmaDefragmentationContext *pContext)
+{
+ VMA_ASSERT(allocator && pInfo && pContext);
+
+ // Degenerate case: Nothing to defragment.
+ if(pInfo->allocationCount == 0 && pInfo->poolCount == 0)
+ {
+ return VK_SUCCESS;
+ }
+
+ VMA_ASSERT(pInfo->allocationCount == 0 || pInfo->pAllocations != VMA_NULL);
+ VMA_ASSERT(pInfo->poolCount == 0 || pInfo->pPools != VMA_NULL);
+ VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->allocationCount, pInfo->pAllocations));
+ VMA_HEAVY_ASSERT(VmaValidatePointerArray(pInfo->poolCount, pInfo->pPools));
+
+ VMA_DEBUG_LOG("vmaDefragmentationBegin");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ VkResult res = allocator->DefragmentationBegin(*pInfo, pStats, pContext);
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordDefragmentationBegin(
+ allocator->GetCurrentFrameIndex(), *pInfo, *pContext);
+ }
+#endif
+
+ return res;
+}
+
+VkResult vmaDefragmentationEnd(
+ VmaAllocator allocator,
+ VmaDefragmentationContext context)
+{
+ VMA_ASSERT(allocator);
+
+ VMA_DEBUG_LOG("vmaDefragmentationEnd");
+
+ if(context != VK_NULL_HANDLE)
+ {
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordDefragmentationEnd(
+ allocator->GetCurrentFrameIndex(), context);
+ }
+#endif
+
+ return allocator->DefragmentationEnd(context);
+ }
+ else
+ {
+ return VK_SUCCESS;
+ }
+}
+
+VkResult vmaBindBufferMemory(
+ VmaAllocator allocator,
+ VmaAllocation allocation,
+ VkBuffer buffer)
+{
+ VMA_ASSERT(allocator && allocation && buffer);
+
+ VMA_DEBUG_LOG("vmaBindBufferMemory");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ return allocator->BindBufferMemory(allocation, buffer);
+}
+
+VkResult vmaBindImageMemory(
+ VmaAllocator allocator,
+ VmaAllocation allocation,
+ VkImage image)
+{
+ VMA_ASSERT(allocator && allocation && image);
+
+ VMA_DEBUG_LOG("vmaBindImageMemory");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ return allocator->BindImageMemory(allocation, image);
+}
+
+VkResult vmaCreateBuffer(
+ VmaAllocator allocator,
+ const VkBufferCreateInfo* pBufferCreateInfo,
+ const VmaAllocationCreateInfo* pAllocationCreateInfo,
+ VkBuffer* pBuffer,
+ VmaAllocation* pAllocation,
+ VmaAllocationInfo* pAllocationInfo)
+{
+ VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);
+
+ if(pBufferCreateInfo->size == 0)
+ {
+ return VK_ERROR_VALIDATION_FAILED_EXT;
+ }
+
+ VMA_DEBUG_LOG("vmaCreateBuffer");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ *pBuffer = VK_NULL_HANDLE;
+ *pAllocation = VK_NULL_HANDLE;
+
+ // 1. Create VkBuffer.
+ VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(
+ allocator->m_hDevice,
+ pBufferCreateInfo,
+ allocator->GetAllocationCallbacks(),
+ pBuffer);
+ if(res >= 0)
+ {
+ // 2. vkGetBufferMemoryRequirements.
+ VkMemoryRequirements vkMemReq = {};
+ bool requiresDedicatedAllocation = false;
+ bool prefersDedicatedAllocation = false;
+ allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,
+ requiresDedicatedAllocation, prefersDedicatedAllocation);
+
+ // Make sure alignment requirements for specific buffer usages reported
+ // in Physical Device Properties are included in alignment reported by memory requirements.
+ if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT) != 0)
+ {
+ VMA_ASSERT(vkMemReq.alignment %
+ allocator->m_PhysicalDeviceProperties.limits.minTexelBufferOffsetAlignment == 0);
+ }
+ if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) != 0)
+ {
+ VMA_ASSERT(vkMemReq.alignment %
+ allocator->m_PhysicalDeviceProperties.limits.minUniformBufferOffsetAlignment == 0);
+ }
+ if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) != 0)
+ {
+ VMA_ASSERT(vkMemReq.alignment %
+ allocator->m_PhysicalDeviceProperties.limits.minStorageBufferOffsetAlignment == 0);
+ }
+
+ // 3. Allocate memory using allocator.
+ res = allocator->AllocateMemory(
+ vkMemReq,
+ requiresDedicatedAllocation,
+ prefersDedicatedAllocation,
+ *pBuffer, // dedicatedBuffer
+ VK_NULL_HANDLE, // dedicatedImage
+ *pAllocationCreateInfo,
+ VMA_SUBALLOCATION_TYPE_BUFFER,
+ 1, // allocationCount
+ pAllocation);
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordCreateBuffer(
+ allocator->GetCurrentFrameIndex(),
+ *pBufferCreateInfo,
+ *pAllocationCreateInfo,
+ *pAllocation);
+ }
+#endif
+
+ if(res >= 0)
+ {
+ // 3. Bind buffer with memory.
+ res = allocator->BindBufferMemory(*pAllocation, *pBuffer);
+ if(res >= 0)
+ {
+ // All steps succeeded.
+ #if VMA_STATS_STRING_ENABLED
+ (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);
+ #endif
+ if(pAllocationInfo != VMA_NULL)
+ {
+ allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
+ }
+
+ return VK_SUCCESS;
+ }
+ allocator->FreeMemory(
+ 1, // allocationCount
+ pAllocation);
+ *pAllocation = VK_NULL_HANDLE;
+ (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
+ *pBuffer = VK_NULL_HANDLE;
+ return res;
+ }
+ (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
+ *pBuffer = VK_NULL_HANDLE;
+ return res;
+ }
+ return res;
+}
+
+void vmaDestroyBuffer(
+ VmaAllocator allocator,
+ VkBuffer buffer,
+ VmaAllocation allocation)
+{
+ VMA_ASSERT(allocator);
+
+ if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
+ {
+ return;
+ }
+
+ VMA_DEBUG_LOG("vmaDestroyBuffer");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordDestroyBuffer(
+ allocator->GetCurrentFrameIndex(),
+ allocation);
+ }
+#endif
+
+ if(buffer != VK_NULL_HANDLE)
+ {
+ (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
+ }
+
+ if(allocation != VK_NULL_HANDLE)
+ {
+ allocator->FreeMemory(
+ 1, // allocationCount
+ &allocation);
+ }
+}
+
+VkResult vmaCreateImage(
+ VmaAllocator allocator,
+ const VkImageCreateInfo* pImageCreateInfo,
+ const VmaAllocationCreateInfo* pAllocationCreateInfo,
+ VkImage* pImage,
+ VmaAllocation* pAllocation,
+ VmaAllocationInfo* pAllocationInfo)
+{
+ VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);
+
+ if(pImageCreateInfo->extent.width == 0 ||
+ pImageCreateInfo->extent.height == 0 ||
+ pImageCreateInfo->extent.depth == 0 ||
+ pImageCreateInfo->mipLevels == 0 ||
+ pImageCreateInfo->arrayLayers == 0)
+ {
+ return VK_ERROR_VALIDATION_FAILED_EXT;
+ }
+
+ VMA_DEBUG_LOG("vmaCreateImage");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+ *pImage = VK_NULL_HANDLE;
+ *pAllocation = VK_NULL_HANDLE;
+
+ // 1. Create VkImage.
+ VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(
+ allocator->m_hDevice,
+ pImageCreateInfo,
+ allocator->GetAllocationCallbacks(),
+ pImage);
+ if(res >= 0)
+ {
+ VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
+ VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
+ VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
+
+ // 2. Allocate memory using allocator.
+ VkMemoryRequirements vkMemReq = {};
+ bool requiresDedicatedAllocation = false;
+ bool prefersDedicatedAllocation = false;
+ allocator->GetImageMemoryRequirements(*pImage, vkMemReq,
+ requiresDedicatedAllocation, prefersDedicatedAllocation);
+
+ res = allocator->AllocateMemory(
+ vkMemReq,
+ requiresDedicatedAllocation,
+ prefersDedicatedAllocation,
+ VK_NULL_HANDLE, // dedicatedBuffer
+ *pImage, // dedicatedImage
+ *pAllocationCreateInfo,
+ suballocType,
+ 1, // allocationCount
+ pAllocation);
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordCreateImage(
+ allocator->GetCurrentFrameIndex(),
+ *pImageCreateInfo,
+ *pAllocationCreateInfo,
+ *pAllocation);
+ }
+#endif
+
+ if(res >= 0)
+ {
+ // 3. Bind image with memory.
+ res = allocator->BindImageMemory(*pAllocation, *pImage);
+ if(res >= 0)
+ {
+ // All steps succeeded.
+ #if VMA_STATS_STRING_ENABLED
+ (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);
+ #endif
+ if(pAllocationInfo != VMA_NULL)
+ {
+ allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
+ }
+
+ return VK_SUCCESS;
+ }
+ allocator->FreeMemory(
+ 1, // allocationCount
+ pAllocation);
+ *pAllocation = VK_NULL_HANDLE;
+ (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
+ *pImage = VK_NULL_HANDLE;
+ return res;
+ }
+ (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
+ *pImage = VK_NULL_HANDLE;
+ return res;
+ }
+ return res;
+}
+
+void vmaDestroyImage(
+ VmaAllocator allocator,
+ VkImage image,
+ VmaAllocation allocation)
+{
+ VMA_ASSERT(allocator);
+
+ if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)
+ {
+ return;
+ }
+
+ VMA_DEBUG_LOG("vmaDestroyImage");
+
+ VMA_DEBUG_GLOBAL_MUTEX_LOCK
+
+#if VMA_RECORDING_ENABLED
+ if(allocator->GetRecorder() != VMA_NULL)
+ {
+ allocator->GetRecorder()->RecordDestroyImage(
+ allocator->GetCurrentFrameIndex(),
+ allocation);
+ }
+#endif
+
+ if(image != VK_NULL_HANDLE)
+ {
+ (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
+ }
+ if(allocation != VK_NULL_HANDLE)
+ {
+ allocator->FreeMemory(
+ 1, // allocationCount
+ &allocation);
+ }
+}
+
+#endif // #ifdef VMA_IMPLEMENTATION
diff --git a/src/3rdparty/double-conversion/README b/src/3rdparty/double-conversion/README
deleted file mode 100644
index 75d08df5fe..0000000000
--- a/src/3rdparty/double-conversion/README
+++ /dev/null
@@ -1,8 +0,0 @@
-This is a copy of the library for binary-decimal and decimal-binary
-conversion routines for IEEE doubles, available from
-
- https://github.com/google/double-conversion
-
-commit 2fb03de56faa32bbba5e02222528e7b760f71d77
-
-See the LICENSE file for license information.
diff --git a/src/3rdparty/double-conversion/double-conversion.cc b/src/3rdparty/double-conversion/double-conversion.cc
index ecd1a5ef3f..8c52755cfb 100644
--- a/src/3rdparty/double-conversion/double-conversion.cc
+++ b/src/3rdparty/double-conversion/double-conversion.cc
@@ -38,6 +38,11 @@
#include <double-conversion/strtod.h>
#include <double-conversion/utils.h>
+// Fix warning C4244: 'argument': conversion from 'const uc16' to 'char', possible loss of data
+#ifdef _MSC_VER
+ __pragma(warning(disable: 4244))
+#endif
+
namespace double_conversion {
const DoubleToStringConverter& DoubleToStringConverter::EcmaScriptConverter() {
@@ -250,6 +255,12 @@ bool DoubleToStringConverter::ToExponential(
const int kDecimalRepCapacity = kMaxExponentialDigits + 2;
ASSERT(kDecimalRepCapacity > kBase10MaximalLength);
char decimal_rep[kDecimalRepCapacity];
+#ifndef NDEBUG
+ // Problem: there is an assert in StringBuilder::AddSubstring() that
+ // will pass this buffer to strlen(), and this buffer is not generally
+ // null-terminated.
+ memset(decimal_rep, 0, sizeof(decimal_rep));
+#endif
int decimal_rep_length;
if (requested_digits == -1) {
@@ -529,7 +540,7 @@ static double SignedZero(bool sign) {
// because it constant-propagated the radix and concluded that the last
// condition was always true. By moving it into a separate function the
// compiler wouldn't warn anymore.
-#if _MSC_VER
+#ifdef _MSC_VER
#pragma optimize("",off)
static bool IsDecimalDigitForRadix(int c, int radix) {
return '0' <= c && c <= '9' && (c - '0') < radix;
@@ -553,7 +564,7 @@ static bool IsCharacterDigitForRadix(int c, int radix, char a_character) {
// Returns true, when the iterator is equal to end.
template<class Iterator>
-static bool Advance (Iterator* it, char separator, int base, Iterator& end) {
+static bool Advance (Iterator* it, uc16 separator, int base, Iterator& end) {
if (separator == StringToDoubleConverter::kNoSeparator) {
++(*it);
return *it == end;
@@ -581,7 +592,7 @@ static bool Advance (Iterator* it, char separator, int base, Iterator& end) {
template<class Iterator>
static bool IsHexFloatString(Iterator start,
Iterator end,
- char separator,
+ uc16 separator,
bool allow_trailing_junk) {
ASSERT(start != end);
@@ -598,8 +609,8 @@ static bool IsHexFloatString(Iterator start,
saw_digit = true;
if (Advance(&current, separator, 16, end)) return false;
}
- if (!saw_digit) return false; // Only the '.', but no digits.
}
+ if (!saw_digit) return false;
if (*current != 'p' && *current != 'P') return false;
if (Advance(&current, separator, 16, end)) return false;
if (*current == '+' || *current == '-') {
@@ -622,7 +633,7 @@ template <int radix_log_2, class Iterator>
static double RadixStringToIeee(Iterator* current,
Iterator end,
bool sign,
- char separator,
+ uc16 separator,
bool parse_as_hex_float,
bool allow_trailing_junk,
double junk_string_value,
@@ -757,7 +768,11 @@ static double RadixStringToIeee(Iterator* current,
}
int written_exponent = 0;
while (IsDecimalDigitForRadix(**current, 10)) {
- written_exponent = 10 * written_exponent + **current - '0';
+ // No need to read exponents if they are too big. That could potentially overflow
+ // the `written_exponent` variable.
+ if (abs(written_exponent) <= 100 * Double::kMaxExponent) {
+ written_exponent = 10 * written_exponent + **current - '0';
+ }
if (Advance(current, separator, radix, end)) break;
}
if (is_negative) written_exponent = -written_exponent;
@@ -893,10 +908,11 @@ double StringToDoubleConverter::StringToIeee(
(*current == 'x' || *current == 'X')) {
++current;
+ if (current == end) return junk_string_value_; // "0x"
+
bool parse_as_hex_float = (flags_ & ALLOW_HEX_FLOATS) &&
IsHexFloatString(current, end, separator_, allow_trailing_junk);
- if (current == end) return junk_string_value_; // "0x"
if (!parse_as_hex_float && !isDigit(*current, 16)) {
return junk_string_value_;
}
diff --git a/src/3rdparty/double-conversion/double-conversion.pri b/src/3rdparty/double-conversion/double-conversion.pri
index 6564f960d4..395c4682f9 100644
--- a/src/3rdparty/double-conversion/double-conversion.pri
+++ b/src/3rdparty/double-conversion/double-conversion.pri
@@ -1,4 +1,4 @@
-INCLUDEPATH += $$PWD/.. $$PWD/include $$PWD/include/double-conversion
+INCLUDEPATH += $$PWD/.. $$PWD/include
SOURCES += \
$$PWD/bignum.cc \
$$PWD/bignum-dtoa.cc \
@@ -20,5 +20,3 @@ HEADERS += \
$$PWD/ieee.h \
$$PWD/strtod.h \
$$PWD/include/double-conversion/utils.h
-
-OTHER_FILES += README
diff --git a/src/3rdparty/double-conversion/ieee.h b/src/3rdparty/double-conversion/ieee.h
index baaeced31c..6d23cc71cf 100644
--- a/src/3rdparty/double-conversion/ieee.h
+++ b/src/3rdparty/double-conversion/ieee.h
@@ -47,6 +47,8 @@ class Double {
static const uint64_t kHiddenBit = UINT64_2PART_C(0x00100000, 00000000);
static const int kPhysicalSignificandSize = 52; // Excludes the hidden bit.
static const int kSignificandSize = 53;
+ static const int kExponentBias = 0x3FF + kPhysicalSignificandSize;
+ static const int kMaxExponent = 0x7FF - kExponentBias;
Double() : d64_(0) {}
explicit Double(double d) : d64_(double_to_uint64(d)) {}
@@ -222,9 +224,7 @@ class Double {
}
private:
- static const int kExponentBias = 0x3FF + kPhysicalSignificandSize;
static const int kDenormalExponent = -kExponentBias + 1;
- static const int kMaxExponent = 0x7FF - kExponentBias;
static const uint64_t kInfinity = UINT64_2PART_C(0x7FF00000, 00000000);
static const uint64_t kNaN = UINT64_2PART_C(0x7FF80000, 00000000);
diff --git a/src/3rdparty/double-conversion/include/double-conversion/utils.h b/src/3rdparty/double-conversion/include/double-conversion/utils.h
index 7622fe6162..70e697ca00 100644
--- a/src/3rdparty/double-conversion/include/double-conversion/utils.h
+++ b/src/3rdparty/double-conversion/include/double-conversion/utils.h
@@ -56,6 +56,13 @@ inline void abort_noreturn() { abort(); }
#endif
#endif
+#ifndef DOUBLE_CONVERSION_UNUSED
+#ifdef __GNUC__
+#define DOUBLE_CONVERSION_UNUSED __attribute__((unused))
+#else
+#define DOUBLE_CONVERSION_UNUSED
+#endif
+#endif
// Double operations detection based on target architecture.
// Linux uses a 80bit wide floating point stack on x86. This induces double
@@ -91,10 +98,11 @@ int main(int argc, char** argv) {
defined(_POWER) || defined(_ARCH_PPC) || defined(_ARCH_PPC64) || \
defined(__sparc__) || defined(__sparc) || defined(__s390__) || \
defined(__SH4__) || defined(__alpha__) || \
- defined(_MIPS_ARCH_MIPS32R2) || \
+ defined(_MIPS_ARCH_MIPS32R2) || defined(__ARMEB__) ||\
defined(__AARCH64EL__) || defined(__aarch64__) || defined(__AARCH64EB__) || \
- defined(__riscv) || defined(__EMSCRIPTEN__) || \
- defined(__or1k__)
+ defined(__riscv) || \
+ defined(__or1k__) || defined(__arc__) || \
+ defined(__EMSCRIPTEN__)
#define DOUBLE_CONVERSION_CORRECT_DOUBLE_OPERATIONS 1
#elif defined(__mc68000__) || \
defined(__pnacl__) || defined(__native_client__)
@@ -343,6 +351,7 @@ inline Dest BitCast(const Source& source) {
static_assert(sizeof(Dest) == sizeof(Source),
"source and destination size mismatch");
#else
+ DOUBLE_CONVERSION_UNUSED
typedef char VerifySizesAreEqual[sizeof(Dest) == sizeof(Source) ? 1 : -1];
#endif
diff --git a/src/3rdparty/double-conversion/qt_attribution.json b/src/3rdparty/double-conversion/qt_attribution.json
index 1d244a69b4..86193e3b50 100644
--- a/src/3rdparty/double-conversion/qt_attribution.json
+++ b/src/3rdparty/double-conversion/qt_attribution.json
@@ -5,8 +5,8 @@
"QtUsage": "Used in Qt Core. Configure with -system-doubleconversion or -no-doubleconversion to avoid.",
"Homepage": "https://github.com/google/double-conversion",
- "Version": "3.1.1",
- "DownloadLocation": "https://github.com/google/double-conversion/commit/4199ef3d456ed0549e5665cf4186f0ee6210db3b",
+ "Version": "3.1.5",
+ "DownloadLocation": "https://github.com/google/double-conversion/commit/5fa81e88ef24e735b4283b8f7454dc59693ac1fc",
"License": "BSD 3-clause \"New\" or \"Revised\" License",
"LicenseId": "BSD-3-Clause",
"LicenseFile": "LICENSE",
diff --git a/src/3rdparty/forkfd/forkfd.c b/src/3rdparty/forkfd/forkfd.c
index bef109e401..e4f3bd85de 100644
--- a/src/3rdparty/forkfd/forkfd.c
+++ b/src/3rdparty/forkfd/forkfd.c
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 Intel Corporation.
+** Copyright (C) 2019 Intel Corporation.
** Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -59,9 +59,6 @@
# define HAVE_PIPE2 1
# endif
#endif
-#if defined(__FreeBSD__) && __FreeBSD__ >= 9
-# include <sys/procdesc.h>
-#endif
#if _POSIX_VERSION-0 >= 200809L || _XOPEN_VERSION-0 >= 500
# define HAVE_WAITID 1
@@ -93,9 +90,11 @@
# endif
#endif
-#ifndef FFD_ATOMIC_RELAXED
-# include "forkfd_gcc.h"
-#endif
+#include "forkfd_atomic.h"
+
+static int system_has_forkfd(void);
+static int system_forkfd(int flags, pid_t *ppid, int *system);
+static int system_forkfd_wait(int ffd, struct forkfd_info *info, struct rusage *rusage);
#define CHILDREN_IN_SMALL_ARRAY 16
#define CHILDREN_IN_BIG_ARRAY 256
@@ -267,7 +266,7 @@ static int tryReaping(pid_t pid, struct pipe_payload *payload)
static void freeInfo(Header *header, ProcessInfo *entry)
{
entry->deathPipe = -1;
- entry->pid = 0;
+ ffd_atomic_store(&entry->pid, 0, FFD_ATOMIC_RELEASE);
(void)ffd_atomic_add_fetch(&header->busyCount, -1, FFD_ATOMIC_RELEASE);
assert(header->busyCount >= 0);
@@ -448,6 +447,37 @@ static void ignore_sigpipe()
#endif
}
+#if defined(__GNUC__) && (!defined(__FreeBSD__) || __FreeBSD__ < 10)
+__attribute((destructor, unused)) static void cleanup();
+#endif
+
+static void cleanup()
+{
+ BigArray *array;
+ /* This function is not thread-safe!
+ * It must only be called when the process is shutting down.
+ * At shutdown, we expect no one to be calling forkfd(), so we don't
+ * need to be thread-safe with what is done there.
+ *
+ * But SIGCHLD might be delivered to any thread, including this one.
+ * There's no way to prevent that. The correct solution would be to
+ * cooperatively delete. We don't do that.
+ */
+ if (ffd_atomic_load(&forkfd_status, FFD_ATOMIC_RELAXED) == 0)
+ return;
+
+ /* notify the handler that we're no longer in operation */
+ ffd_atomic_store(&forkfd_status, 0, FFD_ATOMIC_RELAXED);
+
+ /* free any arrays we might have */
+ array = ffd_atomic_load(&children.header.nextArray, FFD_ATOMIC_ACQUIRE);
+ while (array != NULL) {
+ BigArray *next = ffd_atomic_load(&array->header.nextArray, FFD_ATOMIC_ACQUIRE);
+ free(array);
+ array = next;
+ }
+}
+
static void forkfd_initialize()
{
#if defined(HAVE_BROKEN_WAITID)
@@ -489,44 +519,15 @@ static void forkfd_initialize()
ignore_sigpipe();
#endif
-#ifndef __GNUC__
+#ifdef __GNUC__
+ (void) cleanup; /* suppress unused static function warning */
+#else
atexit(cleanup);
#endif
ffd_atomic_store(&forkfd_status, 1, FFD_ATOMIC_RELAXED);
}
-#ifdef __GNUC__
-__attribute((destructor, unused)) static void cleanup();
-#endif
-
-static void cleanup()
-{
- BigArray *array;
- /* This function is not thread-safe!
- * It must only be called when the process is shutting down.
- * At shutdown, we expect no one to be calling forkfd(), so we don't
- * need to be thread-safe with what is done there.
- *
- * But SIGCHLD might be delivered to any thread, including this one.
- * There's no way to prevent that. The correct solution would be to
- * cooperatively delete. We don't do that.
- */
- if (ffd_atomic_load(&forkfd_status, FFD_ATOMIC_RELAXED) == 0)
- return;
-
- /* notify the handler that we're no longer in operation */
- ffd_atomic_store(&forkfd_status, 0, FFD_ATOMIC_RELAXED);
-
- /* free any arrays we might have */
- array = children.header.nextArray;
- while (array != NULL) {
- BigArray *next = array->header.nextArray;
- free(array);
- array = next;
- }
-}
-
static int create_pipe(int filedes[], int flags)
{
int ret = -1;
@@ -565,55 +566,6 @@ static int create_pipe(int filedes[], int flags)
return ret;
}
-#if defined(FORKFD_NO_SPAWNFD) && defined(__FreeBSD__) && __FreeBSD__ >= 9
-# if __FreeBSD__ == 9
-/* PROCDESC is an optional feature in the kernel and wasn't enabled
- * by default on FreeBSD 9. So we need to check for it at runtime. */
-static ffd_atomic_int system_has_forkfd = FFD_ATOMIC_INIT(1);
-# else
-/* On FreeBSD 10, PROCDESC was enabled by default. On v11, it's not an option
- * anymore and can't be disabled. */
-static const int system_has_forkfd = 1;
-# endif
-
-static int system_forkfd(int flags, pid_t *ppid)
-{
- int ret;
- pid_t pid;
- pid = pdfork(&ret, PD_DAEMON);
- if (__builtin_expect(pid == -1, 0)) {
-# if __FreeBSD__ == 9
- if (errno == ENOSYS) {
- /* PROCDESC wasn't compiled into the kernel: don't try it again. */
- ffd_atomic_store(&system_has_forkfd, 0, FFD_ATOMIC_RELAXED);
- }
-# endif
- return -1;
- }
- if (pid == 0) {
- /* child process */
- return FFD_CHILD_PROCESS;
- }
-
- /* parent process */
- if (flags & FFD_CLOEXEC)
- fcntl(ret, F_SETFD, FD_CLOEXEC);
- if (flags & FFD_NONBLOCK)
- fcntl(ret, F_SETFL, fcntl(ret, F_GETFL) | O_NONBLOCK);
- if (ppid)
- *ppid = pid;
- return ret;
-}
-#else
-static const int system_has_forkfd = 0;
-static int system_forkfd(int flags, pid_t *ppid)
-{
- (void)flags;
- (void)ppid;
- return -1;
-}
-#endif
-
#ifndef FORKFD_NO_FORKFD
/**
* @brief forkfd returns a file descriptor representing a child process
@@ -661,11 +613,9 @@ int forkfd(int flags, pid_t *ppid)
int efd;
#endif
- if (system_has_forkfd) {
- ret = system_forkfd(flags, ppid);
- if (system_has_forkfd)
- return ret;
- }
+ fd = system_forkfd(flags, ppid, &ret);
+ if (ret)
+ return fd;
(void) pthread_once(&forkfd_initialization, forkfd_initialize);
@@ -790,7 +740,7 @@ int spawnfd(int flags, pid_t *ppid, const char *path, const posix_spawn_file_act
/* we can only do work if we have a way to start the child in stopped mode;
* otherwise, we have a major race condition. */
- assert(!system_has_forkfd);
+ assert(!system_has_forkfd());
(void) pthread_once(&forkfd_initialization, forkfd_initialize);
@@ -843,30 +793,13 @@ out:
#endif // _POSIX_SPAWN && !FORKFD_NO_SPAWNFD
-int forkfd_wait(int ffd, forkfd_info *info, struct rusage *rusage)
+int forkfd_wait(int ffd, struct forkfd_info *info, struct rusage *rusage)
{
struct pipe_payload payload;
int ret;
- if (system_has_forkfd) {
-#if defined(__FreeBSD__) && __FreeBSD__ >= 9
- pid_t pid;
- int status;
- int options = WEXITED;
-
- ret = pdgetpid(ffd, &pid);
- if (ret == -1)
- return ret;
- ret = fcntl(ffd, F_GETFL);
- if (ret == -1)
- return ret;
- options |= (ret & O_NONBLOCK) ? WNOHANG : 0;
- ret = wait4(pid, &status, options, rusage);
- if (ret != -1 && info)
- convertStatusToForkfdInfo(status, info);
- return ret == -1 ? -1 : 0;
-#endif
- }
+ if (system_has_forkfd())
+ return system_forkfd_wait(ffd, info, rusage);
ret = read(ffd, &payload, sizeof(payload));
if (ret == -1)
@@ -886,3 +819,28 @@ int forkfd_close(int ffd)
{
return close(ffd);
}
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 9
+# include "forkfd_freebsd.c"
+#else
+int system_has_forkfd()
+{
+ return 0;
+}
+
+int system_forkfd(int flags, pid_t *ppid, int *system)
+{
+ (void)flags;
+ (void)ppid;
+ *system = 0;
+ return -1;
+}
+
+int system_forkfd_wait(int ffd, struct forkfd_info *info, struct rusage *rusage)
+{
+ (void)ffd;
+ (void)info;
+ (void)rusage;
+ return -1;
+}
+#endif
diff --git a/src/3rdparty/forkfd/forkfd.h b/src/3rdparty/forkfd/forkfd.h
index 958321c299..eb121de593 100644
--- a/src/3rdparty/forkfd/forkfd.h
+++ b/src/3rdparty/forkfd/forkfd.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 Intel Corporation.
+** Copyright (C) 2019 Intel Corporation.
**
** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to deal
@@ -27,6 +27,7 @@
#include <fcntl.h>
#include <stdint.h>
+#include <sys/wait.h>
#include <unistd.h> // to get the POSIX flags
#if _POSIX_SPAWN > 0
@@ -48,7 +49,7 @@ struct forkfd_info {
};
int forkfd(int flags, pid_t *ppid);
-int forkfd_wait(int ffd, forkfd_info *info, struct rusage *rusage);
+int forkfd_wait(int ffd, struct forkfd_info *info, struct rusage *rusage);
int forkfd_close(int ffd);
#if _POSIX_SPAWN > 0
diff --git a/src/3rdparty/forkfd/forkfd_atomic.h b/src/3rdparty/forkfd/forkfd_atomic.h
new file mode 100644
index 0000000000..394e30d26c
--- /dev/null
+++ b/src/3rdparty/forkfd/forkfd_atomic.h
@@ -0,0 +1,39 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Intel Corporation.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and associated documentation files (the "Software"), to deal
+** in the Software without restriction, including without limitation the rights
+** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+** copies of the Software, and to permit persons to whom the Software is
+** furnished to do so, subject to the following conditions:
+**
+** The above copyright notice and this permission notice shall be included in
+** all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+** THE SOFTWARE.
+**
+****************************************************************************/
+
+#if !defined(FFD_ATOMIC_H) & !defined(FFD_ATOMIC_RELAXED)
+#define FFD_ATOMIC_H
+
+#if defined(__cplusplus) && __cplusplus >= 201103L
+# include "forkfd_c11.h"
+#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
+# include "forkfd_c11.h"
+#elif defined(__GNUC__)
+# include "forkfd_gcc.h"
+#endif
+
+#endif /* FFD_ATOMIC_h && FFD_ATOMIC_RELAXED */
+#ifndef FFD_ATOMIC_RELAXED
+# error "Could not determine atomics for this platform"
+#endif
diff --git a/src/3rdparty/forkfd/forkfd_c11.h b/src/3rdparty/forkfd/forkfd_c11.h
new file mode 100644
index 0000000000..f3dc2b5357
--- /dev/null
+++ b/src/3rdparty/forkfd/forkfd_c11.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Intel Corporation.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and associated documentation files (the "Software"), to deal
+** in the Software without restriction, including without limitation the rights
+** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+** copies of the Software, and to permit persons to whom the Software is
+** furnished to do so, subject to the following conditions:
+**
+** The above copyright notice and this permission notice shall be included in
+** all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+** THE SOFTWARE.
+**
+****************************************************************************/
+
+#ifndef FFD_ATOMIC_C11_H
+#define FFD_ATOMIC_C11_H
+
+/* atomics */
+/* Using the C11 <stdatomic.h> header or C++11's <atomic>
+ */
+
+#if defined(__cplusplus)
+# include <atomic>
+# define ffd_atomic_pointer(type) std::atomic<type*>
+# define FFD_ATOMIC_RELAXED std::memory_order_relaxed
+# define FFD_ATOMIC_ACQUIRE std::memory_order_acquire
+# define FFD_ATOMIC_RELEASE std::memory_order_release
+// acq_rel & cst not necessary
+typedef std::atomic_int ffd_atomic_int;
+#else
+# include <stdatomic.h>
+# define ffd_atomic_pointer(type) _Atomic(type*)
+# define FFD_ATOMIC_RELAXED memory_order_relaxed
+# define FFD_ATOMIC_ACQUIRE memory_order_acquire
+# define FFD_ATOMIC_RELEASE memory_order_release
+// acq_rel & cst not necessary
+
+typedef atomic_int ffd_atomic_int;
+#endif
+
+#define FFD_ATOMIC_INIT(val) ATOMIC_VAR_INIT(val)
+
+#define ffd_atomic_load(ptr, order) \
+ atomic_load_explicit(ptr, order)
+#define ffd_atomic_store(ptr, val, order) \
+ atomic_store_explicit(ptr, val, order)
+#define ffd_atomic_exchange(ptr,val,order) \
+ atomic_exchange_explicit(ptr, val, order)
+#define ffd_atomic_compare_exchange(ptr, expected, desired, order1, order2) \
+ atomic_compare_exchange_strong_explicit(ptr, expected, desired, order1, order2)
+#define ffd_atomic_add_fetch(ptr, val, order) \
+ (atomic_fetch_add_explicit(ptr, val, order) + (val))
+
+#endif
diff --git a/src/3rdparty/forkfd/forkfd_freebsd.c b/src/3rdparty/forkfd/forkfd_freebsd.c
new file mode 100644
index 0000000000..77ce3fcfad
--- /dev/null
+++ b/src/3rdparty/forkfd/forkfd_freebsd.c
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Intel Corporation.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and associated documentation files (the "Software"), to deal
+** in the Software without restriction, including without limitation the rights
+** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+** copies of the Software, and to permit persons to whom the Software is
+** furnished to do so, subject to the following conditions:
+**
+** The above copyright notice and this permission notice shall be included in
+** all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+** THE SOFTWARE.
+**
+****************************************************************************/
+
+#include "forkfd.h"
+
+#include <sys/types.h>
+#include <sys/procdesc.h>
+
+#include "forkfd_atomic.h"
+
+#if __FreeBSD__ >= 10
+/* On FreeBSD 10, PROCDESC was enabled by default. On v11, it's not an option
+ * anymore and can't be disabled. */
+static ffd_atomic_int system_forkfd_state = FFD_ATOMIC_INIT(1);
+#else
+static ffd_atomic_int system_forkfd_state = FFD_ATOMIC_INIT(0);
+#endif
+
+int system_has_forkfd()
+{
+ return ffd_atomic_load(&system_forkfd_state, FFD_ATOMIC_RELAXED) > 0;
+}
+
+int system_forkfd(int flags, pid_t *ppid, int *system)
+{
+ int ret;
+ pid_t pid;
+
+ int state = ffd_atomic_load(&system_forkfd_state, FFD_ATOMIC_RELAXED);
+ *system = 0;
+ if (state < 0)
+ return -1;
+
+ pid = pdfork(&ret, PD_DAEMON);
+# if __FreeBSD__ == 9
+ if (state == 0 && pid != 0) {
+ /* Parent process: remember whether PROCDESC was compiled into the kernel */
+ state = (pid == -1 && errno == ENOSYS) ? -1 : 1;
+ ffd_atomic_store(&system_forkfd_state, state, FFD_ATOMIC_RELAXED);
+ }
+ if (state < 0)
+ return -1;
+# endif
+ *system = 1;
+ if (__builtin_expect(pid == -1, 0))
+ return -1;
+
+ if (pid == 0) {
+ /* child process */
+ return FFD_CHILD_PROCESS;
+ }
+
+ /* parent process */
+ if (flags & FFD_CLOEXEC)
+ fcntl(ret, F_SETFD, FD_CLOEXEC);
+ if (flags & FFD_NONBLOCK)
+ fcntl(ret, F_SETFL, fcntl(ret, F_GETFL) | O_NONBLOCK);
+ if (ppid)
+ *ppid = pid;
+ return ret;
+}
+
+int system_forkfd_wait(int ffd, struct forkfd_info *info, struct rusage *rusage)
+{
+ pid_t pid;
+ int status;
+ int options = WEXITED;
+
+ int ret = pdgetpid(ffd, &pid);
+ if (ret == -1)
+ return ret;
+ ret = fcntl(ffd, F_GETFL);
+ if (ret == -1)
+ return ret;
+ options |= (ret & O_NONBLOCK) ? WNOHANG : 0;
+ ret = wait4(pid, &status, options, rusage);
+ if (ret != -1 && info)
+ convertStatusToForkfdInfo(status, info);
+ return ret == -1 ? -1 : 0;
+}
diff --git a/src/3rdparty/gradle/gradle/wrapper/gradle-wrapper.properties b/src/3rdparty/gradle/gradle/wrapper/gradle-wrapper.properties
index bf3de21830..4b7e1f3d38 100644
--- a/src/3rdparty/gradle/gradle/wrapper/gradle-wrapper.properties
+++ b/src/3rdparty/gradle/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/src/3rdparty/harfbuzz/src/harfbuzz-indic.cpp b/src/3rdparty/harfbuzz/src/harfbuzz-indic.cpp
index 704ea9774a..de3bcb2bbf 100644
--- a/src/3rdparty/harfbuzz/src/harfbuzz-indic.cpp
+++ b/src/3rdparty/harfbuzz/src/harfbuzz-indic.cpp
@@ -1471,7 +1471,7 @@ static bool indic_shape_syllable(HB_Bool openType, HB_ShaperItem *item, bool inv
while (finalOrder[toMove].form && fixed < len-1) {
IDEBUG(" fixed = %d, toMove=%d, moving form %d with pos %d", fixed, toMove, finalOrder[toMove].form, finalOrder[toMove].position);
for (i = fixed; i < len; i++) {
-// IDEBUG() << " i=" << i << "uc=" << hex << uc[i] << "form=" << form(uc[i])
+// IDEBUG() << " i=" << i << "uc=" << Qt::hex << uc[i] << "form=" << form(uc[i])
// << "position=" << position[i];
if (form(uc[i]) == finalOrder[toMove].form &&
position[i] == finalOrder[toMove].position) {
diff --git a/src/3rdparty/harfbuzz/tests/shaping/main.cpp b/src/3rdparty/harfbuzz/tests/shaping/main.cpp
index 10818c565c..16f469029b 100644
--- a/src/3rdparty/harfbuzz/tests/shaping/main.cpp
+++ b/src/3rdparty/harfbuzz/tests/shaping/main.cpp
@@ -370,7 +370,7 @@ void tst_QScriptEngine::greek()
QString str;
str.append(uc);
if (str.normalized(QString::NormalizationForm_D).normalized(QString::NormalizationForm_C) != str) {
- //qDebug() << "skipping" << hex << uc;
+ //qDebug() << "skipping" << Qt::hex << uc;
continue;
}
if (uc == 0x1fc1 || uc == 0x1fed)
@@ -389,7 +389,7 @@ void tst_QScriptEngine::greek()
QString str;
str.append(uc);
if (str.normalized(QString::NormalizationForm_D).normalized(QString::NormalizationForm_C) != str) {
- //qDebug() << "skipping" << hex << uc;
+ //qDebug() << "skipping" << Qt::hex << uc;
continue;
}
if (uc == 0x1fc1 || uc == 0x1fed)
diff --git a/src/3rdparty/libjpeg/jconfig.h b/src/3rdparty/libjpeg/jconfig.h
index 3f6a7f6b3c..fb1e88ae29 100644
--- a/src/3rdparty/libjpeg/jconfig.h
+++ b/src/3rdparty/libjpeg/jconfig.h
@@ -2,7 +2,7 @@
#define JPEG_LIB_VERSION 80
-#define LIBJPEG_TURBO_VERSION 2.0.2
+#define LIBJPEG_TURBO_VERSION 2.0.3
#define LIBJPEG_TURBO_VERSION_NUMBER 2000002
diff --git a/src/3rdparty/libjpeg/jconfigint.h b/src/3rdparty/libjpeg/jconfigint.h
index c3549bfd25..6616918509 100644
--- a/src/3rdparty/libjpeg/jconfigint.h
+++ b/src/3rdparty/libjpeg/jconfigint.h
@@ -8,7 +8,7 @@
#define PACKAGE_NAME "libjpeg-turbo"
-#define VERSION "2.0.0"
+#define VERSION "2.0.3"
#if SIZE_MAX == 0xffffffff
#define SIZEOF_SIZE_T 4
diff --git a/src/3rdparty/libjpeg/qt_attribution.json b/src/3rdparty/libjpeg/qt_attribution.json
index d1497bc20f..fa81529968 100644
--- a/src/3rdparty/libjpeg/qt_attribution.json
+++ b/src/3rdparty/libjpeg/qt_attribution.json
@@ -6,7 +6,7 @@
"Description": "The Independent JPEG Group's JPEG software",
"Homepage": "http://libjpeg-turbo.virtualgl.org/",
- "Version": "2.0.2",
+ "Version": "2.0.3",
"License": "Independent JPEG Group License",
"LicenseId": "IJG",
"LicenseFile": "LICENSE",
diff --git a/src/3rdparty/libjpeg/src/ChangeLog.md b/src/3rdparty/libjpeg/src/ChangeLog.md
index 7cf92c30fd..3667d120b1 100644
--- a/src/3rdparty/libjpeg/src/ChangeLog.md
+++ b/src/3rdparty/libjpeg/src/ChangeLog.md
@@ -1,3 +1,41 @@
+2.0.3
+=====
+
+### Significant changes relative to 2.0.2:
+
+1. Fixed "using JNI after critical get" errors that occurred on Android
+platforms when passing invalid arguments to certain methods in the TurboJPEG
+Java API.
+
+2. Fixed a regression in the SIMD feature detection code, introduced by
+the AVX2 SIMD extensions (2.0 beta1[1]), that was known to cause an illegal
+instruction exception, in rare cases, on CPUs that lack support for CPUID leaf
+07H (or on which the maximum CPUID leaf has been limited by way of a BIOS
+setting.)
+
+3. The 4:4:0 (h1v2) fancy (smooth) chroma upsampling algorithm in the
+decompressor now uses a similar bias pattern to that of the 4:2:2 (h2v1) fancy
+chroma upsampling algorithm, rounding up or down the upsampled result for
+alternate pixels rather than always rounding down. This ensures that,
+regardless of whether a 4:2:2 JPEG image is rotated or transposed prior to
+decompression (in the frequency domain) or after decompression (in the spatial
+domain), the final image will be similar.
+
+4. Fixed an integer overflow and subsequent segfault that occurred when
+attempting to compress or decompress images with more than 1 billion pixels
+using the TurboJPEG API.
+
+5. Fixed a regression introduced by 2.0 beta1[15] whereby attempting to
+generate a progressive JPEG image on an SSE2-capable CPU using a scan script
+containing one or more scans with lengths divisible by 16 would result in an
+error ("Missing Huffman code table entry") and an invalid JPEG image.
+
+6. Fixed an issue whereby `tjDecodeYUV()` and `tjDecodeYUVPlanes()` would throw
+an error ("Invalid progressive parameters") or a warning ("Inconsistent
+progression sequence") if passed a TurboJPEG instance that was previously used
+to decompress a progressive JPEG image.
+
+
2.0.2
=====
diff --git a/src/3rdparty/libjpeg/src/README.md b/src/3rdparty/libjpeg/src/README.md
index a769259891..c61b855644 100644
--- a/src/3rdparty/libjpeg/src/README.md
+++ b/src/3rdparty/libjpeg/src/README.md
@@ -135,12 +135,11 @@ without recompiling. libjpeg-turbo does not claim to support all of the
libjpeg v7+ features, nor to produce identical output to libjpeg v7+ in all
cases (see below.)
-By passing an argument of `--with-jpeg7` or `--with-jpeg8` to `configure`, or
-an argument of `-DWITH_JPEG7=1` or `-DWITH_JPEG8=1` to `cmake`, you can build a
-version of libjpeg-turbo that emulates the libjpeg v7 or v8 ABI, so that
-programs that are built against libjpeg v7 or v8 can be run with libjpeg-turbo.
-The following section describes which libjpeg v7+ features are supported and
-which aren't.
+By passing an argument of `-DWITH_JPEG7=1` or `-DWITH_JPEG8=1` to `cmake`, you
+can build a version of libjpeg-turbo that emulates the libjpeg v7 or v8 ABI, so
+that programs that are built against libjpeg v7 or v8 can be run with
+libjpeg-turbo. The following section describes which libjpeg v7+ features are
+supported and which aren't.
### Support for libjpeg v7 and v8 Features
@@ -247,9 +246,8 @@ don't, and it allows those functions to be provided in the "official"
libjpeg-turbo binaries.
Those who are concerned about maintaining strict conformance with the libjpeg
-v6b or v7 API can pass an argument of `--without-mem-srcdst` to `configure` or
-an argument of `-DWITH_MEM_SRCDST=0` to `cmake` prior to building
-libjpeg-turbo. This will restore the pre-1.3 behavior, in which
+v6b or v7 API can pass an argument of `-DWITH_MEM_SRCDST=0` to `cmake` prior to
+building libjpeg-turbo. This will restore the pre-1.3 behavior, in which
`jpeg_mem_src()` and `jpeg_mem_dest()` are only included when emulating the
libjpeg v8 API/ABI.
@@ -344,3 +342,15 @@ quality of 98-100. Thus, libjpeg-turbo must use the non-SIMD quantization
function in those cases. This causes performance to drop by as much as 40%.
It is therefore strongly advised that you use the slow integer forward DCT
whenever encoding images with a JPEG quality of 98 or higher.
+
+
+Memory Debugger Pitfalls
+========================
+
+Valgrind and Memory Sanitizer (MSan) can generate false positives
+(specifically, incorrect reports of uninitialized memory accesses) when used
+with libjpeg-turbo's SIMD extensions. It is generally recommended that the
+SIMD extensions be disabled, either by passing an argument of `-DWITH_SIMD=0`
+to `cmake` when configuring the build or by setting the environment variable
+`JSIMD_FORCENONE` to `1` at run time, when testing libjpeg-turbo with Valgrind,
+MSan, or other memory debuggers.
diff --git a/src/3rdparty/libjpeg/src/jchuff.c b/src/3rdparty/libjpeg/src/jchuff.c
index 939b3e76a1..526203e3db 100644
--- a/src/3rdparty/libjpeg/src/jchuff.c
+++ b/src/3rdparty/libjpeg/src/jchuff.c
@@ -4,7 +4,7 @@
* This file was part of the Independent JPEG Group's software:
* Copyright (C) 1991-1997, Thomas G. Lane.
* libjpeg-turbo Modifications:
- * Copyright (C) 2009-2011, 2014-2016, 2018, D. R. Commander.
+ * Copyright (C) 2009-2011, 2014-2016, 2018-2019, D. R. Commander.
* Copyright (C) 2015, Matthieu Darbois.
* For conditions of distribution and use, see the accompanying README.ijg
* file.
@@ -356,6 +356,8 @@ dump_buffer(working_state *state)
put_buffer = (put_buffer << size) | code; \
}
+#if SIZEOF_SIZE_T != 8 && !defined(_WIN64)
+
#define CHECKBUF15() { \
if (put_bits > 15) { \
EMIT_BYTE() \
@@ -363,6 +365,8 @@ dump_buffer(working_state *state)
} \
}
+#endif
+
#define CHECKBUF31() { \
if (put_bits > 31) { \
EMIT_BYTE() \
diff --git a/src/3rdparty/libjpeg/src/jcmaster.c b/src/3rdparty/libjpeg/src/jcmaster.c
index 93b3de6826..998dc40a5c 100644
--- a/src/3rdparty/libjpeg/src/jcmaster.c
+++ b/src/3rdparty/libjpeg/src/jcmaster.c
@@ -492,8 +492,8 @@ prepare_for_pass(j_compress_ptr cinfo)
*/
master->pass_type = output_pass;
master->pass_number++;
- /*FALLTHROUGH*/
#endif
+ /*FALLTHROUGH*/
case output_pass:
/* Do a data-output pass. */
/* We need not repeat per-scan setup if prior optimization pass did it. */
diff --git a/src/3rdparty/libjpeg/src/jdhuff.c b/src/3rdparty/libjpeg/src/jdhuff.c
index 95f38e547e..a1128178b0 100644
--- a/src/3rdparty/libjpeg/src/jdhuff.c
+++ b/src/3rdparty/libjpeg/src/jdhuff.c
@@ -4,7 +4,7 @@
* This file was part of the Independent JPEG Group's software:
* Copyright (C) 1991-1997, Thomas G. Lane.
* libjpeg-turbo Modifications:
- * Copyright (C) 2009-2011, 2016, 2018, D. R. Commander.
+ * Copyright (C) 2009-2011, 2016, 2018-2019, D. R. Commander.
* For conditions of distribution and use, see the accompanying README.ijg
* file.
*
@@ -589,7 +589,11 @@ decode_mcu_slow(j_decompress_ptr cinfo, JBLOCKROW *MCU_data)
if (entropy->dc_needed[blkn]) {
/* Convert DC difference to actual value, update last_dc_val */
int ci = cinfo->MCU_membership[blkn];
- s += state.last_dc_val[ci];
+ /* This is really just
+ * s += state.last_dc_val[ci];
+ * It is written this way in order to shut up UBSan.
+ */
+ s = (int)((unsigned int)s + (unsigned int)state.last_dc_val[ci]);
state.last_dc_val[ci] = s;
if (block) {
/* Output the DC coefficient (assumes jpeg_natural_order[0] = 0) */
@@ -684,7 +688,7 @@ decode_mcu_fast(j_decompress_ptr cinfo, JBLOCKROW *MCU_data)
if (entropy->dc_needed[blkn]) {
int ci = cinfo->MCU_membership[blkn];
- s += state.last_dc_val[ci];
+ s = (int)((unsigned int)s + (unsigned int)state.last_dc_val[ci]);
state.last_dc_val[ci] = s;
if (block)
(*block)[0] = (JCOEF)s;
diff --git a/src/3rdparty/libjpeg/src/jdmerge.c b/src/3rdparty/libjpeg/src/jdmerge.c
index b3fec04f71..dff5a35087 100644
--- a/src/3rdparty/libjpeg/src/jdmerge.c
+++ b/src/3rdparty/libjpeg/src/jdmerge.c
@@ -429,8 +429,6 @@ h2v2_merged_upsample(j_decompress_ptr cinfo, JSAMPIMAGE input_buf,
#define PACK_TWO_PIXELS_LE(l, r) ((r << 16) | l)
#define PACK_TWO_PIXELS_BE(l, r) ((l << 16) | r)
-#define PACK_NEED_ALIGNMENT(ptr) (((size_t)(ptr)) & 3)
-
#define WRITE_TWO_PIXELS_LE(addr, pixels) { \
((INT16 *)(addr))[0] = (INT16)(pixels); \
((INT16 *)(addr))[1] = (INT16)((pixels) >> 16); \
diff --git a/src/3rdparty/libjpeg/src/jdsample.c b/src/3rdparty/libjpeg/src/jdsample.c
index 52ee9af49b..50a68b3013 100644
--- a/src/3rdparty/libjpeg/src/jdsample.c
+++ b/src/3rdparty/libjpeg/src/jdsample.c
@@ -8,6 +8,7 @@
* Copyright (C) 2010, 2015-2016, D. R. Commander.
* Copyright (C) 2014, MIPS Technologies, Inc., California.
* Copyright (C) 2015, Google, Inc.
+ * Copyright (C) 2019, Arm Limited.
* For conditions of distribution and use, see the accompanying README.ijg
* file.
*
@@ -315,9 +316,9 @@ h1v2_fancy_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr,
JSAMPARRAY output_data = *output_data_ptr;
JSAMPROW inptr0, inptr1, outptr;
#if BITS_IN_JSAMPLE == 8
- int thiscolsum;
+ int thiscolsum, bias;
#else
- JLONG thiscolsum;
+ JLONG thiscolsum, bias;
#endif
JDIMENSION colctr;
int inrow, outrow, v;
@@ -327,15 +328,18 @@ h1v2_fancy_upsample(j_decompress_ptr cinfo, jpeg_component_info *compptr,
for (v = 0; v < 2; v++) {
/* inptr0 points to nearest input row, inptr1 points to next nearest */
inptr0 = input_data[inrow];
- if (v == 0) /* next nearest is row above */
+ if (v == 0) { /* next nearest is row above */
inptr1 = input_data[inrow - 1];
- else /* next nearest is row below */
+ bias = 1;
+ } else { /* next nearest is row below */
inptr1 = input_data[inrow + 1];
+ bias = 2;
+ }
outptr = output_data[outrow++];
for (colctr = 0; colctr < compptr->downsampled_width; colctr++) {
thiscolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++);
- *outptr++ = (JSAMPLE)((thiscolsum + 1) >> 2);
+ *outptr++ = (JSAMPLE)((thiscolsum + bias) >> 2);
}
}
inrow++;
diff --git a/src/3rdparty/md4c.pri b/src/3rdparty/md4c.pri
new file mode 100644
index 0000000000..e0150dc6ed
--- /dev/null
+++ b/src/3rdparty/md4c.pri
@@ -0,0 +1,3 @@
+INCLUDEPATH += $$PWD/md4c
+HEADERS += $$PWD/md4c/md4c.h
+SOURCES += $$PWD/md4c/md4c.c
diff --git a/src/3rdparty/md4c/LICENSE.md b/src/3rdparty/md4c/LICENSE.md
new file mode 100644
index 0000000000..d58ef9341d
--- /dev/null
+++ b/src/3rdparty/md4c/LICENSE.md
@@ -0,0 +1,22 @@
+
+# The MIT License (MIT)
+
+Copyright © 2016-2019 Martin Mitáš
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the “Software”),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
diff --git a/src/3rdparty/md4c/md4c.c b/src/3rdparty/md4c/md4c.c
new file mode 100644
index 0000000000..3745cf3e46
--- /dev/null
+++ b/src/3rdparty/md4c/md4c.c
@@ -0,0 +1,6173 @@
+/*
+ * MD4C: Markdown parser for C
+ * (http://github.com/mity/md4c)
+ *
+ * Copyright (c) 2016-2019 Martin Mitas
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "md4c.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+/*****************************
+ *** Miscellaneous Stuff ***
+ *****************************/
+
+#ifdef _MSC_VER
+ /* MSVC does not understand "inline" when building as pure C (not C++).
+ * However it understands "__inline" */
+ #ifndef __cplusplus
+ #define inline __inline
+ #endif
+#endif
+
+#ifdef _T
+ #undef _T
+#endif
+#if defined MD4C_USE_UTF16
+ #define _T(x) L##x
+#else
+ #define _T(x) x
+#endif
+
+/* Misc. macros. */
+#define SIZEOF_ARRAY(a) (sizeof(a) / sizeof(a[0]))
+
+#define STRINGIZE_(x) #x
+#define STRINGIZE(x) STRINGIZE_(x)
+
+#ifndef TRUE
+ #define TRUE 1
+ #define FALSE 0
+#endif
+
+
+/************************
+ *** Internal Types ***
+ ************************/
+
+/* These are omnipresent so lets save some typing. */
+#define CHAR MD_CHAR
+#define SZ MD_SIZE
+#define OFF MD_OFFSET
+
+typedef struct MD_MARK_tag MD_MARK;
+typedef struct MD_BLOCK_tag MD_BLOCK;
+typedef struct MD_CONTAINER_tag MD_CONTAINER;
+typedef struct MD_REF_DEF_tag MD_REF_DEF;
+
+
+/* During analyzes of inline marks, we need to manage some "mark chains",
+ * of (yet unresolved) openers. This structure holds start/end of the chain.
+ * The chain internals are then realized through MD_MARK::prev and ::next.
+ */
+typedef struct MD_MARKCHAIN_tag MD_MARKCHAIN;
+struct MD_MARKCHAIN_tag {
+ int head; /* Index of first mark in the chain, or -1 if empty. */
+ int tail; /* Index of last mark in the chain, or -1 if empty. */
+};
+
+/* Context propagated through all the parsing. */
+typedef struct MD_CTX_tag MD_CTX;
+struct MD_CTX_tag {
+ /* Immutable stuff (parameters of md_parse()). */
+ const CHAR* text;
+ SZ size;
+ MD_PARSER parser;
+ void* userdata;
+
+ /* When this is true, it allows some optimizations. */
+ int doc_ends_with_newline;
+
+ /* Helper temporary growing buffer. */
+ CHAR* buffer;
+ unsigned alloc_buffer;
+
+ /* Reference definitions. */
+ MD_REF_DEF* ref_defs;
+ int n_ref_defs;
+ int alloc_ref_defs;
+ void** ref_def_hashtable;
+ int ref_def_hashtable_size;
+
+ /* Stack of inline/span markers.
+ * This is only used for parsing a single block contents but by storing it
+ * here we may reuse the stack for subsequent blocks; i.e. we have fewer
+ * (re)allocations. */
+ MD_MARK* marks;
+ int n_marks;
+ int alloc_marks;
+
+#if defined MD4C_USE_UTF16
+ char mark_char_map[128];
+#else
+ char mark_char_map[256];
+#endif
+
+ /* For resolving of inline spans. */
+ MD_MARKCHAIN mark_chains[12];
+#define PTR_CHAIN ctx->mark_chains[0]
+#define TABLECELLBOUNDARIES ctx->mark_chains[1]
+#define ASTERISK_OPENERS_extraword_mod3_0 ctx->mark_chains[2]
+#define ASTERISK_OPENERS_extraword_mod3_1 ctx->mark_chains[3]
+#define ASTERISK_OPENERS_extraword_mod3_2 ctx->mark_chains[4]
+#define ASTERISK_OPENERS_intraword_mod3_0 ctx->mark_chains[5]
+#define ASTERISK_OPENERS_intraword_mod3_1 ctx->mark_chains[6]
+#define ASTERISK_OPENERS_intraword_mod3_2 ctx->mark_chains[7]
+#define UNDERSCORE_OPENERS ctx->mark_chains[8]
+#define TILDE_OPENERS ctx->mark_chains[9]
+#define BRACKET_OPENERS ctx->mark_chains[10]
+#define DOLLAR_OPENERS ctx->mark_chains[11]
+#define OPENERS_CHAIN_FIRST 2
+#define OPENERS_CHAIN_LAST 11
+
+ int n_table_cell_boundaries;
+
+ /* For resolving links. */
+ int unresolved_link_head;
+ int unresolved_link_tail;
+
+ /* For resolving raw HTML. */
+ OFF html_comment_horizon;
+ OFF html_proc_instr_horizon;
+ OFF html_decl_horizon;
+ OFF html_cdata_horizon;
+
+ /* For block analysis.
+ * Notes:
+ * -- It holds MD_BLOCK as well as MD_LINE structures. After each
+ * MD_BLOCK, its (multiple) MD_LINE(s) follow.
+ * -- For MD_BLOCK_HTML and MD_BLOCK_CODE, MD_VERBATIMLINE(s) are used
+ * instead of MD_LINE(s).
+ */
+ void* block_bytes;
+ MD_BLOCK* current_block;
+ int n_block_bytes;
+ int alloc_block_bytes;
+
+ /* For container block analysis. */
+ MD_CONTAINER* containers;
+ int n_containers;
+ int alloc_containers;
+
+ /* Minimal indentation to call the block "indented code block". */
+ unsigned code_indent_offset;
+
+ /* Contextual info for line analysis. */
+ SZ code_fence_length; /* For checking closing fence length. */
+ int html_block_type; /* For checking closing raw HTML condition. */
+ int last_line_has_list_loosening_effect;
+ int last_list_item_starts_with_two_blank_lines;
+};
+
+enum MD_LINETYPE_tag {
+ MD_LINE_BLANK,
+ MD_LINE_HR,
+ MD_LINE_ATXHEADER,
+ MD_LINE_SETEXTHEADER,
+ MD_LINE_SETEXTUNDERLINE,
+ MD_LINE_INDENTEDCODE,
+ MD_LINE_FENCEDCODE,
+ MD_LINE_HTML,
+ MD_LINE_TEXT,
+ MD_LINE_TABLE,
+ MD_LINE_TABLEUNDERLINE
+};
+typedef enum MD_LINETYPE_tag MD_LINETYPE;
+
+typedef struct MD_LINE_ANALYSIS_tag MD_LINE_ANALYSIS;
+struct MD_LINE_ANALYSIS_tag {
+ MD_LINETYPE type : 16;
+ unsigned data : 16;
+ OFF beg;
+ OFF end;
+ unsigned indent; /* Indentation level. */
+};
+
+typedef struct MD_LINE_tag MD_LINE;
+struct MD_LINE_tag {
+ OFF beg;
+ OFF end;
+};
+
+typedef struct MD_VERBATIMLINE_tag MD_VERBATIMLINE;
+struct MD_VERBATIMLINE_tag {
+ OFF beg;
+ OFF end;
+ OFF indent;
+};
+
+
+/*******************
+ *** Debugging ***
+ *******************/
+
+#define MD_LOG(msg) \
+ do { \
+ if(ctx->parser.debug_log != NULL) \
+ ctx->parser.debug_log((msg), ctx->userdata); \
+ } while(0)
+
+#ifdef DEBUG
+ #define MD_ASSERT(cond) \
+ do { \
+ if(!(cond)) { \
+ MD_LOG(__FILE__ ":" STRINGIZE(__LINE__) ": " \
+ "Assertion '" STRINGIZE(cond) "' failed."); \
+ exit(1); \
+ } \
+ } while(0)
+
+ #define MD_UNREACHABLE() MD_ASSERT(1 == 0)
+#else
+ #ifdef __GNUC__
+ #define MD_ASSERT(cond) do { if(!(cond)) __builtin_unreachable(); } while(0)
+ #define MD_UNREACHABLE() do { __builtin_unreachable(); } while(0)
+ #elif defined _MSC_VER && _MSC_VER > 120
+ #define MD_ASSERT(cond) do { __assume(cond); } while(0)
+ #define MD_UNREACHABLE() do { __assume(0); } while(0)
+ #else
+ #define MD_ASSERT(cond) do {} while(0)
+ #define MD_UNREACHABLE() do {} while(0)
+ #endif
+#endif
+
+
+/*****************
+ *** Helpers ***
+ *****************/
+
+/* Character accessors. */
+#define CH(off) (ctx->text[(off)])
+#define STR(off) (ctx->text + (off))
+
+/* Character classification.
+ * Note we assume ASCII compatibility of code points < 128 here. */
+#define ISIN_(ch, ch_min, ch_max) ((ch_min) <= (unsigned)(ch) && (unsigned)(ch) <= (ch_max))
+#define ISANYOF_(ch, palette) (md_strchr((palette), (ch)) != NULL)
+#define ISANYOF2_(ch, ch1, ch2) ((ch) == (ch1) || (ch) == (ch2))
+#define ISANYOF3_(ch, ch1, ch2, ch3) ((ch) == (ch1) || (ch) == (ch2) || (ch) == (ch3))
+#define ISASCII_(ch) ((unsigned)(ch) <= 127)
+#define ISBLANK_(ch) (ISANYOF2_((ch), _T(' '), _T('\t')))
+#define ISNEWLINE_(ch) (ISANYOF2_((ch), _T('\r'), _T('\n')))
+#define ISWHITESPACE_(ch) (ISBLANK_(ch) || ISANYOF2_((ch), _T('\v'), _T('\f')))
+#define ISCNTRL_(ch) ((unsigned)(ch) <= 31 || (unsigned)(ch) == 127)
+#define ISPUNCT_(ch) (ISIN_(ch, 33, 47) || ISIN_(ch, 58, 64) || ISIN_(ch, 91, 96) || ISIN_(ch, 123, 126))
+#define ISUPPER_(ch) (ISIN_(ch, _T('A'), _T('Z')))
+#define ISLOWER_(ch) (ISIN_(ch, _T('a'), _T('z')))
+#define ISALPHA_(ch) (ISUPPER_(ch) || ISLOWER_(ch))
+#define ISDIGIT_(ch) (ISIN_(ch, _T('0'), _T('9')))
+#define ISXDIGIT_(ch) (ISDIGIT_(ch) || ISIN_(ch, _T('A'), _T('F')) || ISIN_(ch, _T('a'), _T('f')))
+#define ISALNUM_(ch) (ISALPHA_(ch) || ISDIGIT_(ch))
+
+#define ISANYOF(off, palette) ISANYOF_(CH(off), (palette))
+#define ISANYOF2(off, ch1, ch2) ISANYOF2_(CH(off), (ch1), (ch2))
+#define ISANYOF3(off, ch1, ch2, ch3) ISANYOF3_(CH(off), (ch1), (ch2), (ch3))
+#define ISASCII(off) ISASCII_(CH(off))
+#define ISBLANK(off) ISBLANK_(CH(off))
+#define ISNEWLINE(off) ISNEWLINE_(CH(off))
+#define ISWHITESPACE(off) ISWHITESPACE_(CH(off))
+#define ISCNTRL(off) ISCNTRL_(CH(off))
+#define ISPUNCT(off) ISPUNCT_(CH(off))
+#define ISUPPER(off) ISUPPER_(CH(off))
+#define ISLOWER(off) ISLOWER_(CH(off))
+#define ISALPHA(off) ISALPHA_(CH(off))
+#define ISDIGIT(off) ISDIGIT_(CH(off))
+#define ISXDIGIT(off) ISXDIGIT_(CH(off))
+#define ISALNUM(off) ISALNUM_(CH(off))
+static inline const CHAR*
+md_strchr(const CHAR* str, CHAR ch)
+{
+ OFF i;
+ for(i = 0; str[i] != _T('\0'); i++) {
+ if(ch == str[i])
+ return (str + i);
+ }
+ return NULL;
+}
+
+/* Case insensitive check of string equality. */
+static inline int
+md_ascii_case_eq(const CHAR* s1, const CHAR* s2, SZ n)
+{
+ OFF i;
+ for(i = 0; i < n; i++) {
+ CHAR ch1 = s1[i];
+ CHAR ch2 = s2[i];
+
+ if(ISLOWER_(ch1))
+ ch1 += ('A'-'a');
+ if(ISLOWER_(ch2))
+ ch2 += ('A'-'a');
+ if(ch1 != ch2)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static inline int
+md_ascii_eq(const CHAR* s1, const CHAR* s2, SZ n)
+{
+ return memcmp(s1, s2, n * sizeof(CHAR)) == 0;
+}
+
+static int
+md_text_with_null_replacement(MD_CTX* ctx, MD_TEXTTYPE type, const CHAR* str, SZ size)
+{
+ OFF off = 0;
+ int ret = 0;
+
+ while(1) {
+ while(off < size && str[off] != _T('\0'))
+ off++;
+
+ if(off > 0) {
+ ret = ctx->parser.text(type, str, off, ctx->userdata);
+ if(ret != 0)
+ return ret;
+
+ str += off;
+ size -= off;
+ off = 0;
+ }
+
+ if(off >= size)
+ return 0;
+
+ ret = ctx->parser.text(MD_TEXT_NULLCHAR, _T(""), 1, ctx->userdata);
+ if(ret != 0)
+ return ret;
+ off++;
+ }
+}
+
+
+#define MD_CHECK(func) \
+ do { \
+ ret = (func); \
+ if(ret < 0) \
+ goto abort; \
+ } while(0)
+
+
+#define MD_TEMP_BUFFER(sz) \
+ do { \
+ if(sz > ctx->alloc_buffer) { \
+ CHAR* new_buffer; \
+ SZ new_size = ((sz) + (sz) / 2 + 128) & ~127; \
+ \
+ new_buffer = realloc(ctx->buffer, new_size); \
+ if(new_buffer == NULL) { \
+ MD_LOG("realloc() failed."); \
+ ret = -1; \
+ goto abort; \
+ } \
+ \
+ ctx->buffer = new_buffer; \
+ ctx->alloc_buffer = new_size; \
+ } \
+ } while(0)
+
+
+#define MD_ENTER_BLOCK(type, arg) \
+ do { \
+ ret = ctx->parser.enter_block((type), (arg), ctx->userdata); \
+ if(ret != 0) { \
+ MD_LOG("Aborted from enter_block() callback."); \
+ goto abort; \
+ } \
+ } while(0)
+
+#define MD_LEAVE_BLOCK(type, arg) \
+ do { \
+ ret = ctx->parser.leave_block((type), (arg), ctx->userdata); \
+ if(ret != 0) { \
+ MD_LOG("Aborted from leave_block() callback."); \
+ goto abort; \
+ } \
+ } while(0)
+
+#define MD_ENTER_SPAN(type, arg) \
+ do { \
+ ret = ctx->parser.enter_span((type), (arg), ctx->userdata); \
+ if(ret != 0) { \
+ MD_LOG("Aborted from enter_span() callback."); \
+ goto abort; \
+ } \
+ } while(0)
+
+#define MD_LEAVE_SPAN(type, arg) \
+ do { \
+ ret = ctx->parser.leave_span((type), (arg), ctx->userdata); \
+ if(ret != 0) { \
+ MD_LOG("Aborted from leave_span() callback."); \
+ goto abort; \
+ } \
+ } while(0)
+
+#define MD_TEXT(type, str, size) \
+ do { \
+ if(size > 0) { \
+ ret = ctx->parser.text((type), (str), (size), ctx->userdata); \
+ if(ret != 0) { \
+ MD_LOG("Aborted from text() callback."); \
+ goto abort; \
+ } \
+ } \
+ } while(0)
+
+#define MD_TEXT_INSECURE(type, str, size) \
+ do { \
+ if(size > 0) { \
+ ret = md_text_with_null_replacement(ctx, type, str, size); \
+ if(ret != 0) { \
+ MD_LOG("Aborted from text() callback."); \
+ goto abort; \
+ } \
+ } \
+ } while(0)
+
+
+
+/*************************
+ *** Unicode Support ***
+ *************************/
+
+typedef struct MD_UNICODE_FOLD_INFO_tag MD_UNICODE_FOLD_INFO;
+struct MD_UNICODE_FOLD_INFO_tag {
+ unsigned codepoints[3];
+ int n_codepoints;
+};
+
+
+#if defined MD4C_USE_UTF16 || defined MD4C_USE_UTF8
+ /* Binary search over sorted "map" of codepoints. Consecutive sequences
+ * of codepoints may be encoded in the map by just using the
+ * (MIN_CODEPOINT | 0x40000000) and (MAX_CODEPOINT | 0x80000000).
+ *
+ * Returns index of the found record in the map (in the case of ranges,
+ * the minimal value is used); or -1 on failure. */
+ static int
+ md_unicode_bsearch__(unsigned codepoint, const unsigned* map, size_t map_size)
+ {
+ int beg, end;
+ int pivot_beg, pivot_end;
+
+ beg = 0;
+ end = (int) map_size-1;
+ while(beg <= end) {
+ /* Pivot may be a range, not just a single value. */
+ pivot_beg = pivot_end = (beg + end) / 2;
+ if(map[pivot_end] & 0x40000000)
+ pivot_end++;
+ if(map[pivot_beg] & 0x80000000)
+ pivot_beg--;
+
+ if(codepoint < (map[pivot_beg] & 0x00ffffff))
+ end = pivot_beg - 1;
+ else if(codepoint > (map[pivot_end] & 0x00ffffff))
+ beg = pivot_end + 1;
+ else
+ return pivot_beg;
+ }
+
+ return -1;
+ }
+
+ static int
+ md_is_unicode_whitespace__(unsigned codepoint)
+ {
+#define R(cp_min, cp_max) ((cp_min) | 0x40000000), ((cp_max) | 0x80000000)
+#define S(cp) (cp)
+ /* Unicode "Zs" category.
+ * (generated by scripts/build_whitespace_map.py) */
+ static const unsigned WHITESPACE_MAP[] = {
+ S(0x0020), S(0x00a0), S(0x1680), R(0x2000,0x200a), S(0x202f), S(0x205f), S(0x3000)
+ };
+#undef R
+#undef S
+
+ /* The ASCII ones are the most frequently used ones, also CommonMark
+ * specification requests few more in this range. */
+ if(codepoint <= 0x7f)
+ return ISWHITESPACE_(codepoint);
+
+ return (md_unicode_bsearch__(codepoint, WHITESPACE_MAP, SIZEOF_ARRAY(WHITESPACE_MAP)) >= 0);
+ }
+
+ static int
+ md_is_unicode_punct__(unsigned codepoint)
+ {
+#define R(cp_min, cp_max) ((cp_min) | 0x40000000), ((cp_max) | 0x80000000)
+#define S(cp) (cp)
+ /* Unicode "Pc", "Pd", "Pe", "Pf", "Pi", "Po", "Ps" categories.
+ * (generated by scripts/build_punct_map.py) */
+ static const unsigned PUNCT_MAP[] = {
+ R(0x0021,0x0023), R(0x0025,0x002a), R(0x002c,0x002f), R(0x003a,0x003b), R(0x003f,0x0040),
+ R(0x005b,0x005d), S(0x005f), S(0x007b), S(0x007d), S(0x00a1), S(0x00a7), S(0x00ab), R(0x00b6,0x00b7),
+ S(0x00bb), S(0x00bf), S(0x037e), S(0x0387), R(0x055a,0x055f), R(0x0589,0x058a), S(0x05be), S(0x05c0),
+ S(0x05c3), S(0x05c6), R(0x05f3,0x05f4), R(0x0609,0x060a), R(0x060c,0x060d), S(0x061b), R(0x061e,0x061f),
+ R(0x066a,0x066d), S(0x06d4), R(0x0700,0x070d), R(0x07f7,0x07f9), R(0x0830,0x083e), S(0x085e),
+ R(0x0964,0x0965), S(0x0970), S(0x09fd), S(0x0a76), S(0x0af0), S(0x0c77), S(0x0c84), S(0x0df4), S(0x0e4f),
+ R(0x0e5a,0x0e5b), R(0x0f04,0x0f12), S(0x0f14), R(0x0f3a,0x0f3d), S(0x0f85), R(0x0fd0,0x0fd4),
+ R(0x0fd9,0x0fda), R(0x104a,0x104f), S(0x10fb), R(0x1360,0x1368), S(0x1400), S(0x166e), R(0x169b,0x169c),
+ R(0x16eb,0x16ed), R(0x1735,0x1736), R(0x17d4,0x17d6), R(0x17d8,0x17da), R(0x1800,0x180a),
+ R(0x1944,0x1945), R(0x1a1e,0x1a1f), R(0x1aa0,0x1aa6), R(0x1aa8,0x1aad), R(0x1b5a,0x1b60),
+ R(0x1bfc,0x1bff), R(0x1c3b,0x1c3f), R(0x1c7e,0x1c7f), R(0x1cc0,0x1cc7), S(0x1cd3), R(0x2010,0x2027),
+ R(0x2030,0x2043), R(0x2045,0x2051), R(0x2053,0x205e), R(0x207d,0x207e), R(0x208d,0x208e),
+ R(0x2308,0x230b), R(0x2329,0x232a), R(0x2768,0x2775), R(0x27c5,0x27c6), R(0x27e6,0x27ef),
+ R(0x2983,0x2998), R(0x29d8,0x29db), R(0x29fc,0x29fd), R(0x2cf9,0x2cfc), R(0x2cfe,0x2cff), S(0x2d70),
+ R(0x2e00,0x2e2e), R(0x2e30,0x2e4f), R(0x3001,0x3003), R(0x3008,0x3011), R(0x3014,0x301f), S(0x3030),
+ S(0x303d), S(0x30a0), S(0x30fb), R(0xa4fe,0xa4ff), R(0xa60d,0xa60f), S(0xa673), S(0xa67e),
+ R(0xa6f2,0xa6f7), R(0xa874,0xa877), R(0xa8ce,0xa8cf), R(0xa8f8,0xa8fa), S(0xa8fc), R(0xa92e,0xa92f),
+ S(0xa95f), R(0xa9c1,0xa9cd), R(0xa9de,0xa9df), R(0xaa5c,0xaa5f), R(0xaade,0xaadf), R(0xaaf0,0xaaf1),
+ S(0xabeb), R(0xfd3e,0xfd3f), R(0xfe10,0xfe19), R(0xfe30,0xfe52), R(0xfe54,0xfe61), S(0xfe63), S(0xfe68),
+ R(0xfe6a,0xfe6b), R(0xff01,0xff03), R(0xff05,0xff0a), R(0xff0c,0xff0f), R(0xff1a,0xff1b),
+ R(0xff1f,0xff20), R(0xff3b,0xff3d), S(0xff3f), S(0xff5b), S(0xff5d), R(0xff5f,0xff65), R(0x10100,0x10102),
+ S(0x1039f), S(0x103d0), S(0x1056f), S(0x10857), S(0x1091f), S(0x1093f), R(0x10a50,0x10a58), S(0x10a7f),
+ R(0x10af0,0x10af6), R(0x10b39,0x10b3f), R(0x10b99,0x10b9c), R(0x10f55,0x10f59), R(0x11047,0x1104d),
+ R(0x110bb,0x110bc), R(0x110be,0x110c1), R(0x11140,0x11143), R(0x11174,0x11175), R(0x111c5,0x111c8),
+ S(0x111cd), S(0x111db), R(0x111dd,0x111df), R(0x11238,0x1123d), S(0x112a9), R(0x1144b,0x1144f),
+ S(0x1145b), S(0x1145d), S(0x114c6), R(0x115c1,0x115d7), R(0x11641,0x11643), R(0x11660,0x1166c),
+ R(0x1173c,0x1173e), S(0x1183b), S(0x119e2), R(0x11a3f,0x11a46), R(0x11a9a,0x11a9c), R(0x11a9e,0x11aa2),
+ R(0x11c41,0x11c45), R(0x11c70,0x11c71), R(0x11ef7,0x11ef8), S(0x11fff), R(0x12470,0x12474),
+ R(0x16a6e,0x16a6f), S(0x16af5), R(0x16b37,0x16b3b), S(0x16b44), R(0x16e97,0x16e9a), S(0x16fe2),
+ S(0x1bc9f), R(0x1da87,0x1da8b), R(0x1e95e,0x1e95f)
+ };
+#undef R
+#undef S
+
+ /* The ASCII ones are the most frequently used ones, also CommonMark
+ * specification requests few more in this range. */
+ if(codepoint <= 0x7f)
+ return ISPUNCT_(codepoint);
+
+ return (md_unicode_bsearch__(codepoint, PUNCT_MAP, SIZEOF_ARRAY(PUNCT_MAP)) >= 0);
+ }
+
+ static void
+ md_get_unicode_fold_info(unsigned codepoint, MD_UNICODE_FOLD_INFO* info)
+ {
+#define R(cp_min, cp_max) ((cp_min) | 0x40000000), ((cp_max) | 0x80000000)
+#define S(cp) (cp)
+ /* Unicode "Pc", "Pd", "Pe", "Pf", "Pi", "Po", "Ps" categories.
+ * (generated by scripts/build_punct_map.py) */
+ static const unsigned FOLD_MAP_1[] = {
+ R(0x0041,0x005a), S(0x00b5), R(0x00c0,0x00d6), R(0x00d8,0x00de), R(0x0100,0x012e), R(0x0132,0x0136),
+ R(0x0139,0x0147), R(0x014a,0x0176), S(0x0178), R(0x0179,0x017d), S(0x017f), S(0x0181), S(0x0182),
+ S(0x0186), S(0x0187), S(0x0189), S(0x018b), S(0x018e), S(0x018f), S(0x0190), S(0x0191), S(0x0193),
+ S(0x0194), S(0x0196), S(0x0197), S(0x0198), S(0x019c), S(0x019d), S(0x019f), R(0x01a0,0x01a4), S(0x01a6),
+ S(0x01a7), S(0x01a9), S(0x01ac), S(0x01ae), S(0x01af), S(0x01b1), S(0x01b3), S(0x01b7), S(0x01b8),
+ S(0x01bc), S(0x01c4), S(0x01c5), S(0x01c7), S(0x01c8), S(0x01ca), R(0x01cb,0x01db), R(0x01de,0x01ee),
+ S(0x01f1), S(0x01f2), S(0x01f6), S(0x01f7), R(0x01f8,0x021e), S(0x0220), R(0x0222,0x0232), S(0x023a),
+ S(0x023b), S(0x023d), S(0x023e), S(0x0241), S(0x0243), S(0x0244), S(0x0245), R(0x0246,0x024e), S(0x0345),
+ S(0x0370), S(0x0376), S(0x037f), S(0x0386), R(0x0388,0x038a), S(0x038c), S(0x038e), R(0x0391,0x03a1),
+ R(0x03a3,0x03ab), S(0x03c2), S(0x03cf), S(0x03d0), S(0x03d1), S(0x03d5), S(0x03d6), R(0x03d8,0x03ee),
+ S(0x03f0), S(0x03f1), S(0x03f4), S(0x03f5), S(0x03f7), S(0x03f9), S(0x03fa), R(0x03fd,0x03ff),
+ R(0x0400,0x040f), R(0x0410,0x042f), R(0x0460,0x0480), R(0x048a,0x04be), S(0x04c0), R(0x04c1,0x04cd),
+ R(0x04d0,0x052e), R(0x0531,0x0556), R(0x10a0,0x10c5), S(0x10c7), S(0x10cd), R(0x13f8,0x13fd), S(0x1c80),
+ S(0x1c81), S(0x1c82), S(0x1c83), S(0x1c85), S(0x1c86), S(0x1c87), S(0x1c88), R(0x1c90,0x1cba),
+ R(0x1cbd,0x1cbf), R(0x1e00,0x1e94), S(0x1e9b), R(0x1ea0,0x1efe), R(0x1f08,0x1f0f), R(0x1f18,0x1f1d),
+ R(0x1f28,0x1f2f), R(0x1f38,0x1f3f), R(0x1f48,0x1f4d), S(0x1f59), S(0x1f5b), S(0x1f5d), S(0x1f5f),
+ R(0x1f68,0x1f6f), S(0x1fb8), S(0x1fba), S(0x1fbe), R(0x1fc8,0x1fcb), S(0x1fd8), S(0x1fda), S(0x1fe8),
+ S(0x1fea), S(0x1fec), S(0x1ff8), S(0x1ffa), S(0x2126), S(0x212a), S(0x212b), S(0x2132), R(0x2160,0x216f),
+ S(0x2183), R(0x24b6,0x24cf), R(0x2c00,0x2c2e), S(0x2c60), S(0x2c62), S(0x2c63), S(0x2c64),
+ R(0x2c67,0x2c6b), S(0x2c6d), S(0x2c6e), S(0x2c6f), S(0x2c70), S(0x2c72), S(0x2c75), S(0x2c7e),
+ R(0x2c80,0x2ce2), S(0x2ceb), S(0x2cf2), R(0xa640,0xa66c), R(0xa680,0xa69a), R(0xa722,0xa72e),
+ R(0xa732,0xa76e), S(0xa779), S(0xa77d), R(0xa77e,0xa786), S(0xa78b), S(0xa78d), S(0xa790),
+ R(0xa796,0xa7a8), S(0xa7aa), S(0xa7ab), S(0xa7ac), S(0xa7ad), S(0xa7ae), S(0xa7b0), S(0xa7b1), S(0xa7b2),
+ S(0xa7b3), R(0xa7b4,0xa7be), S(0xa7c2), S(0xa7c4), S(0xa7c5), S(0xa7c6), R(0xab70,0xabbf),
+ R(0xff21,0xff3a), R(0x10400,0x10427), R(0x104b0,0x104d3), R(0x10c80,0x10cb2), R(0x118a0,0x118bf),
+ R(0x16e40,0x16e5f), R(0x1e900,0x1e921)
+ };
+ static const unsigned FOLD_MAP_1_DATA[] = {
+ 0x0061, 0x007a, 0x03bc, 0x00e0, 0x00f6, 0x00f8, 0x00fe, 0x0101, 0x012f, 0x0133, 0x0137, 0x013a, 0x0148,
+ 0x014b, 0x0177, 0x00ff, 0x017a, 0x017e, 0x0073, 0x0253, 0x0183, 0x0254, 0x0188, 0x0256, 0x018c, 0x01dd,
+ 0x0259, 0x025b, 0x0192, 0x0260, 0x0263, 0x0269, 0x0268, 0x0199, 0x026f, 0x0272, 0x0275, 0x01a1, 0x01a5,
+ 0x0280, 0x01a8, 0x0283, 0x01ad, 0x0288, 0x01b0, 0x028a, 0x01b4, 0x0292, 0x01b9, 0x01bd, 0x01c6, 0x01c6,
+ 0x01c9, 0x01c9, 0x01cc, 0x01cc, 0x01dc, 0x01df, 0x01ef, 0x01f3, 0x01f3, 0x0195, 0x01bf, 0x01f9, 0x021f,
+ 0x019e, 0x0223, 0x0233, 0x2c65, 0x023c, 0x019a, 0x2c66, 0x0242, 0x0180, 0x0289, 0x028c, 0x0247, 0x024f,
+ 0x03b9, 0x0371, 0x0377, 0x03f3, 0x03ac, 0x03ad, 0x03af, 0x03cc, 0x03cd, 0x03b1, 0x03c1, 0x03c3, 0x03cb,
+ 0x03c3, 0x03d7, 0x03b2, 0x03b8, 0x03c6, 0x03c0, 0x03d9, 0x03ef, 0x03ba, 0x03c1, 0x03b8, 0x03b5, 0x03f8,
+ 0x03f2, 0x03fb, 0x037b, 0x037d, 0x0450, 0x045f, 0x0430, 0x044f, 0x0461, 0x0481, 0x048b, 0x04bf, 0x04cf,
+ 0x04c2, 0x04ce, 0x04d1, 0x052f, 0x0561, 0x0586, 0x2d00, 0x2d25, 0x2d27, 0x2d2d, 0x13f0, 0x13f5, 0x0432,
+ 0x0434, 0x043e, 0x0441, 0x0442, 0x044a, 0x0463, 0xa64b, 0x10d0, 0x10fa, 0x10fd, 0x10ff, 0x1e01, 0x1e95,
+ 0x1e61, 0x1ea1, 0x1eff, 0x1f00, 0x1f07, 0x1f10, 0x1f15, 0x1f20, 0x1f27, 0x1f30, 0x1f37, 0x1f40, 0x1f45,
+ 0x1f51, 0x1f53, 0x1f55, 0x1f57, 0x1f60, 0x1f67, 0x1fb0, 0x1f70, 0x03b9, 0x1f72, 0x1f75, 0x1fd0, 0x1f76,
+ 0x1fe0, 0x1f7a, 0x1fe5, 0x1f78, 0x1f7c, 0x03c9, 0x006b, 0x00e5, 0x214e, 0x2170, 0x217f, 0x2184, 0x24d0,
+ 0x24e9, 0x2c30, 0x2c5e, 0x2c61, 0x026b, 0x1d7d, 0x027d, 0x2c68, 0x2c6c, 0x0251, 0x0271, 0x0250, 0x0252,
+ 0x2c73, 0x2c76, 0x023f, 0x2c81, 0x2ce3, 0x2cec, 0x2cf3, 0xa641, 0xa66d, 0xa681, 0xa69b, 0xa723, 0xa72f,
+ 0xa733, 0xa76f, 0xa77a, 0x1d79, 0xa77f, 0xa787, 0xa78c, 0x0265, 0xa791, 0xa797, 0xa7a9, 0x0266, 0x025c,
+ 0x0261, 0x026c, 0x026a, 0x029e, 0x0287, 0x029d, 0xab53, 0xa7b5, 0xa7bf, 0xa7c3, 0xa794, 0x0282, 0x1d8e,
+ 0x13a0, 0x13ef, 0xff41, 0xff5a, 0x10428, 0x1044f, 0x104d8, 0x104fb, 0x10cc0, 0x10cf2, 0x118c0, 0x118df,
+ 0x16e60, 0x16e7f, 0x1e922, 0x1e943
+ };
+ static const unsigned FOLD_MAP_2[] = {
+ S(0x00df), S(0x0130), S(0x0149), S(0x01f0), S(0x0587), S(0x1e96), S(0x1e97), S(0x1e98), S(0x1e99),
+ S(0x1e9a), S(0x1e9e), S(0x1f50), R(0x1f80,0x1f87), R(0x1f88,0x1f8f), R(0x1f90,0x1f97), R(0x1f98,0x1f9f),
+ R(0x1fa0,0x1fa7), R(0x1fa8,0x1faf), S(0x1fb2), S(0x1fb3), S(0x1fb4), S(0x1fb6), S(0x1fbc), S(0x1fc2),
+ S(0x1fc3), S(0x1fc4), S(0x1fc6), S(0x1fcc), S(0x1fd6), S(0x1fe4), S(0x1fe6), S(0x1ff2), S(0x1ff3),
+ S(0x1ff4), S(0x1ff6), S(0x1ffc), S(0xfb00), S(0xfb01), S(0xfb02), S(0xfb05), S(0xfb06), S(0xfb13),
+ S(0xfb14), S(0xfb15), S(0xfb16), S(0xfb17)
+ };
+ static const unsigned FOLD_MAP_2_DATA[] = {
+ 0x0073,0x0073, 0x0069,0x0307, 0x02bc,0x006e, 0x006a,0x030c, 0x0565,0x0582, 0x0068,0x0331, 0x0074,0x0308,
+ 0x0077,0x030a, 0x0079,0x030a, 0x0061,0x02be, 0x0073,0x0073, 0x03c5,0x0313, 0x1f00,0x03b9, 0x1f07,0x03b9,
+ 0x1f00,0x03b9, 0x1f07,0x03b9, 0x1f20,0x03b9, 0x1f27,0x03b9, 0x1f20,0x03b9, 0x1f27,0x03b9, 0x1f60,0x03b9,
+ 0x1f67,0x03b9, 0x1f60,0x03b9, 0x1f67,0x03b9, 0x1f70,0x03b9, 0x03b1,0x03b9, 0x03ac,0x03b9, 0x03b1,0x0342,
+ 0x03b1,0x03b9, 0x1f74,0x03b9, 0x03b7,0x03b9, 0x03ae,0x03b9, 0x03b7,0x0342, 0x03b7,0x03b9, 0x03b9,0x0342,
+ 0x03c1,0x0313, 0x03c5,0x0342, 0x1f7c,0x03b9, 0x03c9,0x03b9, 0x03ce,0x03b9, 0x03c9,0x0342, 0x03c9,0x03b9,
+ 0x0066,0x0066, 0x0066,0x0069, 0x0066,0x006c, 0x0073,0x0074, 0x0073,0x0074, 0x0574,0x0576, 0x0574,0x0565,
+ 0x0574,0x056b, 0x057e,0x0576, 0x0574,0x056d
+ };
+ static const unsigned FOLD_MAP_3[] = {
+ S(0x0390), S(0x03b0), S(0x1f52), S(0x1f54), S(0x1f56), S(0x1fb7), S(0x1fc7), S(0x1fd2), S(0x1fd3),
+ S(0x1fd7), S(0x1fe2), S(0x1fe3), S(0x1fe7), S(0x1ff7), S(0xfb03), S(0xfb04)
+ };
+ static const unsigned FOLD_MAP_3_DATA[] = {
+ 0x03b9,0x0308,0x0301, 0x03c5,0x0308,0x0301, 0x03c5,0x0313,0x0300, 0x03c5,0x0313,0x0301,
+ 0x03c5,0x0313,0x0342, 0x03b1,0x0342,0x03b9, 0x03b7,0x0342,0x03b9, 0x03b9,0x0308,0x0300,
+ 0x03b9,0x0308,0x0301, 0x03b9,0x0308,0x0342, 0x03c5,0x0308,0x0300, 0x03c5,0x0308,0x0301,
+ 0x03c5,0x0308,0x0342, 0x03c9,0x0342,0x03b9, 0x0066,0x0066,0x0069, 0x0066,0x0066,0x006c
+ };
+#undef R
+#undef S
+ static const struct {
+ const unsigned* map;
+ const unsigned* data;
+ size_t map_size;
+ int n_codepoints;
+ } FOLD_MAP_LIST[] = {
+ { FOLD_MAP_1, FOLD_MAP_1_DATA, SIZEOF_ARRAY(FOLD_MAP_1), 1 },
+ { FOLD_MAP_2, FOLD_MAP_2_DATA, SIZEOF_ARRAY(FOLD_MAP_2), 2 },
+ { FOLD_MAP_3, FOLD_MAP_3_DATA, SIZEOF_ARRAY(FOLD_MAP_3), 3 }
+ };
+
+ int i;
+
+ /* Fast path for ASCII characters. */
+ if(codepoint <= 0x7f) {
+ info->codepoints[0] = codepoint;
+ if(ISUPPER_(codepoint))
+ info->codepoints[0] += 'a' - 'A';
+ info->n_codepoints = 1;
+ return;
+ }
+
+ /* Try to locate the codepoint in any of the maps. */
+ for(i = 0; i < (int) SIZEOF_ARRAY(FOLD_MAP_LIST); i++) {
+ int index;
+
+ index = md_unicode_bsearch__(codepoint, FOLD_MAP_LIST[i].map, FOLD_MAP_LIST[i].map_size);
+ if(index >= 0) {
+ /* Found the mapping. */
+ int n_codepoints = FOLD_MAP_LIST[i].n_codepoints;
+ const unsigned* map = FOLD_MAP_LIST[i].map;
+ const unsigned* codepoints = FOLD_MAP_LIST[i].data + (index * n_codepoints);
+
+ memcpy(info->codepoints, codepoints, sizeof(unsigned) * n_codepoints);
+ info->n_codepoints = n_codepoints;
+
+ if(FOLD_MAP_LIST[i].map[index] != codepoint) {
+ /* The found mapping maps whole range of codepoints,
+ * i.e. we have to offset info->codepoints[0] accordingly. */
+ if((map[index] & 0x00ffffff)+1 == codepoints[0]) {
+ /* Alternating type of the range. */
+ info->codepoints[0] = codepoint + ((codepoint & 0x1) == (map[index] & 0x1) ? 1 : 0);
+ } else {
+ /* Range to range kind of mapping. */
+ info->codepoints[0] += (codepoint - (map[index] & 0x00ffffff));
+ }
+ }
+
+ return;
+ }
+ }
+
+ /* No mapping found. Map the codepoint to itself. */
+ info->codepoints[0] = codepoint;
+ info->n_codepoints = 1;
+ }
+#endif
+
+
+#if defined MD4C_USE_UTF16
+ #define IS_UTF16_SURROGATE_HI(word) (((WORD)(word) & 0xfc00) == 0xd800)
+ #define IS_UTF16_SURROGATE_LO(word) (((WORD)(word) & 0xfc00) == 0xdc00)
+ #define UTF16_DECODE_SURROGATE(hi, lo) (0x10000 + ((((unsigned)(hi) & 0x3ff) << 10) | (((unsigned)(lo) & 0x3ff) << 0)))
+
+ static unsigned
+ md_decode_utf16le__(const CHAR* str, SZ str_size, SZ* p_size)
+ {
+ if(IS_UTF16_SURROGATE_HI(str[0])) {
+ if(1 < str_size && IS_UTF16_SURROGATE_LO(str[1])) {
+ if(p_size != NULL)
+ *p_size = 2;
+ return UTF16_DECODE_SURROGATE(str[0], str[1]);
+ }
+ }
+
+ if(p_size != NULL)
+ *p_size = 1;
+ return str[0];
+ }
+
+ static unsigned
+ md_decode_utf16le_before__(MD_CTX* ctx, OFF off)
+ {
+ if(off > 2 && IS_UTF16_SURROGATE_HI(CH(off-2)) && IS_UTF16_SURROGATE_LO(CH(off-1)))
+ return UTF16_DECODE_SURROGATE(CH(off-2), CH(off-1));
+
+ return CH(off);
+ }
+
+ /* No whitespace uses surrogates, so no decoding needed here. */
+ #define ISUNICODEWHITESPACE_(codepoint) md_is_unicode_whitespace__(codepoint)
+ #define ISUNICODEWHITESPACE(off) md_is_unicode_whitespace__(CH(off))
+ #define ISUNICODEWHITESPACEBEFORE(off) md_is_unicode_whitespace__(CH((off)-1))
+
+ #define ISUNICODEPUNCT(off) md_is_unicode_punct__(md_decode_utf16le__(STR(off), ctx->size - (off), NULL))
+ #define ISUNICODEPUNCTBEFORE(off) md_is_unicode_punct__(md_decode_utf16le_before__(ctx, off))
+
+ static inline int
+ md_decode_unicode(const CHAR* str, OFF off, SZ str_size, SZ* p_char_size)
+ {
+ return md_decode_utf16le__(str+off, str_size-off, p_char_size);
+ }
+#elif defined MD4C_USE_UTF8
+ #define IS_UTF8_LEAD1(byte) ((unsigned char)(byte) <= 0x7f)
+ #define IS_UTF8_LEAD2(byte) (((unsigned char)(byte) & 0xe0) == 0xc0)
+ #define IS_UTF8_LEAD3(byte) (((unsigned char)(byte) & 0xf0) == 0xe0)
+ #define IS_UTF8_LEAD4(byte) (((unsigned char)(byte) & 0xf8) == 0xf0)
+ #define IS_UTF8_TAIL(byte) (((unsigned char)(byte) & 0xc0) == 0x80)
+
+ static unsigned
+ md_decode_utf8__(const CHAR* str, SZ str_size, SZ* p_size)
+ {
+ if(!IS_UTF8_LEAD1(str[0])) {
+ if(IS_UTF8_LEAD2(str[0])) {
+ if(1 < str_size && IS_UTF8_TAIL(str[1])) {
+ if(p_size != NULL)
+ *p_size = 2;
+
+ return (((unsigned int)str[0] & 0x1f) << 6) |
+ (((unsigned int)str[1] & 0x3f) << 0);
+ }
+ } else if(IS_UTF8_LEAD3(str[0])) {
+ if(2 < str_size && IS_UTF8_TAIL(str[1]) && IS_UTF8_TAIL(str[2])) {
+ if(p_size != NULL)
+ *p_size = 3;
+
+ return (((unsigned int)str[0] & 0x0f) << 12) |
+ (((unsigned int)str[1] & 0x3f) << 6) |
+ (((unsigned int)str[2] & 0x3f) << 0);
+ }
+ } else if(IS_UTF8_LEAD4(str[0])) {
+ if(3 < str_size && IS_UTF8_TAIL(str[1]) && IS_UTF8_TAIL(str[2]) && IS_UTF8_TAIL(str[3])) {
+ if(p_size != NULL)
+ *p_size = 4;
+
+ return (((unsigned int)str[0] & 0x07) << 18) |
+ (((unsigned int)str[1] & 0x3f) << 12) |
+ (((unsigned int)str[2] & 0x3f) << 6) |
+ (((unsigned int)str[3] & 0x3f) << 0);
+ }
+ }
+ }
+
+ if(p_size != NULL)
+ *p_size = 1;
+ return (unsigned) str[0];
+ }
+
+ static unsigned
+ md_decode_utf8_before__(MD_CTX* ctx, OFF off)
+ {
+ if(!IS_UTF8_LEAD1(CH(off-1))) {
+ if(off > 1 && IS_UTF8_LEAD2(CH(off-2)) && IS_UTF8_TAIL(CH(off-1)))
+ return (((unsigned int)CH(off-2) & 0x1f) << 6) |
+ (((unsigned int)CH(off-1) & 0x3f) << 0);
+
+ if(off > 2 && IS_UTF8_LEAD3(CH(off-3)) && IS_UTF8_TAIL(CH(off-2)) && IS_UTF8_TAIL(CH(off-1)))
+ return (((unsigned int)CH(off-3) & 0x0f) << 12) |
+ (((unsigned int)CH(off-2) & 0x3f) << 6) |
+ (((unsigned int)CH(off-1) & 0x3f) << 0);
+
+ if(off > 3 && IS_UTF8_LEAD4(CH(off-4)) && IS_UTF8_TAIL(CH(off-3)) && IS_UTF8_TAIL(CH(off-2)) && IS_UTF8_TAIL(CH(off-1)))
+ return (((unsigned int)CH(off-4) & 0x07) << 18) |
+ (((unsigned int)CH(off-3) & 0x3f) << 12) |
+ (((unsigned int)CH(off-2) & 0x3f) << 6) |
+ (((unsigned int)CH(off-1) & 0x3f) << 0);
+ }
+
+ return (unsigned) CH(off-1);
+ }
+
+ #define ISUNICODEWHITESPACE_(codepoint) md_is_unicode_whitespace__(codepoint)
+ #define ISUNICODEWHITESPACE(off) md_is_unicode_whitespace__(md_decode_utf8__(STR(off), ctx->size - (off), NULL))
+ #define ISUNICODEWHITESPACEBEFORE(off) md_is_unicode_whitespace__(md_decode_utf8_before__(ctx, off))
+
+ #define ISUNICODEPUNCT(off) md_is_unicode_punct__(md_decode_utf8__(STR(off), ctx->size - (off), NULL))
+ #define ISUNICODEPUNCTBEFORE(off) md_is_unicode_punct__(md_decode_utf8_before__(ctx, off))
+
+ static inline unsigned
+ md_decode_unicode(const CHAR* str, OFF off, SZ str_size, SZ* p_char_size)
+ {
+ return md_decode_utf8__(str+off, str_size-off, p_char_size);
+ }
+#else
+ #define ISUNICODEWHITESPACE_(codepoint) ISWHITESPACE_(codepoint)
+ #define ISUNICODEWHITESPACE(off) ISWHITESPACE(off)
+ #define ISUNICODEWHITESPACEBEFORE(off) ISWHITESPACE((off)-1)
+
+ #define ISUNICODEPUNCT(off) ISPUNCT(off)
+ #define ISUNICODEPUNCTBEFORE(off) ISPUNCT((off)-1)
+
+ static inline void
+ md_get_unicode_fold_info(unsigned codepoint, MD_UNICODE_FOLD_INFO* info)
+ {
+ info->codepoints[0] = codepoint;
+ if(ISUPPER_(codepoint))
+ info->codepoints[0] += 'a' - 'A';
+ info->n_codepoints = 1;
+ }
+
+ static inline unsigned
+ md_decode_unicode(const CHAR* str, OFF off, SZ str_size, SZ* p_size)
+ {
+ *p_size = 1;
+ return (unsigned) str[off];
+ }
+#endif
+
+
+/*************************************
+ *** Helper string manipulations ***
+ *************************************/
+
+/* Fill buffer with copy of the string between 'beg' and 'end' but replace any
+ * line breaks with given replacement character.
+ *
+ * NOTE: Caller is responsible to make sure the buffer is large enough.
+ * (Given the output is always shorter then input, (end - beg) is good idea
+ * what the caller should allocate.)
+ */
+static void
+md_merge_lines(MD_CTX* ctx, OFF beg, OFF end, const MD_LINE* lines, int n_lines,
+ CHAR line_break_replacement_char, CHAR* buffer, SZ* p_size)
+{
+ CHAR* ptr = buffer;
+ int line_index = 0;
+ OFF off = beg;
+
+ while(1) {
+ const MD_LINE* line = &lines[line_index];
+ OFF line_end = line->end;
+ if(end < line_end)
+ line_end = end;
+
+ while(off < line_end) {
+ *ptr = CH(off);
+ ptr++;
+ off++;
+ }
+
+ if(off >= end) {
+ *p_size = ptr - buffer;
+ return;
+ }
+
+ *ptr = line_break_replacement_char;
+ ptr++;
+
+ line_index++;
+ off = lines[line_index].beg;
+ }
+}
+
+/* Wrapper of md_merge_lines() which allocates new buffer for the output string.
+ */
+static int
+md_merge_lines_alloc(MD_CTX* ctx, OFF beg, OFF end, const MD_LINE* lines, int n_lines,
+ CHAR line_break_replacement_char, CHAR** p_str, SZ* p_size)
+{
+ CHAR* buffer;
+
+ buffer = (CHAR*) malloc(sizeof(CHAR) * (end - beg));
+ if(buffer == NULL) {
+ MD_LOG("malloc() failed.");
+ return -1;
+ }
+
+ md_merge_lines(ctx, beg, end, lines, n_lines,
+ line_break_replacement_char, buffer, p_size);
+
+ *p_str = buffer;
+ return 0;
+}
+
+static OFF
+md_skip_unicode_whitespace(const CHAR* label, OFF off, SZ size)
+{
+ SZ char_size;
+ unsigned codepoint;
+
+ while(off < size) {
+ codepoint = md_decode_unicode(label, off, size, &char_size);
+ if(!ISUNICODEWHITESPACE_(codepoint) && !ISNEWLINE_(label[off]))
+ break;
+ off += char_size;
+ }
+
+ return off;
+}
+
+
+/******************************
+ *** Recognizing raw HTML ***
+ ******************************/
+
+/* md_is_html_tag() may be called when processing inlines (inline raw HTML)
+ * or when breaking document to blocks (checking for start of HTML block type 7).
+ *
+ * When breaking document to blocks, we do not yet know line boundaries, but
+ * in that case the whole tag has to live on a single line. We distinguish this
+ * by n_lines == 0.
+ */
+static int
+md_is_html_tag(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, OFF max_end, OFF* p_end)
+{
+ int attr_state;
+ OFF off = beg;
+ OFF line_end = (n_lines > 0) ? lines[0].end : ctx->size;
+ int i = 0;
+
+ MD_ASSERT(CH(beg) == _T('<'));
+
+ if(off + 1 >= line_end)
+ return FALSE;
+ off++;
+
+ /* For parsing attributes, we need a little state automaton below.
+ * State -1: no attributes are allowed.
+ * State 0: attribute could follow after some whitespace.
+ * State 1: after a whitespace (attribute name may follow).
+ * State 2: after attribute name ('=' MAY follow).
+ * State 3: after '=' (value specification MUST follow).
+ * State 41: in middle of unquoted attribute value.
+ * State 42: in middle of single-quoted attribute value.
+ * State 43: in middle of double-quoted attribute value.
+ */
+ attr_state = 0;
+
+ if(CH(off) == _T('/')) {
+ /* Closer tag "</ ... >". No attributes may be present. */
+ attr_state = -1;
+ off++;
+ }
+
+ /* Tag name */
+ if(off >= line_end || !ISALPHA(off))
+ return FALSE;
+ off++;
+ while(off < line_end && (ISALNUM(off) || CH(off) == _T('-')))
+ off++;
+
+ /* (Optional) attributes (if not closer), (optional) '/' (if not closer)
+ * and final '>'. */
+ while(1) {
+ while(off < line_end && !ISNEWLINE(off)) {
+ if(attr_state > 40) {
+ if(attr_state == 41 && (ISBLANK(off) || ISANYOF(off, _T("\"'=<>`")))) {
+ attr_state = 0;
+ off--; /* Put the char back for re-inspection in the new state. */
+ } else if(attr_state == 42 && CH(off) == _T('\'')) {
+ attr_state = 0;
+ } else if(attr_state == 43 && CH(off) == _T('"')) {
+ attr_state = 0;
+ }
+ off++;
+ } else if(ISWHITESPACE(off)) {
+ if(attr_state == 0)
+ attr_state = 1;
+ off++;
+ } else if(attr_state <= 2 && CH(off) == _T('>')) {
+ /* End. */
+ goto done;
+ } else if(attr_state <= 2 && CH(off) == _T('/') && off+1 < line_end && CH(off+1) == _T('>')) {
+ /* End with digraph '/>' */
+ off++;
+ goto done;
+ } else if((attr_state == 1 || attr_state == 2) && (ISALPHA(off) || CH(off) == _T('_') || CH(off) == _T(':'))) {
+ off++;
+ /* Attribute name */
+ while(off < line_end && (ISALNUM(off) || ISANYOF(off, _T("_.:-"))))
+ off++;
+ attr_state = 2;
+ } else if(attr_state == 2 && CH(off) == _T('=')) {
+ /* Attribute assignment sign */
+ off++;
+ attr_state = 3;
+ } else if(attr_state == 3) {
+ /* Expecting start of attribute value. */
+ if(CH(off) == _T('"'))
+ attr_state = 43;
+ else if(CH(off) == _T('\''))
+ attr_state = 42;
+ else if(!ISANYOF(off, _T("\"'=<>`")) && !ISNEWLINE(off))
+ attr_state = 41;
+ else
+ return FALSE;
+ off++;
+ } else {
+ /* Anything unexpected. */
+ return FALSE;
+ }
+ }
+
+ /* We have to be on a single line. See definition of start condition
+ * of HTML block, type 7. */
+ if(n_lines == 0)
+ return FALSE;
+
+ i++;
+ if(i >= n_lines)
+ return FALSE;
+
+ off = lines[i].beg;
+ line_end = lines[i].end;
+
+ if(attr_state == 0 || attr_state == 41)
+ attr_state = 1;
+
+ if(off >= max_end)
+ return FALSE;
+ }
+
+done:
+ if(off >= max_end)
+ return FALSE;
+
+ *p_end = off+1;
+ return TRUE;
+}
+
+static int
+md_scan_for_html_closer(MD_CTX* ctx, const MD_CHAR* str, MD_SIZE len,
+ const MD_LINE* lines, int n_lines,
+ OFF beg, OFF max_end, OFF* p_end,
+ OFF* p_scan_horizon)
+{
+ OFF off = beg;
+ int i = 0;
+
+ if(off < *p_scan_horizon && *p_scan_horizon >= max_end - len) {
+ /* We have already scanned the range up to the max_end so we know
+ * there is nothing to see. */
+ return FALSE;
+ }
+
+ while(TRUE) {
+ while(off + len <= lines[i].end && off + len <= max_end) {
+ if(md_ascii_eq(STR(off), str, len)) {
+ /* Success. */
+ *p_end = off + len;
+ return TRUE;
+ }
+ off++;
+ }
+
+ i++;
+ if(off >= max_end || i >= n_lines) {
+ /* Failure. */
+ *p_scan_horizon = off;
+ return FALSE;
+ }
+
+ off = lines[i].beg;
+ }
+}
+
+static int
+md_is_html_comment(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, OFF max_end, OFF* p_end)
+{
+ OFF off = beg;
+
+ MD_ASSERT(CH(beg) == _T('<'));
+
+ if(off + 4 >= lines[0].end)
+ return FALSE;
+ if(CH(off+1) != _T('!') || CH(off+2) != _T('-') || CH(off+3) != _T('-'))
+ return FALSE;
+ off += 4;
+
+ /* ">" and "->" must not follow the opening. */
+ if(off < lines[0].end && CH(off) == _T('>'))
+ return FALSE;
+ if(off+1 < lines[0].end && CH(off) == _T('-') && CH(off+1) == _T('>'))
+ return FALSE;
+
+ /* HTML comment must not contain "--", so we scan just for "--" instead
+ * of "-->" and verify manually that '>' follows. */
+ if(md_scan_for_html_closer(ctx, _T("--"), 2,
+ lines, n_lines, off, max_end, p_end, &ctx->html_comment_horizon))
+ {
+ if(*p_end < max_end && CH(*p_end) == _T('>')) {
+ *p_end = *p_end + 1;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static int
+md_is_html_processing_instruction(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, OFF max_end, OFF* p_end)
+{
+ OFF off = beg;
+
+ if(off + 2 >= lines[0].end)
+ return FALSE;
+ if(CH(off+1) != _T('?'))
+ return FALSE;
+ off += 2;
+
+ return md_scan_for_html_closer(ctx, _T("?>"), 2,
+ lines, n_lines, off, max_end, p_end, &ctx->html_proc_instr_horizon);
+}
+
+static int
+md_is_html_declaration(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, OFF max_end, OFF* p_end)
+{
+ OFF off = beg;
+
+ if(off + 2 >= lines[0].end)
+ return FALSE;
+ if(CH(off+1) != _T('!'))
+ return FALSE;
+ off += 2;
+
+ /* Declaration name. */
+ if(off >= lines[0].end || !ISALPHA(off))
+ return FALSE;
+ off++;
+ while(off < lines[0].end && ISALPHA(off))
+ off++;
+ if(off < lines[0].end && !ISWHITESPACE(off))
+ return FALSE;
+
+ return md_scan_for_html_closer(ctx, _T(">"), 1,
+ lines, n_lines, off, max_end, p_end, &ctx->html_decl_horizon);
+}
+
+static int
+md_is_html_cdata(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, OFF max_end, OFF* p_end)
+{
+ static const CHAR open_str[] = _T("<![CDATA[");
+ static const SZ open_size = SIZEOF_ARRAY(open_str) - 1;
+
+ OFF off = beg;
+
+ if(off + open_size >= lines[0].end)
+ return FALSE;
+ if(memcmp(STR(off), open_str, open_size) != 0)
+ return FALSE;
+ off += open_size;
+
+ if(lines[n_lines-1].end < max_end)
+ max_end = lines[n_lines-1].end - 2;
+
+ return md_scan_for_html_closer(ctx, _T("]]>"), 3,
+ lines, n_lines, off, max_end, p_end, &ctx->html_cdata_horizon);
+}
+
+static int
+md_is_html_any(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg, OFF max_end, OFF* p_end)
+{
+ MD_ASSERT(CH(beg) == _T('<'));
+ return (md_is_html_tag(ctx, lines, n_lines, beg, max_end, p_end) ||
+ md_is_html_comment(ctx, lines, n_lines, beg, max_end, p_end) ||
+ md_is_html_processing_instruction(ctx, lines, n_lines, beg, max_end, p_end) ||
+ md_is_html_declaration(ctx, lines, n_lines, beg, max_end, p_end) ||
+ md_is_html_cdata(ctx, lines, n_lines, beg, max_end, p_end));
+}
+
+
+/****************************
+ *** Recognizing Entity ***
+ ****************************/
+
+static int
+md_is_hex_entity_contents(MD_CTX* ctx, const CHAR* text, OFF beg, OFF max_end, OFF* p_end)
+{
+ OFF off = beg;
+
+ while(off < max_end && ISXDIGIT_(text[off]) && off - beg <= 8)
+ off++;
+
+ if(1 <= off - beg && off - beg <= 6) {
+ *p_end = off;
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+static int
+md_is_dec_entity_contents(MD_CTX* ctx, const CHAR* text, OFF beg, OFF max_end, OFF* p_end)
+{
+ OFF off = beg;
+
+ while(off < max_end && ISDIGIT_(text[off]) && off - beg <= 8)
+ off++;
+
+ if(1 <= off - beg && off - beg <= 7) {
+ *p_end = off;
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+static int
+md_is_named_entity_contents(MD_CTX* ctx, const CHAR* text, OFF beg, OFF max_end, OFF* p_end)
+{
+ OFF off = beg;
+
+ if(off < max_end && ISALPHA_(text[off]))
+ off++;
+ else
+ return FALSE;
+
+ while(off < max_end && ISALNUM_(text[off]) && off - beg <= 48)
+ off++;
+
+ if(2 <= off - beg && off - beg <= 48) {
+ *p_end = off;
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+static int
+md_is_entity_str(MD_CTX* ctx, const CHAR* text, OFF beg, OFF max_end, OFF* p_end)
+{
+ int is_contents;
+ OFF off = beg;
+
+ MD_ASSERT(text[off] == _T('&'));
+ off++;
+
+ if(off+2 < max_end && text[off] == _T('#') && (text[off+1] == _T('x') || text[off+1] == _T('X')))
+ is_contents = md_is_hex_entity_contents(ctx, text, off+2, max_end, &off);
+ else if(off+1 < max_end && text[off] == _T('#'))
+ is_contents = md_is_dec_entity_contents(ctx, text, off+1, max_end, &off);
+ else
+ is_contents = md_is_named_entity_contents(ctx, text, off, max_end, &off);
+
+ if(is_contents && off < max_end && text[off] == _T(';')) {
+ *p_end = off+1;
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+static inline int
+md_is_entity(MD_CTX* ctx, OFF beg, OFF max_end, OFF* p_end)
+{
+ return md_is_entity_str(ctx, ctx->text, beg, max_end, p_end);
+}
+
+
+/******************************
+ *** Attribute Management ***
+ ******************************/
+
+typedef struct MD_ATTRIBUTE_BUILD_tag MD_ATTRIBUTE_BUILD;
+struct MD_ATTRIBUTE_BUILD_tag {
+ CHAR* text;
+ MD_TEXTTYPE* substr_types;
+ OFF* substr_offsets;
+ int substr_count;
+ int substr_alloc;
+ MD_TEXTTYPE trivial_types[1];
+ OFF trivial_offsets[2];
+};
+
+
+#define MD_BUILD_ATTR_NO_ESCAPES 0x0001
+
+static int
+md_build_attr_append_substr(MD_CTX* ctx, MD_ATTRIBUTE_BUILD* build,
+ MD_TEXTTYPE type, OFF off)
+{
+ if(build->substr_count >= build->substr_alloc) {
+ MD_TEXTTYPE* new_substr_types;
+ OFF* new_substr_offsets;
+
+ build->substr_alloc = (build->substr_alloc == 0 ? 8 : build->substr_alloc * 2);
+
+ new_substr_types = (MD_TEXTTYPE*) realloc(build->substr_types,
+ build->substr_alloc * sizeof(MD_TEXTTYPE));
+ if(new_substr_types == NULL) {
+ MD_LOG("realloc() failed.");
+ return -1;
+ }
+ /* Note +1 to reserve space for final offset (== raw_size). */
+ new_substr_offsets = (OFF*) realloc(build->substr_offsets,
+ (build->substr_alloc+1) * sizeof(OFF));
+ if(new_substr_offsets == NULL) {
+ MD_LOG("realloc() failed.");
+ free(new_substr_types);
+ return -1;
+ }
+
+ build->substr_types = new_substr_types;
+ build->substr_offsets = new_substr_offsets;
+ }
+
+ build->substr_types[build->substr_count] = type;
+ build->substr_offsets[build->substr_count] = off;
+ build->substr_count++;
+ return 0;
+}
+
+static void
+md_free_attribute(MD_CTX* ctx, MD_ATTRIBUTE_BUILD* build)
+{
+ if(build->substr_alloc > 0) {
+ free(build->text);
+ free(build->substr_types);
+ free(build->substr_offsets);
+ }
+}
+
+static int
+md_build_attribute(MD_CTX* ctx, const CHAR* raw_text, SZ raw_size,
+ unsigned flags, MD_ATTRIBUTE* attr, MD_ATTRIBUTE_BUILD* build)
+{
+ OFF raw_off, off;
+ int is_trivial;
+ int ret = 0;
+
+ memset(build, 0, sizeof(MD_ATTRIBUTE_BUILD));
+
+ /* If there is no backslash and no ampersand, build trivial attribute
+ * without any malloc(). */
+ is_trivial = TRUE;
+ for(raw_off = 0; raw_off < raw_size; raw_off++) {
+ if(ISANYOF3_(raw_text[raw_off], _T('\\'), _T('&'), _T('\0'))) {
+ is_trivial = FALSE;
+ break;
+ }
+ }
+
+ if(is_trivial) {
+ build->text = (CHAR*) (raw_size ? raw_text : NULL);
+ build->substr_types = build->trivial_types;
+ build->substr_offsets = build->trivial_offsets;
+ build->substr_count = 1;
+ build->substr_alloc = 0;
+ build->trivial_types[0] = MD_TEXT_NORMAL;
+ build->trivial_offsets[0] = 0;
+ build->trivial_offsets[1] = raw_size;
+ off = raw_size;
+ } else {
+ build->text = (CHAR*) malloc(raw_size * sizeof(CHAR));
+ if(build->text == NULL) {
+ MD_LOG("malloc() failed.");
+ goto abort;
+ }
+
+ raw_off = 0;
+ off = 0;
+
+ while(raw_off < raw_size) {
+ if(raw_text[raw_off] == _T('\0')) {
+ MD_CHECK(md_build_attr_append_substr(ctx, build, MD_TEXT_NULLCHAR, off));
+ memcpy(build->text + off, raw_text + raw_off, 1);
+ off++;
+ raw_off++;
+ continue;
+ }
+
+ if(raw_text[raw_off] == _T('&')) {
+ OFF ent_end;
+
+ if(md_is_entity_str(ctx, raw_text, raw_off, raw_size, &ent_end)) {
+ MD_CHECK(md_build_attr_append_substr(ctx, build, MD_TEXT_ENTITY, off));
+ memcpy(build->text + off, raw_text + raw_off, ent_end - raw_off);
+ off += ent_end - raw_off;
+ raw_off = ent_end;
+ continue;
+ }
+ }
+
+ if(build->substr_count == 0 || build->substr_types[build->substr_count-1] != MD_TEXT_NORMAL)
+ MD_CHECK(md_build_attr_append_substr(ctx, build, MD_TEXT_NORMAL, off));
+
+ if(!(flags & MD_BUILD_ATTR_NO_ESCAPES) &&
+ raw_text[raw_off] == _T('\\') && raw_off+1 < raw_size &&
+ (ISPUNCT_(raw_text[raw_off+1]) || ISNEWLINE_(raw_text[raw_off+1])))
+ raw_off++;
+
+ build->text[off++] = raw_text[raw_off++];
+ }
+ build->substr_offsets[build->substr_count] = off;
+ }
+
+ attr->text = build->text;
+ attr->size = off;
+ attr->substr_offsets = build->substr_offsets;
+ attr->substr_types = build->substr_types;
+ return 0;
+
+abort:
+ md_free_attribute(ctx, build);
+ return -1;
+}
+
+
+/*********************************************
+ *** Dictionary of Reference Definitions ***
+ *********************************************/
+
+#define MD_FNV1A_BASE 2166136261
+#define MD_FNV1A_PRIME 16777619
+
+static inline unsigned
+md_fnv1a(unsigned base, const void* data, size_t n)
+{
+ const unsigned char* buf = (const unsigned char*) data;
+ unsigned hash = base;
+ size_t i;
+
+ for(i = 0; i < n; i++) {
+ hash ^= buf[i];
+ hash *= MD_FNV1A_PRIME;
+ }
+
+ return hash;
+}
+
+
+struct MD_REF_DEF_tag {
+ CHAR* label;
+ CHAR* title;
+ unsigned hash;
+ SZ label_size : 24;
+ unsigned label_needs_free : 1;
+ unsigned title_needs_free : 1;
+ SZ title_size;
+ OFF dest_beg;
+ OFF dest_end;
+};
+
+/* Label equivalence is quite complicated with regards to whitespace and case
+ * folding. This complicates computing a hash of it as well as direct comparison
+ * of two labels. */
+
+static unsigned
+md_link_label_hash(const CHAR* label, SZ size)
+{
+ unsigned hash = MD_FNV1A_BASE;
+ OFF off;
+ unsigned codepoint;
+ int is_whitespace = FALSE;
+
+ off = md_skip_unicode_whitespace(label, 0, size);
+ while(off < size) {
+ SZ char_size;
+
+ codepoint = md_decode_unicode(label, off, size, &char_size);
+ is_whitespace = ISUNICODEWHITESPACE_(codepoint) || ISNEWLINE_(label[off]);
+
+ if(is_whitespace) {
+ codepoint = ' ';
+ hash = md_fnv1a(hash, &codepoint, sizeof(unsigned));
+ off = md_skip_unicode_whitespace(label, off, size);
+ } else {
+ MD_UNICODE_FOLD_INFO fold_info;
+
+ md_get_unicode_fold_info(codepoint, &fold_info);
+ hash = md_fnv1a(hash, fold_info.codepoints, fold_info.n_codepoints * sizeof(unsigned));
+ off += char_size;
+ }
+ }
+
+ return hash;
+}
+
+static OFF
+md_link_label_cmp_load_fold_info(const CHAR* label, OFF off, SZ size,
+ MD_UNICODE_FOLD_INFO* fold_info)
+{
+ unsigned codepoint;
+ SZ char_size;
+
+ if(off >= size) {
+ /* Treat end of link label as a whitespace. */
+ goto whitespace;
+ }
+
+ if(ISNEWLINE_(label[off])) {
+ /* Treat new lines as a whitespace. */
+ off++;
+ goto whitespace;
+ }
+
+ codepoint = md_decode_unicode(label, off, size, &char_size);
+ off += char_size;
+ if(ISUNICODEWHITESPACE_(codepoint)) {
+ /* Treat all whitespace as equivalent */
+ goto whitespace;
+ }
+
+ /* Get real folding info. */
+ md_get_unicode_fold_info(codepoint, fold_info);
+ return off;
+
+whitespace:
+ fold_info->codepoints[0] = _T(' ');
+ fold_info->n_codepoints = 1;
+ return off;
+}
+
+static int
+md_link_label_cmp(const CHAR* a_label, SZ a_size, const CHAR* b_label, SZ b_size)
+{
+ OFF a_off;
+ OFF b_off;
+ int a_reached_end = FALSE;
+ int b_reached_end = FALSE;
+ MD_UNICODE_FOLD_INFO a_fi = { 0 };
+ MD_UNICODE_FOLD_INFO b_fi = { 0 };
+ OFF a_fi_off = 0;
+ OFF b_fi_off = 0;
+ int cmp;
+
+ a_off = md_skip_unicode_whitespace(a_label, 0, a_size);
+ b_off = md_skip_unicode_whitespace(b_label, 0, b_size);
+ while(!a_reached_end && !b_reached_end) {
+ /* If needed, load fold info for next char. */
+ if(a_fi_off >= a_fi.n_codepoints) {
+ a_fi_off = 0;
+ a_off = md_link_label_cmp_load_fold_info(a_label, a_off, a_size, &a_fi);
+ a_reached_end = (a_off >= a_size);
+ }
+ if(b_fi_off >= b_fi.n_codepoints) {
+ b_fi_off = 0;
+ b_off = md_link_label_cmp_load_fold_info(b_label, b_off, b_size, &b_fi);
+ b_reached_end = (b_off >= b_size);
+ }
+
+ cmp = b_fi.codepoints[b_fi_off] - a_fi.codepoints[a_fi_off];
+ if(cmp != 0)
+ return cmp;
+
+ a_fi_off++;
+ b_fi_off++;
+ }
+
+ return 0;
+}
+
+typedef struct MD_REF_DEF_LIST_tag MD_REF_DEF_LIST;
+struct MD_REF_DEF_LIST_tag {
+ int n_ref_defs;
+ int alloc_ref_defs;
+ MD_REF_DEF* ref_defs[]; /* Valid items always point into ctx->ref_defs[] */
+};
+
+static int
+md_ref_def_cmp(const void* a, const void* b)
+{
+ const MD_REF_DEF* a_ref = *(const MD_REF_DEF**)a;
+ const MD_REF_DEF* b_ref = *(const MD_REF_DEF**)b;
+
+ if(a_ref->hash < b_ref->hash)
+ return -1;
+ else if(a_ref->hash > b_ref->hash)
+ return +1;
+ else
+ return md_link_label_cmp(a_ref->label, a_ref->label_size, b_ref->label, b_ref->label_size);
+}
+
+static int
+md_ref_def_cmp_stable(const void* a, const void* b)
+{
+ int cmp;
+
+ cmp = md_ref_def_cmp(a, b);
+
+ /* Ensure stability of the sorting. */
+ if(cmp == 0) {
+ const MD_REF_DEF* a_ref = *(const MD_REF_DEF**)a;
+ const MD_REF_DEF* b_ref = *(const MD_REF_DEF**)b;
+
+ if(a_ref < b_ref)
+ cmp = -1;
+ else if(a_ref > b_ref)
+ cmp = +1;
+ else
+ cmp = 0;
+ }
+
+ return cmp;
+}
+
+static int
+md_build_ref_def_hashtable(MD_CTX* ctx)
+{
+ int i, j;
+
+ if(ctx->n_ref_defs == 0)
+ return 0;
+
+ ctx->ref_def_hashtable_size = (ctx->n_ref_defs * 5) / 4;
+ ctx->ref_def_hashtable = malloc(ctx->ref_def_hashtable_size * sizeof(void*));
+ if(ctx->ref_def_hashtable == NULL) {
+ MD_LOG("malloc() failed.");
+ goto abort;
+ }
+ memset(ctx->ref_def_hashtable, 0, ctx->ref_def_hashtable_size * sizeof(void*));
+
+ /* Each member of ctx->ref_def_hashtable[] can be:
+ * -- NULL,
+ * -- pointer to the MD_REF_DEF in ctx->ref_defs[], or
+ * -- pointer to a MD_REF_DEF_LIST, which holds multiple pointers to
+ * such MD_REF_DEFs.
+ */
+ for(i = 0; i < ctx->n_ref_defs; i++) {
+ MD_REF_DEF* def = &ctx->ref_defs[i];
+ void* bucket;
+ MD_REF_DEF_LIST* list;
+
+ def->hash = md_link_label_hash(def->label, def->label_size);
+ bucket = ctx->ref_def_hashtable[def->hash % ctx->ref_def_hashtable_size];
+
+ if(bucket == NULL) {
+ ctx->ref_def_hashtable[def->hash % ctx->ref_def_hashtable_size] = def;
+ continue;
+ }
+
+ if(ctx->ref_defs <= (MD_REF_DEF*) bucket && (MD_REF_DEF*) bucket < ctx->ref_defs + ctx->n_ref_defs) {
+ /* The bucket already contains one ref. def. Lets see whether it
+ * is the same label (ref. def. duplicate) or different one
+ * (hash conflict). */
+ MD_REF_DEF* old_def = (MD_REF_DEF*) bucket;
+
+ if(md_link_label_cmp(def->label, def->label_size, old_def->label, old_def->label_size) == 0) {
+ /* Ignore this ref. def. */
+ continue;
+ }
+
+ /* Make the bucket capable of holding more ref. defs. */
+ list = (MD_REF_DEF_LIST*) malloc(sizeof(MD_REF_DEF_LIST) + 4 * sizeof(MD_REF_DEF*));
+ if(list == NULL) {
+ MD_LOG("malloc() failed.");
+ goto abort;
+ }
+ list->ref_defs[0] = old_def;
+ list->ref_defs[1] = def;
+ list->n_ref_defs = 2;
+ list->alloc_ref_defs = 4;
+ ctx->ref_def_hashtable[def->hash % ctx->ref_def_hashtable_size] = list;
+ continue;
+ }
+
+ /* Append the def to the bucket list. */
+ list = (MD_REF_DEF_LIST*) bucket;
+ if(list->n_ref_defs >= list->alloc_ref_defs) {
+ MD_REF_DEF_LIST* list_tmp = (MD_REF_DEF_LIST*) realloc(list,
+ sizeof(MD_REF_DEF_LIST) + 2 * list->alloc_ref_defs * sizeof(MD_REF_DEF*));
+ if(list_tmp == NULL) {
+ MD_LOG("realloc() failed.");
+ goto abort;
+ }
+ list = list_tmp;
+ list->alloc_ref_defs *= 2;
+ ctx->ref_def_hashtable[def->hash % ctx->ref_def_hashtable_size] = list;
+ }
+
+ list->ref_defs[list->n_ref_defs] = def;
+ list->n_ref_defs++;
+ }
+
+ /* Sort the complex buckets so we can use bsearch() with them. */
+ for(i = 0; i < ctx->ref_def_hashtable_size; i++) {
+ void* bucket = ctx->ref_def_hashtable[i];
+ MD_REF_DEF_LIST* list;
+
+ if(bucket == NULL)
+ continue;
+ if(ctx->ref_defs <= (MD_REF_DEF*) bucket && (MD_REF_DEF*) bucket < ctx->ref_defs + ctx->n_ref_defs)
+ continue;
+
+ list = (MD_REF_DEF_LIST*) bucket;
+ qsort(list->ref_defs, list->n_ref_defs, sizeof(MD_REF_DEF*), md_ref_def_cmp_stable);
+
+ /* Disable duplicates. */
+ for(j = 1; j < list->n_ref_defs; j++) {
+ if(md_ref_def_cmp(&list->ref_defs[j-1], &list->ref_defs[j]) == 0)
+ list->ref_defs[j] = list->ref_defs[j-1];
+ }
+ }
+
+ return 0;
+
+abort:
+ return -1;
+}
+
+static void
+md_free_ref_def_hashtable(MD_CTX* ctx)
+{
+ if(ctx->ref_def_hashtable != NULL) {
+ int i;
+
+ for(i = 0; i < ctx->ref_def_hashtable_size; i++) {
+ void* bucket = ctx->ref_def_hashtable[i];
+ if(bucket == NULL)
+ continue;
+ if(ctx->ref_defs <= (MD_REF_DEF*) bucket && (MD_REF_DEF*) bucket < ctx->ref_defs + ctx->n_ref_defs)
+ continue;
+ free(bucket);
+ }
+
+ free(ctx->ref_def_hashtable);
+ }
+}
+
+static const MD_REF_DEF*
+md_lookup_ref_def(MD_CTX* ctx, const CHAR* label, SZ label_size)
+{
+ unsigned hash;
+ void* bucket;
+
+ if(ctx->ref_def_hashtable_size == 0)
+ return NULL;
+
+ hash = md_link_label_hash(label, label_size);
+ bucket = ctx->ref_def_hashtable[hash % ctx->ref_def_hashtable_size];
+
+ if(bucket == NULL) {
+ return NULL;
+ } else if(ctx->ref_defs <= (MD_REF_DEF*) bucket && (MD_REF_DEF*) bucket < ctx->ref_defs + ctx->n_ref_defs) {
+ const MD_REF_DEF* def = (MD_REF_DEF*) bucket;
+
+ if(md_link_label_cmp(def->label, def->label_size, label, label_size) == 0)
+ return def;
+ else
+ return NULL;
+ } else {
+ MD_REF_DEF_LIST* list = (MD_REF_DEF_LIST*) bucket;
+ MD_REF_DEF key_buf;
+ const MD_REF_DEF* key = &key_buf;
+ const MD_REF_DEF** ret;
+
+ key_buf.label = (CHAR*) label;
+ key_buf.label_size = label_size;
+ key_buf.hash = md_link_label_hash(key_buf.label, key_buf.label_size);
+
+ ret = (const MD_REF_DEF**) bsearch(&key, list->ref_defs,
+ list->n_ref_defs, sizeof(MD_REF_DEF*), md_ref_def_cmp);
+ if(ret != NULL)
+ return *ret;
+ else
+ return NULL;
+ }
+}
+
+
+/***************************
+ *** Recognizing Links ***
+ ***************************/
+
+/* Note this code is partially shared between processing inlines and blocks
+ * as reference definitions and links share some helper parser functions.
+ */
+
+typedef struct MD_LINK_ATTR_tag MD_LINK_ATTR;
+struct MD_LINK_ATTR_tag {
+ OFF dest_beg;
+ OFF dest_end;
+
+ CHAR* title;
+ SZ title_size;
+ int title_needs_free;
+};
+
+
+static int
+md_is_link_label(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg,
+ OFF* p_end, int* p_beg_line_index, int* p_end_line_index,
+ OFF* p_contents_beg, OFF* p_contents_end)
+{
+ OFF off = beg;
+ OFF contents_beg = 0;
+ OFF contents_end = 0;
+ int line_index = 0;
+ int len = 0;
+
+ if(CH(off) != _T('['))
+ return FALSE;
+ off++;
+
+ while(1) {
+ OFF line_end = lines[line_index].end;
+
+ while(off < line_end) {
+ if(CH(off) == _T('\\') && off+1 < ctx->size && (ISPUNCT(off+1) || ISNEWLINE(off+1))) {
+ if(contents_end == 0) {
+ contents_beg = off;
+ *p_beg_line_index = line_index;
+ }
+ contents_end = off + 2;
+ off += 2;
+ } else if(CH(off) == _T('[')) {
+ return FALSE;
+ } else if(CH(off) == _T(']')) {
+ if(contents_beg < contents_end) {
+ /* Success. */
+ *p_contents_beg = contents_beg;
+ *p_contents_end = contents_end;
+ *p_end = off+1;
+ *p_end_line_index = line_index;
+ return TRUE;
+ } else {
+ /* Link label must have some non-whitespace contents. */
+ return FALSE;
+ }
+ } else {
+ unsigned codepoint;
+ SZ char_size;
+
+ codepoint = md_decode_unicode(ctx->text, off, ctx->size, &char_size);
+ if(!ISUNICODEWHITESPACE_(codepoint)) {
+ if(contents_end == 0) {
+ contents_beg = off;
+ *p_beg_line_index = line_index;
+ }
+ contents_end = off + char_size;
+ }
+
+ off += char_size;
+ }
+
+ len++;
+ if(len > 999)
+ return FALSE;
+ }
+
+ line_index++;
+ len++;
+ if(line_index < n_lines)
+ off = lines[line_index].beg;
+ else
+ break;
+ }
+
+ return FALSE;
+}
+
+static int
+md_is_link_destination_A(MD_CTX* ctx, OFF beg, OFF max_end, OFF* p_end,
+ OFF* p_contents_beg, OFF* p_contents_end)
+{
+ OFF off = beg;
+
+ if(off >= max_end || CH(off) != _T('<'))
+ return FALSE;
+ off++;
+
+ while(off < max_end) {
+ if(CH(off) == _T('\\') && off+1 < max_end && ISPUNCT(off+1)) {
+ off += 2;
+ continue;
+ }
+
+ if(ISNEWLINE(off) || CH(off) == _T('<'))
+ return FALSE;
+
+ if(CH(off) == _T('>')) {
+ /* Success. */
+ *p_contents_beg = beg+1;
+ *p_contents_end = off;
+ *p_end = off+1;
+ return TRUE;
+ }
+
+ off++;
+ }
+
+ return FALSE;
+}
+
+static int
+md_is_link_destination_B(MD_CTX* ctx, OFF beg, OFF max_end, OFF* p_end,
+ OFF* p_contents_beg, OFF* p_contents_end)
+{
+ OFF off = beg;
+ int parenthesis_level = 0;
+
+ while(off < max_end) {
+ if(CH(off) == _T('\\') && off+1 < max_end && ISPUNCT(off+1)) {
+ off += 2;
+ continue;
+ }
+
+ if(ISWHITESPACE(off) || ISCNTRL(off))
+ break;
+
+ /* Link destination may include balanced pairs of unescaped '(' ')'.
+ * Note we limit the maximal nesting level by 32 to protect us from
+ * https://github.com/jgm/cmark/issues/214 */
+ if(CH(off) == _T('(')) {
+ parenthesis_level++;
+ if(parenthesis_level > 32)
+ return FALSE;
+ } else if(CH(off) == _T(')')) {
+ if(parenthesis_level == 0)
+ break;
+ parenthesis_level--;
+ }
+
+ off++;
+ }
+
+ if(parenthesis_level != 0 || off == beg)
+ return FALSE;
+
+ /* Success. */
+ *p_contents_beg = beg;
+ *p_contents_end = off;
+ *p_end = off;
+ return TRUE;
+}
+
+static inline int
+md_is_link_destination(MD_CTX* ctx, OFF beg, OFF max_end, OFF* p_end,
+ OFF* p_contents_beg, OFF* p_contents_end)
+{
+ if(CH(beg) == _T('<'))
+ return md_is_link_destination_A(ctx, beg, max_end, p_end, p_contents_beg, p_contents_end);
+ else
+ return md_is_link_destination_B(ctx, beg, max_end, p_end, p_contents_beg, p_contents_end);
+}
+
+static int
+md_is_link_title(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg,
+ OFF* p_end, int* p_beg_line_index, int* p_end_line_index,
+ OFF* p_contents_beg, OFF* p_contents_end)
+{
+ OFF off = beg;
+ CHAR closer_char;
+ int line_index = 0;
+
+ /* White space with up to one line break. */
+ while(off < lines[line_index].end && ISWHITESPACE(off))
+ off++;
+ if(off >= lines[line_index].end) {
+ line_index++;
+ if(line_index >= n_lines)
+ return FALSE;
+ off = lines[line_index].beg;
+ }
+ if(off == beg)
+ return FALSE;
+
+ *p_beg_line_index = line_index;
+
+ /* First char determines how to detect end of it. */
+ switch(CH(off)) {
+ case _T('"'): closer_char = _T('"'); break;
+ case _T('\''): closer_char = _T('\''); break;
+ case _T('('): closer_char = _T(')'); break;
+ default: return FALSE;
+ }
+ off++;
+
+ *p_contents_beg = off;
+
+ while(line_index < n_lines) {
+ OFF line_end = lines[line_index].end;
+
+ while(off < line_end) {
+ if(CH(off) == _T('\\') && off+1 < ctx->size && (ISPUNCT(off+1) || ISNEWLINE(off+1))) {
+ off++;
+ } else if(CH(off) == closer_char) {
+ /* Success. */
+ *p_contents_end = off;
+ *p_end = off+1;
+ *p_end_line_index = line_index;
+ return TRUE;
+ } else if(closer_char == _T(')') && CH(off) == _T('(')) {
+ /* ()-style title cannot contain (unescaped '(')) */
+ return FALSE;
+ }
+
+ off++;
+ }
+
+ line_index++;
+ }
+
+ return FALSE;
+}
+
+/* Returns 0 if it is not a reference definition.
+ *
+ * Returns N > 0 if it is a reference definition. N then corresponds to the
+ * number of lines forming it). In this case the definition is stored for
+ * resolving any links referring to it.
+ *
+ * Returns -1 in case of an error (out of memory).
+ */
+static int
+md_is_link_reference_definition(MD_CTX* ctx, const MD_LINE* lines, int n_lines)
+{
+ OFF label_contents_beg;
+ OFF label_contents_end;
+ int label_contents_line_index = -1;
+ int label_is_multiline;
+ CHAR* label;
+ SZ label_size;
+ int label_needs_free = FALSE;
+ OFF dest_contents_beg;
+ OFF dest_contents_end;
+ OFF title_contents_beg;
+ OFF title_contents_end;
+ int title_contents_line_index;
+ int title_is_multiline;
+ OFF off;
+ int line_index = 0;
+ int tmp_line_index;
+ MD_REF_DEF* def;
+ int ret;
+
+ /* Link label. */
+ if(!md_is_link_label(ctx, lines, n_lines, lines[0].beg,
+ &off, &label_contents_line_index, &line_index,
+ &label_contents_beg, &label_contents_end))
+ return FALSE;
+ label_is_multiline = (label_contents_line_index != line_index);
+
+ /* Colon. */
+ if(off >= lines[line_index].end || CH(off) != _T(':'))
+ return FALSE;
+ off++;
+
+ /* Optional white space with up to one line break. */
+ while(off < lines[line_index].end && ISWHITESPACE(off))
+ off++;
+ if(off >= lines[line_index].end) {
+ line_index++;
+ if(line_index >= n_lines)
+ return FALSE;
+ off = lines[line_index].beg;
+ }
+
+ /* Link destination. */
+ if(!md_is_link_destination(ctx, off, lines[line_index].end,
+ &off, &dest_contents_beg, &dest_contents_end))
+ return FALSE;
+
+ /* (Optional) title. Note we interpret it as an title only if nothing
+ * more follows on its last line. */
+ if(md_is_link_title(ctx, lines + line_index, n_lines - line_index, off,
+ &off, &title_contents_line_index, &tmp_line_index,
+ &title_contents_beg, &title_contents_end)
+ && off >= lines[line_index + tmp_line_index].end)
+ {
+ title_is_multiline = (tmp_line_index != title_contents_line_index);
+ title_contents_line_index += line_index;
+ line_index += tmp_line_index;
+ } else {
+ /* Not a title. */
+ title_is_multiline = FALSE;
+ title_contents_beg = off;
+ title_contents_end = off;
+ title_contents_line_index = 0;
+ }
+
+ /* Nothing more can follow on the last line. */
+ if(off < lines[line_index].end)
+ return FALSE;
+
+ /* Construct label. */
+ if(!label_is_multiline) {
+ label = (CHAR*) STR(label_contents_beg);
+ label_size = label_contents_end - label_contents_beg;
+ label_needs_free = FALSE;
+ } else {
+ MD_CHECK(md_merge_lines_alloc(ctx, label_contents_beg, label_contents_end,
+ lines + label_contents_line_index, n_lines - label_contents_line_index,
+ _T(' '), &label, &label_size));
+ label_needs_free = TRUE;
+ }
+
+ /* Store the reference definition. */
+ if(ctx->n_ref_defs >= ctx->alloc_ref_defs) {
+ MD_REF_DEF* new_defs;
+
+ ctx->alloc_ref_defs = (ctx->alloc_ref_defs > 0 ? ctx->alloc_ref_defs * 2 : 16);
+ new_defs = (MD_REF_DEF*) realloc(ctx->ref_defs, ctx->alloc_ref_defs * sizeof(MD_REF_DEF));
+ if(new_defs == NULL) {
+ MD_LOG("realloc() failed.");
+ ret = -1;
+ goto abort;
+ }
+
+ ctx->ref_defs = new_defs;
+ }
+
+ def = &ctx->ref_defs[ctx->n_ref_defs];
+ memset(def, 0, sizeof(MD_REF_DEF));
+
+ def->label = label;
+ def->label_size = label_size;
+ def->label_needs_free = label_needs_free;
+
+ def->dest_beg = dest_contents_beg;
+ def->dest_end = dest_contents_end;
+
+ if(title_contents_beg >= title_contents_end) {
+ def->title = NULL;
+ def->title_size = 0;
+ } else if(!title_is_multiline) {
+ def->title = (CHAR*) STR(title_contents_beg);
+ def->title_size = title_contents_end - title_contents_beg;
+ } else {
+ MD_CHECK(md_merge_lines_alloc(ctx, title_contents_beg, title_contents_end,
+ lines + title_contents_line_index, n_lines - title_contents_line_index,
+ _T('\n'), &def->title, &def->title_size));
+ def->title_needs_free = TRUE;
+ }
+
+ /* Success. */
+ ctx->n_ref_defs++;
+ return line_index + 1;
+
+abort:
+ /* Failure. */
+ if(label_needs_free)
+ free(label);
+ return -1;
+}
+
+static int
+md_is_link_reference(MD_CTX* ctx, const MD_LINE* lines, int n_lines,
+ OFF beg, OFF end, MD_LINK_ATTR* attr)
+{
+ const MD_REF_DEF* def;
+ const MD_LINE* beg_line;
+ const MD_LINE* end_line;
+ CHAR* label;
+ SZ label_size;
+ int ret;
+
+ MD_ASSERT(CH(beg) == _T('[') || CH(beg) == _T('!'));
+ MD_ASSERT(CH(end-1) == _T(']'));
+
+ beg += (CH(beg) == _T('!') ? 2 : 1);
+ end--;
+
+ /* Find lines corresponding to the beg and end positions. */
+ MD_ASSERT(lines[0].beg <= beg);
+ beg_line = lines;
+ while(beg >= beg_line->end)
+ beg_line++;
+
+ MD_ASSERT(end <= lines[n_lines-1].end);
+ end_line = beg_line;
+ while(end >= end_line->end)
+ end_line++;
+
+ if(beg_line != end_line) {
+ MD_CHECK(md_merge_lines_alloc(ctx, beg, end, beg_line,
+ n_lines - (beg_line - lines), _T(' '), &label, &label_size));
+ } else {
+ label = (CHAR*) STR(beg);
+ label_size = end - beg;
+ }
+
+ def = md_lookup_ref_def(ctx, label, label_size);
+ if(def != NULL) {
+ attr->dest_beg = def->dest_beg;
+ attr->dest_end = def->dest_end;
+ attr->title = def->title;
+ attr->title_size = def->title_size;
+ attr->title_needs_free = FALSE;
+ }
+
+ if(beg_line != end_line)
+ free(label);
+
+ ret = (def != NULL);
+
+abort:
+ return ret;
+}
+
+static int
+md_is_inline_link_spec(MD_CTX* ctx, const MD_LINE* lines, int n_lines,
+ OFF beg, OFF* p_end, MD_LINK_ATTR* attr)
+{
+ int line_index = 0;
+ int tmp_line_index;
+ OFF title_contents_beg;
+ OFF title_contents_end;
+ int title_contents_line_index;
+ int title_is_multiline;
+ OFF off = beg;
+ int ret = FALSE;
+
+ while(off >= lines[line_index].end)
+ line_index++;
+
+ MD_ASSERT(CH(off) == _T('('));
+ off++;
+
+ /* Optional white space with up to one line break. */
+ while(off < lines[line_index].end && ISWHITESPACE(off))
+ off++;
+ if(off >= lines[line_index].end && ISNEWLINE(off)) {
+ line_index++;
+ if(line_index >= n_lines)
+ return FALSE;
+ off = lines[line_index].beg;
+ }
+
+ /* Link destination may be omitted, but only when not also having a title. */
+ if(off < ctx->size && CH(off) == _T(')')) {
+ attr->dest_beg = off;
+ attr->dest_end = off;
+ attr->title = NULL;
+ attr->title_size = 0;
+ attr->title_needs_free = FALSE;
+ off++;
+ *p_end = off;
+ return TRUE;
+ }
+
+ /* Link destination. */
+ if(!md_is_link_destination(ctx, off, lines[line_index].end,
+ &off, &attr->dest_beg, &attr->dest_end))
+ return FALSE;
+
+ /* (Optional) title. */
+ if(md_is_link_title(ctx, lines + line_index, n_lines - line_index, off,
+ &off, &title_contents_line_index, &tmp_line_index,
+ &title_contents_beg, &title_contents_end))
+ {
+ title_is_multiline = (tmp_line_index != title_contents_line_index);
+ title_contents_line_index += line_index;
+ line_index += tmp_line_index;
+ } else {
+ /* Not a title. */
+ title_is_multiline = FALSE;
+ title_contents_beg = off;
+ title_contents_end = off;
+ title_contents_line_index = 0;
+ }
+
+ /* Optional whitespace followed with final ')'. */
+ while(off < lines[line_index].end && ISWHITESPACE(off))
+ off++;
+ if(off >= lines[line_index].end && ISNEWLINE(off)) {
+ line_index++;
+ if(line_index >= n_lines)
+ return FALSE;
+ off = lines[line_index].beg;
+ }
+ if(CH(off) != _T(')'))
+ goto abort;
+ off++;
+
+ if(title_contents_beg >= title_contents_end) {
+ attr->title = NULL;
+ attr->title_size = 0;
+ attr->title_needs_free = FALSE;
+ } else if(!title_is_multiline) {
+ attr->title = (CHAR*) STR(title_contents_beg);
+ attr->title_size = title_contents_end - title_contents_beg;
+ attr->title_needs_free = FALSE;
+ } else {
+ MD_CHECK(md_merge_lines_alloc(ctx, title_contents_beg, title_contents_end,
+ lines + title_contents_line_index, n_lines - title_contents_line_index,
+ _T('\n'), &attr->title, &attr->title_size));
+ attr->title_needs_free = TRUE;
+ }
+
+ *p_end = off;
+ ret = TRUE;
+
+abort:
+ return ret;
+}
+
+static void
+md_free_ref_defs(MD_CTX* ctx)
+{
+ int i;
+
+ for(i = 0; i < ctx->n_ref_defs; i++) {
+ MD_REF_DEF* def = &ctx->ref_defs[i];
+
+ if(def->label_needs_free)
+ free(def->label);
+ if(def->title_needs_free)
+ free(def->title);
+ }
+
+ free(ctx->ref_defs);
+}
+
+
+/******************************************
+ *** Processing Inlines (a.k.a Spans) ***
+ ******************************************/
+
+/* We process inlines in few phases:
+ *
+ * (1) We go through the block text and collect all significant characters
+ * which may start/end a span or some other significant position into
+ * ctx->marks[]. Core of this is what md_collect_marks() does.
+ *
+ * We also do some very brief preliminary context-less analysis, whether
+ * it might be opener or closer (e.g. of an emphasis span).
+ *
+ * This speeds the other steps as we do not need to re-iterate over all
+ * characters anymore.
+ *
+ * (2) We analyze each potential mark types, in order by their precedence.
+ *
+ * In each md_analyze_XXX() function, we re-iterate list of the marks,
+ * skipping already resolved regions (in preceding precedences) and try to
+ * resolve them.
+ *
+ * (2.1) For trivial marks, which are single (e.g. HTML entity), we just mark
+ * them as resolved.
+ *
+ * (2.2) For range-type marks, we analyze whether the mark could be closer
+ * and, if yes, whether there is some preceding opener it could satisfy.
+ *
+ * If not we check whether it could be really an opener and if yes, we
+ * remember it so subsequent closers may resolve it.
+ *
+ * (3) Finally, when all marks were analyzed, we render the block contents
+ * by calling MD_RENDERER::text() callback, interrupting by ::enter_span()
+ * or ::close_span() whenever we reach a resolved mark.
+ */
+
+
+/* The mark structure.
+ *
+ * '\\': Maybe escape sequence.
+ * '\0': NULL char.
+ * '*': Maybe (strong) emphasis start/end.
+ * '_': Maybe (strong) emphasis start/end.
+ * '~': Maybe strikethrough start/end (needs MD_FLAG_STRIKETHROUGH).
+ * '`': Maybe code span start/end.
+ * '&': Maybe start of entity.
+ * ';': Maybe end of entity.
+ * '<': Maybe start of raw HTML or autolink.
+ * '>': Maybe end of raw HTML or autolink.
+ * '[': Maybe start of link label or link text.
+ * '!': Equivalent of '[' for image.
+ * ']': Maybe end of link label or link text.
+ * '@': Maybe permissive e-mail auto-link (needs MD_FLAG_PERMISSIVEEMAILAUTOLINKS).
+ * ':': Maybe permissive URL auto-link (needs MD_FLAG_PERMISSIVEURLAUTOLINKS).
+ * '.': Maybe permissive WWW auto-link (needs MD_FLAG_PERMISSIVEWWWAUTOLINKS).
+ * 'D': Dummy mark, it reserves a space for splitting a previous mark
+ * (e.g. emphasis) or to make more space for storing some special data
+ * related to the preceding mark (e.g. link).
+ *
+ * Note that not all instances of these chars in the text imply creation of the
+ * structure. Only those which have (or may have, after we see more context)
+ * the special meaning.
+ *
+ * (Keep this struct as small as possible to fit as much of them into CPU
+ * cache line.)
+ */
+struct MD_MARK_tag {
+ OFF beg;
+ OFF end;
+
+ /* For unresolved openers, 'prev' and 'next' form the chain of open openers
+ * of given type 'ch'.
+ *
+ * During resolving, we disconnect from the chain and point to the
+ * corresponding counterpart so opener points to its closer and vice versa.
+ */
+ int prev;
+ int next;
+ CHAR ch;
+ unsigned char flags;
+};
+
+/* Mark flags (these apply to ALL mark types). */
+#define MD_MARK_POTENTIAL_OPENER 0x01 /* Maybe opener. */
+#define MD_MARK_POTENTIAL_CLOSER 0x02 /* Maybe closer. */
+#define MD_MARK_OPENER 0x04 /* Definitely opener. */
+#define MD_MARK_CLOSER 0x08 /* Definitely closer. */
+#define MD_MARK_RESOLVED 0x10 /* Resolved in any definite way. */
+
+/* Mark flags specific for various mark types (so they can share bits). */
+#define MD_MARK_EMPH_INTRAWORD 0x20 /* Helper for the "rule of 3". */
+#define MD_MARK_EMPH_MOD3_0 0x40
+#define MD_MARK_EMPH_MOD3_1 0x80
+#define MD_MARK_EMPH_MOD3_2 (0x40 | 0x80)
+#define MD_MARK_EMPH_MOD3_MASK (0x40 | 0x80)
+#define MD_MARK_AUTOLINK 0x20 /* Distinguisher for '<', '>'. */
+#define MD_MARK_VALIDPERMISSIVEAUTOLINK 0x20 /* For permissive autolinks. */
+
+static MD_MARKCHAIN*
+md_asterisk_chain(MD_CTX* ctx, unsigned flags)
+{
+ switch(flags & (MD_MARK_EMPH_INTRAWORD | MD_MARK_EMPH_MOD3_MASK)) {
+ case MD_MARK_EMPH_INTRAWORD | MD_MARK_EMPH_MOD3_0: return &ASTERISK_OPENERS_intraword_mod3_0;
+ case MD_MARK_EMPH_INTRAWORD | MD_MARK_EMPH_MOD3_1: return &ASTERISK_OPENERS_intraword_mod3_1;
+ case MD_MARK_EMPH_INTRAWORD | MD_MARK_EMPH_MOD3_2: return &ASTERISK_OPENERS_intraword_mod3_2;
+ case MD_MARK_EMPH_MOD3_0: return &ASTERISK_OPENERS_extraword_mod3_0;
+ case MD_MARK_EMPH_MOD3_1: return &ASTERISK_OPENERS_extraword_mod3_1;
+ case MD_MARK_EMPH_MOD3_2: return &ASTERISK_OPENERS_extraword_mod3_2;
+ default: MD_UNREACHABLE();
+ }
+ return NULL;
+}
+
+static MD_MARKCHAIN*
+md_mark_chain(MD_CTX* ctx, int mark_index)
+{
+ MD_MARK* mark = &ctx->marks[mark_index];
+
+ switch(mark->ch) {
+ case _T('*'): return md_asterisk_chain(ctx, mark->flags);
+ case _T('_'): return &UNDERSCORE_OPENERS;
+ case _T('~'): return &TILDE_OPENERS;
+ case _T('['): return &BRACKET_OPENERS;
+ case _T('|'): return &TABLECELLBOUNDARIES;
+ default: return NULL;
+ }
+}
+
+static MD_MARK*
+md_push_mark(MD_CTX* ctx)
+{
+ if(ctx->n_marks >= ctx->alloc_marks) {
+ MD_MARK* new_marks;
+
+ ctx->alloc_marks = (ctx->alloc_marks > 0 ? ctx->alloc_marks * 2 : 64);
+ new_marks = realloc(ctx->marks, ctx->alloc_marks * sizeof(MD_MARK));
+ if(new_marks == NULL) {
+ MD_LOG("realloc() failed.");
+ return NULL;
+ }
+
+ ctx->marks = new_marks;
+ }
+
+ return &ctx->marks[ctx->n_marks++];
+}
+
+#define PUSH_MARK_() \
+ do { \
+ mark = md_push_mark(ctx); \
+ if(mark == NULL) { \
+ ret = -1; \
+ goto abort; \
+ } \
+ } while(0)
+
+#define PUSH_MARK(ch_, beg_, end_, flags_) \
+ do { \
+ PUSH_MARK_(); \
+ mark->beg = (beg_); \
+ mark->end = (end_); \
+ mark->prev = -1; \
+ mark->next = -1; \
+ mark->ch = (char)(ch_); \
+ mark->flags = (flags_); \
+ } while(0)
+
+
+static void
+md_mark_chain_append(MD_CTX* ctx, MD_MARKCHAIN* chain, int mark_index)
+{
+ if(chain->tail >= 0)
+ ctx->marks[chain->tail].next = mark_index;
+ else
+ chain->head = mark_index;
+
+ ctx->marks[mark_index].prev = chain->tail;
+ chain->tail = mark_index;
+}
+
+/* Sometimes, we need to store a pointer into the mark. It is quite rare
+ * so we do not bother to make MD_MARK use union, and it can only happen
+ * for dummy marks. */
+static inline void
+md_mark_store_ptr(MD_CTX* ctx, int mark_index, void* ptr)
+{
+ MD_MARK* mark = &ctx->marks[mark_index];
+ MD_ASSERT(mark->ch == 'D');
+
+ /* Check only members beg and end are misused for this. */
+ MD_ASSERT(sizeof(void*) <= 2 * sizeof(OFF));
+ memcpy(mark, &ptr, sizeof(void*));
+}
+
+static inline void*
+md_mark_get_ptr(MD_CTX* ctx, int mark_index)
+{
+ void* ptr;
+ MD_MARK* mark = &ctx->marks[mark_index];
+ MD_ASSERT(mark->ch == 'D');
+ memcpy(&ptr, mark, sizeof(void*));
+ return ptr;
+}
+
+static void
+md_resolve_range(MD_CTX* ctx, MD_MARKCHAIN* chain, int opener_index, int closer_index)
+{
+ MD_MARK* opener = &ctx->marks[opener_index];
+ MD_MARK* closer = &ctx->marks[closer_index];
+
+ /* Remove opener from the list of openers. */
+ if(chain != NULL) {
+ if(opener->prev >= 0)
+ ctx->marks[opener->prev].next = opener->next;
+ else
+ chain->head = opener->next;
+
+ if(opener->next >= 0)
+ ctx->marks[opener->next].prev = opener->prev;
+ else
+ chain->tail = opener->prev;
+ }
+
+ /* Interconnect opener and closer and mark both as resolved. */
+ opener->next = closer_index;
+ opener->flags |= MD_MARK_OPENER | MD_MARK_RESOLVED;
+ closer->prev = opener_index;
+ closer->flags |= MD_MARK_CLOSER | MD_MARK_RESOLVED;
+}
+
+
+#define MD_ROLLBACK_ALL 0
+#define MD_ROLLBACK_CROSSING 1
+
+/* In the range ctx->marks[opener_index] ... [closer_index], undo some or all
+ * resolvings accordingly to these rules:
+ *
+ * (1) All openers BEFORE the range corresponding to any closer inside the
+ * range are un-resolved and they are re-added to their respective chains
+ * of unresolved openers. This ensures we can reuse the opener for closers
+ * AFTER the range.
+ *
+ * (2) If 'how' is MD_ROLLBACK_ALL, then ALL resolved marks inside the range
+ * are discarded.
+ *
+ * (3) If 'how' is MD_ROLLBACK_CROSSING, only closers with openers handled
+ * in (1) are discarded. I.e. pairs of openers and closers which are both
+ * inside the range are retained as well as any unpaired marks.
+ */
+static void
+md_rollback(MD_CTX* ctx, int opener_index, int closer_index, int how)
+{
+ int i;
+ int mark_index;
+
+ /* Cut all unresolved openers at the mark index. */
+ for(i = OPENERS_CHAIN_FIRST; i < OPENERS_CHAIN_LAST+1; i++) {
+ MD_MARKCHAIN* chain = &ctx->mark_chains[i];
+
+ while(chain->tail >= opener_index)
+ chain->tail = ctx->marks[chain->tail].prev;
+
+ if(chain->tail >= 0)
+ ctx->marks[chain->tail].next = -1;
+ else
+ chain->head = -1;
+ }
+
+ /* Go backwards so that un-resolved openers are re-added into their
+ * respective chains, in the right order. */
+ mark_index = closer_index - 1;
+ while(mark_index > opener_index) {
+ MD_MARK* mark = &ctx->marks[mark_index];
+ int mark_flags = mark->flags;
+ int discard_flag = (how == MD_ROLLBACK_ALL);
+
+ if(mark->flags & MD_MARK_CLOSER) {
+ int mark_opener_index = mark->prev;
+
+ /* Undo opener BEFORE the range. */
+ if(mark_opener_index < opener_index) {
+ MD_MARK* mark_opener = &ctx->marks[mark_opener_index];
+ MD_MARKCHAIN* chain;
+
+ mark_opener->flags &= ~(MD_MARK_OPENER | MD_MARK_CLOSER | MD_MARK_RESOLVED);
+ chain = md_mark_chain(ctx, opener_index);
+ if(chain != NULL) {
+ md_mark_chain_append(ctx, chain, mark_opener_index);
+ discard_flag = 1;
+ }
+ }
+ }
+
+ /* And reset our flags. */
+ if(discard_flag)
+ mark->flags &= ~(MD_MARK_OPENER | MD_MARK_CLOSER | MD_MARK_RESOLVED);
+
+ /* Jump as far as we can over unresolved or non-interesting marks. */
+ switch(how) {
+ case MD_ROLLBACK_CROSSING:
+ if((mark_flags & MD_MARK_CLOSER) && mark->prev > opener_index) {
+ /* If we are closer with opener INSIDE the range, there may
+ * not be any other crosser inside the subrange. */
+ mark_index = mark->prev;
+ break;
+ }
+ /* Pass through. */
+ default:
+ mark_index--;
+ break;
+ }
+ }
+}
+
+static void
+md_build_mark_char_map(MD_CTX* ctx)
+{
+ memset(ctx->mark_char_map, 0, sizeof(ctx->mark_char_map));
+
+ ctx->mark_char_map['\\'] = 1;
+ ctx->mark_char_map['*'] = 1;
+ ctx->mark_char_map['_'] = 1;
+ ctx->mark_char_map['`'] = 1;
+ ctx->mark_char_map['&'] = 1;
+ ctx->mark_char_map[';'] = 1;
+ ctx->mark_char_map['<'] = 1;
+ ctx->mark_char_map['>'] = 1;
+ ctx->mark_char_map['['] = 1;
+ ctx->mark_char_map['!'] = 1;
+ ctx->mark_char_map[']'] = 1;
+ ctx->mark_char_map['\0'] = 1;
+
+ if(ctx->parser.flags & MD_FLAG_STRIKETHROUGH)
+ ctx->mark_char_map['~'] = 1;
+
+ if(ctx->parser.flags & MD_FLAG_LATEXMATHSPANS)
+ ctx->mark_char_map['$'] = 1;
+
+ if(ctx->parser.flags & MD_FLAG_PERMISSIVEEMAILAUTOLINKS)
+ ctx->mark_char_map['@'] = 1;
+
+ if(ctx->parser.flags & MD_FLAG_PERMISSIVEURLAUTOLINKS)
+ ctx->mark_char_map[':'] = 1;
+
+ if(ctx->parser.flags & MD_FLAG_PERMISSIVEWWWAUTOLINKS)
+ ctx->mark_char_map['.'] = 1;
+
+ if(ctx->parser.flags & MD_FLAG_TABLES)
+ ctx->mark_char_map['|'] = 1;
+
+ if(ctx->parser.flags & MD_FLAG_COLLAPSEWHITESPACE) {
+ int i;
+
+ for(i = 0; i < (int) sizeof(ctx->mark_char_map); i++) {
+ if(ISWHITESPACE_(i))
+ ctx->mark_char_map[i] = 1;
+ }
+ }
+}
+
+/* We limit code span marks to lower then 32 backticks. This solves the
+ * pathologic case of too many openers, each of different length: Their
+ * resolving would be then O(n^2). */
+#define CODESPAN_MARK_MAXLEN 32
+
+static int
+md_is_code_span(MD_CTX* ctx, const MD_LINE* lines, int n_lines, OFF beg,
+ OFF* p_opener_beg, OFF* p_opener_end,
+ OFF* p_closer_beg, OFF* p_closer_end,
+ OFF last_potential_closers[CODESPAN_MARK_MAXLEN],
+ int* p_reached_paragraph_end)
+{
+ OFF opener_beg = beg;
+ OFF opener_end;
+ OFF closer_beg;
+ OFF closer_end;
+ SZ mark_len;
+ OFF line_end;
+ int has_space_after_opener = FALSE;
+ int has_eol_after_opener = FALSE;
+ int has_space_before_closer = FALSE;
+ int has_eol_before_closer = FALSE;
+ int has_only_space = TRUE;
+ int line_index = 0;
+
+ line_end = lines[0].end;
+ opener_end = opener_beg;
+ while(opener_end < line_end && CH(opener_end) == _T('`'))
+ opener_end++;
+ has_space_after_opener = (opener_end < line_end && CH(opener_end) == _T(' '));
+ has_eol_after_opener = (opener_end == line_end);
+
+ /* The caller needs to know end of the opening mark even if we fail. */
+ *p_opener_end = opener_end;
+
+ mark_len = opener_end - opener_beg;
+ if(mark_len > CODESPAN_MARK_MAXLEN)
+ return FALSE;
+
+ /* Check whether we already know there is no closer of this length.
+ * If so, re-scan does no sense. This fixes issue #59. */
+ if(last_potential_closers[mark_len-1] >= lines[n_lines-1].end ||
+ (*p_reached_paragraph_end && last_potential_closers[mark_len-1] < opener_end))
+ return FALSE;
+
+ closer_beg = opener_end;
+ closer_end = opener_end;
+
+ /* Find closer mark. */
+ while(TRUE) {
+ while(closer_beg < line_end && CH(closer_beg) != _T('`')) {
+ if(CH(closer_beg) != _T(' '))
+ has_only_space = FALSE;
+ closer_beg++;
+ }
+ closer_end = closer_beg;
+ while(closer_end < line_end && CH(closer_end) == _T('`'))
+ closer_end++;
+
+ if(closer_end - closer_beg == mark_len) {
+ /* Success. */
+ has_space_before_closer = (closer_beg > lines[line_index].beg && CH(closer_beg-1) == _T(' '));
+ has_eol_before_closer = (closer_beg == lines[line_index].beg);
+ break;
+ }
+
+ if(closer_end - closer_beg > 0) {
+ /* We have found a back-tick which is not part of the closer. */
+ has_only_space = FALSE;
+
+ /* But if we eventually fail, remember it as a potential closer
+ * of its own length for future attempts. This mitigates needs for
+ * rescans. */
+ if(closer_end - closer_beg < CODESPAN_MARK_MAXLEN) {
+ if(closer_beg > last_potential_closers[closer_end - closer_beg - 1])
+ last_potential_closers[closer_end - closer_beg - 1] = closer_beg;
+ }
+ }
+
+ if(closer_end >= line_end) {
+ line_index++;
+ if(line_index >= n_lines) {
+ /* Reached end of the paragraph and still nothing. */
+ *p_reached_paragraph_end = TRUE;
+ return FALSE;
+ }
+ /* Try on the next line. */
+ line_end = lines[line_index].end;
+ closer_beg = lines[line_index].beg;
+ } else {
+ closer_beg = closer_end;
+ }
+ }
+
+ /* If there is a space or a new line both after and before the opener
+ * (and if the code span is not made of spaces only), consume one initial
+ * and one trailing space as part of the marks. */
+ if(!has_only_space &&
+ (has_space_after_opener || has_eol_after_opener) &&
+ (has_space_before_closer || has_eol_before_closer))
+ {
+ if(has_space_after_opener)
+ opener_end++;
+ else
+ opener_end = lines[1].beg;
+
+ if(has_space_before_closer)
+ closer_beg--;
+ else {
+ closer_beg = lines[line_index-1].end;
+ /* We need to eat the preceding "\r\n" but not any line trailing
+ * spaces. */
+ while(closer_beg < ctx->size && ISBLANK(closer_beg))
+ closer_beg++;
+ }
+ }
+
+ *p_opener_beg = opener_beg;
+ *p_opener_end = opener_end;
+ *p_closer_beg = closer_beg;
+ *p_closer_end = closer_end;
+ return TRUE;
+}
+
+static int
+md_is_autolink_uri(MD_CTX* ctx, OFF beg, OFF max_end, OFF* p_end)
+{
+ OFF off = beg+1;
+
+ MD_ASSERT(CH(beg) == _T('<'));
+
+ /* Check for scheme. */
+ if(off >= max_end || !ISASCII(off))
+ return FALSE;
+ off++;
+ while(1) {
+ if(off >= max_end)
+ return FALSE;
+ if(off - beg > 32)
+ return FALSE;
+ if(CH(off) == _T(':') && off - beg >= 3)
+ break;
+ if(!ISALNUM(off) && CH(off) != _T('+') && CH(off) != _T('-') && CH(off) != _T('.'))
+ return FALSE;
+ off++;
+ }
+
+ /* Check the path after the scheme. */
+ while(off < max_end && CH(off) != _T('>')) {
+ if(ISWHITESPACE(off) || ISCNTRL(off) || CH(off) == _T('<'))
+ return FALSE;
+ off++;
+ }
+
+ if(off >= max_end)
+ return FALSE;
+
+ MD_ASSERT(CH(off) == _T('>'));
+ *p_end = off+1;
+ return TRUE;
+}
+
+static int
+md_is_autolink_email(MD_CTX* ctx, OFF beg, OFF max_end, OFF* p_end)
+{
+ OFF off = beg + 1;
+ int label_len;
+
+ MD_ASSERT(CH(beg) == _T('<'));
+
+ /* The code should correspond to this regexp:
+ /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+
+ @[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?
+ (?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
+ */
+
+ /* Username (before '@'). */
+ while(off < max_end && (ISALNUM(off) || ISANYOF(off, _T(".!#$%&'*+/=?^_`{|}~-"))))
+ off++;
+ if(off <= beg+1)
+ return FALSE;
+
+ /* '@' */
+ if(off >= max_end || CH(off) != _T('@'))
+ return FALSE;
+ off++;
+
+ /* Labels delimited with '.'; each label is sequence of 1 - 62 alnum
+ * characters or '-', but '-' is not allowed as first or last char. */
+ label_len = 0;
+ while(off < max_end) {
+ if(ISALNUM(off))
+ label_len++;
+ else if(CH(off) == _T('-') && label_len > 0)
+ label_len++;
+ else if(CH(off) == _T('.') && label_len > 0 && CH(off-1) != _T('-'))
+ label_len = 0;
+ else
+ break;
+
+ if(label_len > 62)
+ return FALSE;
+
+ off++;
+ }
+
+ if(label_len <= 0 || off >= max_end || CH(off) != _T('>') || CH(off-1) == _T('-'))
+ return FALSE;
+
+ *p_end = off+1;
+ return TRUE;
+}
+
+static int
+md_is_autolink(MD_CTX* ctx, OFF beg, OFF max_end, OFF* p_end, int* p_missing_mailto)
+{
+ if(md_is_autolink_uri(ctx, beg, max_end, p_end)) {
+ *p_missing_mailto = FALSE;
+ return TRUE;
+ }
+
+ if(md_is_autolink_email(ctx, beg, max_end, p_end)) {
+ *p_missing_mailto = TRUE;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static int
+md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode)
+{
+ int i;
+ int ret = 0;
+ MD_MARK* mark;
+ OFF codespan_last_potential_closers[CODESPAN_MARK_MAXLEN] = { 0 };
+ int codespan_scanned_till_paragraph_end = FALSE;
+
+ for(i = 0; i < n_lines; i++) {
+ const MD_LINE* line = &lines[i];
+ OFF off = line->beg;
+ OFF line_end = line->end;
+
+ while(TRUE) {
+ CHAR ch;
+
+#ifdef MD4C_USE_UTF16
+ /* For UTF-16, mark_char_map[] covers only ASCII. */
+ #define IS_MARK_CHAR(off) ((CH(off) < SIZEOF_ARRAY(ctx->mark_char_map)) && \
+ (ctx->mark_char_map[(unsigned char) CH(off)]))
+#else
+ /* For 8-bit encodings, mark_char_map[] covers all 256 elements. */
+ #define IS_MARK_CHAR(off) (ctx->mark_char_map[(unsigned char) CH(off)])
+#endif
+
+ /* Optimization: Use some loop unrolling. */
+ while(off + 3 < line_end && !IS_MARK_CHAR(off+0) && !IS_MARK_CHAR(off+1)
+ && !IS_MARK_CHAR(off+2) && !IS_MARK_CHAR(off+3))
+ off += 4;
+ while(off < line_end && !IS_MARK_CHAR(off+0))
+ off++;
+
+ if(off >= line_end)
+ break;
+
+ ch = CH(off);
+
+ /* A backslash escape.
+ * It can go beyond line->end as it may involve escaped new
+ * line to form a hard break. */
+ if(ch == _T('\\') && off+1 < ctx->size && (ISPUNCT(off+1) || ISNEWLINE(off+1))) {
+ /* Hard-break cannot be on the last line of the block. */
+ if(!ISNEWLINE(off+1) || i+1 < n_lines)
+ PUSH_MARK(ch, off, off+2, MD_MARK_RESOLVED);
+ off += 2;
+ continue;
+ }
+
+ /* A potential (string) emphasis start/end. */
+ if(ch == _T('*') || ch == _T('_')) {
+ OFF tmp = off+1;
+ int left_level; /* What precedes: 0 = whitespace; 1 = punctuation; 2 = other char. */
+ int right_level; /* What follows: 0 = whitespace; 1 = punctuation; 2 = other char. */
+
+ while(tmp < line_end && CH(tmp) == ch)
+ tmp++;
+
+ if(off == line->beg || ISUNICODEWHITESPACEBEFORE(off))
+ left_level = 0;
+ else if(ISUNICODEPUNCTBEFORE(off))
+ left_level = 1;
+ else
+ left_level = 2;
+
+ if(tmp == line_end || ISUNICODEWHITESPACE(tmp))
+ right_level = 0;
+ else if(ISUNICODEPUNCT(tmp))
+ right_level = 1;
+ else
+ right_level = 2;
+
+ /* Intra-word underscore doesn't have special meaning. */
+ if(ch == _T('_') && left_level == 2 && right_level == 2) {
+ left_level = 0;
+ right_level = 0;
+ }
+
+ if(left_level != 0 || right_level != 0) {
+ unsigned flags = 0;
+
+ if(left_level > 0 && left_level >= right_level)
+ flags |= MD_MARK_POTENTIAL_CLOSER;
+ if(right_level > 0 && right_level >= left_level)
+ flags |= MD_MARK_POTENTIAL_OPENER;
+ if(left_level == 2 && right_level == 2)
+ flags |= MD_MARK_EMPH_INTRAWORD;
+
+ /* For "the rule of three" we need to remember the original
+ * size of the mark (modulo three), before we potentially
+ * split the mark when being later resolved partially by some
+ * shorter closer. */
+ switch((tmp - off) % 3) {
+ case 0: flags |= MD_MARK_EMPH_MOD3_0; break;
+ case 1: flags |= MD_MARK_EMPH_MOD3_1; break;
+ case 2: flags |= MD_MARK_EMPH_MOD3_2; break;
+ }
+
+ PUSH_MARK(ch, off, tmp, flags);
+
+ /* During resolving, multiple asterisks may have to be
+ * split into independent span start/ends. Consider e.g.
+ * "**foo* bar*". Therefore we push also some empty dummy
+ * marks to have enough space for that. */
+ off++;
+ while(off < tmp) {
+ PUSH_MARK('D', off, off, 0);
+ off++;
+ }
+ continue;
+ }
+
+ off = tmp;
+ continue;
+ }
+
+ /* A potential code span start/end. */
+ if(ch == _T('`')) {
+ OFF opener_beg, opener_end;
+ OFF closer_beg, closer_end;
+ int is_code_span;
+
+ is_code_span = md_is_code_span(ctx, lines + i, n_lines - i, off,
+ &opener_beg, &opener_end, &closer_beg, &closer_end,
+ codespan_last_potential_closers,
+ &codespan_scanned_till_paragraph_end);
+ if(is_code_span) {
+ PUSH_MARK(_T('`'), opener_beg, opener_end, MD_MARK_OPENER | MD_MARK_RESOLVED);
+ PUSH_MARK(_T('`'), closer_beg, closer_end, MD_MARK_CLOSER | MD_MARK_RESOLVED);
+ ctx->marks[ctx->n_marks-2].next = ctx->n_marks-1;
+ ctx->marks[ctx->n_marks-1].prev = ctx->n_marks-2;
+
+ off = closer_end;
+
+ /* Advance the current line accordingly. */
+ while(off > line_end) {
+ i++;
+ line++;
+ line_end = line->end;
+ }
+ continue;
+ }
+
+ off = opener_end;
+ continue;
+ }
+
+ /* A potential entity start. */
+ if(ch == _T('&')) {
+ PUSH_MARK(ch, off, off+1, MD_MARK_POTENTIAL_OPENER);
+ off++;
+ continue;
+ }
+
+ /* A potential entity end. */
+ if(ch == _T(';')) {
+ /* We surely cannot be entity unless the previous mark is '&'. */
+ if(ctx->n_marks > 0 && ctx->marks[ctx->n_marks-1].ch == _T('&'))
+ PUSH_MARK(ch, off, off+1, MD_MARK_POTENTIAL_CLOSER);
+
+ off++;
+ continue;
+ }
+
+ /* A potential autolink or raw HTML start/end. */
+ if(ch == _T('<')) {
+ int is_autolink;
+ OFF autolink_end;
+ int missing_mailto;
+
+ if(!(ctx->parser.flags & MD_FLAG_NOHTMLSPANS)) {
+ int is_html;
+ OFF html_end;
+
+ /* Given the nature of the raw HTML, we have to recognize
+ * it here. Doing so later in md_analyze_lt_gt() could
+ * open can of worms of quadratic complexity. */
+ is_html = md_is_html_any(ctx, lines + i, n_lines - i, off,
+ lines[n_lines-1].end, &html_end);
+ if(is_html) {
+ PUSH_MARK(_T('<'), off, off, MD_MARK_OPENER | MD_MARK_RESOLVED);
+ PUSH_MARK(_T('>'), html_end, html_end, MD_MARK_CLOSER | MD_MARK_RESOLVED);
+ ctx->marks[ctx->n_marks-2].next = ctx->n_marks-1;
+ ctx->marks[ctx->n_marks-1].prev = ctx->n_marks-2;
+ off = html_end;
+
+ /* Advance the current line accordingly. */
+ while(off > line_end) {
+ i++;
+ line++;
+ line_end = line->end;
+ }
+ continue;
+ }
+ }
+
+ is_autolink = md_is_autolink(ctx, off, lines[n_lines-1].end,
+ &autolink_end, &missing_mailto);
+ if(is_autolink) {
+ PUSH_MARK((missing_mailto ? _T('@') : _T('<')), off, off+1,
+ MD_MARK_OPENER | MD_MARK_RESOLVED | MD_MARK_AUTOLINK);
+ PUSH_MARK(_T('>'), autolink_end-1, autolink_end,
+ MD_MARK_CLOSER | MD_MARK_RESOLVED | MD_MARK_AUTOLINK);
+ ctx->marks[ctx->n_marks-2].next = ctx->n_marks-1;
+ ctx->marks[ctx->n_marks-1].prev = ctx->n_marks-2;
+ off = autolink_end;
+ continue;
+ }
+
+ off++;
+ continue;
+ }
+
+ /* A potential link or its part. */
+ if(ch == _T('[') || (ch == _T('!') && off+1 < line_end && CH(off+1) == _T('['))) {
+ OFF tmp = (ch == _T('[') ? off+1 : off+2);
+ PUSH_MARK(ch, off, tmp, MD_MARK_POTENTIAL_OPENER);
+ off = tmp;
+ /* Two dummies to make enough place for data we need if it is
+ * a link. */
+ PUSH_MARK('D', off, off, 0);
+ PUSH_MARK('D', off, off, 0);
+ continue;
+ }
+ if(ch == _T(']')) {
+ PUSH_MARK(ch, off, off+1, MD_MARK_POTENTIAL_CLOSER);
+ off++;
+ continue;
+ }
+
+ /* A potential permissive e-mail autolink. */
+ if(ch == _T('@')) {
+ if(line->beg + 1 <= off && ISALNUM(off-1) &&
+ off + 3 < line->end && ISALNUM(off+1))
+ {
+ PUSH_MARK(ch, off, off+1, MD_MARK_POTENTIAL_OPENER);
+ /* Push a dummy as a reserve for a closer. */
+ PUSH_MARK('D', off, off, 0);
+ }
+
+ off++;
+ continue;
+ }
+
+ /* A potential permissive URL autolink. */
+ if(ch == _T(':')) {
+ static struct {
+ const CHAR* scheme;
+ SZ scheme_size;
+ const CHAR* suffix;
+ SZ suffix_size;
+ } scheme_map[] = {
+ /* In the order from the most frequently used, arguably. */
+ { _T("http"), 4, _T("//"), 2 },
+ { _T("https"), 5, _T("//"), 2 },
+ { _T("ftp"), 3, _T("//"), 2 }
+ };
+ int scheme_index;
+
+ for(scheme_index = 0; scheme_index < (int) SIZEOF_ARRAY(scheme_map); scheme_index++) {
+ const CHAR* scheme = scheme_map[scheme_index].scheme;
+ const SZ scheme_size = scheme_map[scheme_index].scheme_size;
+ const CHAR* suffix = scheme_map[scheme_index].suffix;
+ const SZ suffix_size = scheme_map[scheme_index].suffix_size;
+
+ if(line->beg + scheme_size <= off && md_ascii_eq(STR(off-scheme_size), scheme, scheme_size) &&
+ (line->beg + scheme_size == off || ISWHITESPACE(off-scheme_size-1) || ISANYOF(off-scheme_size-1, _T("*_~(["))) &&
+ off + 1 + suffix_size < line->end && md_ascii_eq(STR(off+1), suffix, suffix_size))
+ {
+ PUSH_MARK(ch, off-scheme_size, off+1+suffix_size, MD_MARK_POTENTIAL_OPENER);
+ /* Push a dummy as a reserve for a closer. */
+ PUSH_MARK('D', off, off, 0);
+ off += 1 + suffix_size;
+ continue;
+ }
+ }
+
+ off++;
+ continue;
+ }
+
+ /* A potential permissive WWW autolink. */
+ if(ch == _T('.')) {
+ if(line->beg + 3 <= off && md_ascii_eq(STR(off-3), _T("www"), 3) &&
+ (line->beg + 3 == off || ISWHITESPACE(off-4) || ISANYOF(off-4, _T("*_~(["))) &&
+ off + 1 < line_end)
+ {
+ PUSH_MARK(ch, off-3, off+1, MD_MARK_POTENTIAL_OPENER);
+ /* Push a dummy as a reserve for a closer. */
+ PUSH_MARK('D', off, off, 0);
+ off++;
+ continue;
+ }
+
+ off++;
+ continue;
+ }
+
+ /* A potential table cell boundary. */
+ if(table_mode && ch == _T('|')) {
+ PUSH_MARK(ch, off, off+1, 0);
+ off++;
+ continue;
+ }
+
+ /* A potential strikethrough start/end. */
+ if(ch == _T('~')) {
+ OFF tmp = off+1;
+
+ while(tmp < line_end && CH(tmp) == _T('~'))
+ tmp++;
+
+ PUSH_MARK(ch, off, tmp, MD_MARK_POTENTIAL_OPENER | MD_MARK_POTENTIAL_CLOSER);
+ off = tmp;
+ continue;
+ }
+
+ /* A potential equation start/end */
+ if(ch == _T('$')) {
+ /* We can have at most two consecutive $ signs,
+ * where two dollar signs signify a display equation. */
+ OFF tmp = off+1;
+
+ while(tmp < line_end && CH(tmp) == _T('$'))
+ tmp++;
+
+ if (tmp - off <= 2)
+ PUSH_MARK(ch, off, tmp, MD_MARK_POTENTIAL_OPENER | MD_MARK_POTENTIAL_CLOSER);
+ off = tmp;
+ continue;
+ }
+
+ /* Turn non-trivial whitespace into single space. */
+ if(ISWHITESPACE_(ch)) {
+ OFF tmp = off+1;
+
+ while(tmp < line_end && ISWHITESPACE(tmp))
+ tmp++;
+
+ if(tmp - off > 1 || ch != _T(' '))
+ PUSH_MARK(ch, off, tmp, MD_MARK_RESOLVED);
+
+ off = tmp;
+ continue;
+ }
+
+ /* NULL character. */
+ if(ch == _T('\0')) {
+ PUSH_MARK(ch, off, off+1, MD_MARK_RESOLVED);
+ off++;
+ continue;
+ }
+
+ off++;
+ }
+ }
+
+ /* Add a dummy mark at the end of the mark vector to simplify
+ * process_inlines(). */
+ PUSH_MARK(127, ctx->size, ctx->size, MD_MARK_RESOLVED);
+
+abort:
+ return ret;
+}
+
+static void
+md_analyze_bracket(MD_CTX* ctx, int mark_index)
+{
+ /* We cannot really resolve links here as for that we would need
+ * more context. E.g. a following pair of brackets (reference link),
+ * or enclosing pair of brackets (if the inner is the link, the outer
+ * one cannot be.)
+ *
+ * Therefore we here only construct a list of resolved '[' ']' pairs
+ * ordered by position of the closer. This allows ur to analyze what is
+ * or is not link in the right order, from inside to outside in case
+ * of nested brackets.
+ *
+ * The resolving itself is deferred into md_resolve_links().
+ */
+
+ MD_MARK* mark = &ctx->marks[mark_index];
+
+ if(mark->flags & MD_MARK_POTENTIAL_OPENER) {
+ md_mark_chain_append(ctx, &BRACKET_OPENERS, mark_index);
+ return;
+ }
+
+ if(BRACKET_OPENERS.tail >= 0) {
+ /* Pop the opener from the chain. */
+ int opener_index = BRACKET_OPENERS.tail;
+ MD_MARK* opener = &ctx->marks[opener_index];
+ if(opener->prev >= 0)
+ ctx->marks[opener->prev].next = -1;
+ else
+ BRACKET_OPENERS.head = -1;
+ BRACKET_OPENERS.tail = opener->prev;
+
+ /* Interconnect the opener and closer. */
+ opener->next = mark_index;
+ mark->prev = opener_index;
+
+ /* Add the pair into chain of potential links for md_resolve_links().
+ * Note we misuse opener->prev for this as opener->next points to its
+ * closer. */
+ if(ctx->unresolved_link_tail >= 0)
+ ctx->marks[ctx->unresolved_link_tail].prev = opener_index;
+ else
+ ctx->unresolved_link_head = opener_index;
+ ctx->unresolved_link_tail = opener_index;
+ opener->prev = -1;
+ }
+}
+
+/* Forward declaration. */
+static void md_analyze_link_contents(MD_CTX* ctx, const MD_LINE* lines, int n_lines,
+ int mark_beg, int mark_end);
+
+static int
+md_resolve_links(MD_CTX* ctx, const MD_LINE* lines, int n_lines)
+{
+ int opener_index = ctx->unresolved_link_head;
+ OFF last_link_beg = 0;
+ OFF last_link_end = 0;
+ OFF last_img_beg = 0;
+ OFF last_img_end = 0;
+
+ while(opener_index >= 0) {
+ MD_MARK* opener = &ctx->marks[opener_index];
+ int closer_index = opener->next;
+ MD_MARK* closer = &ctx->marks[closer_index];
+ int next_index = opener->prev;
+ MD_MARK* next_opener;
+ MD_MARK* next_closer;
+ MD_LINK_ATTR attr;
+ int is_link = FALSE;
+
+ if(next_index >= 0) {
+ next_opener = &ctx->marks[next_index];
+ next_closer = &ctx->marks[next_opener->next];
+ } else {
+ next_opener = NULL;
+ next_closer = NULL;
+ }
+
+ /* If nested ("[ [ ] ]"), we need to make sure that:
+ * - The outer does not end inside of (...) belonging to the inner.
+ * - The outer cannot be link if the inner is link (i.e. not image).
+ *
+ * (Note we here analyze from inner to outer as the marks are ordered
+ * by closer->beg.)
+ */
+ if((opener->beg < last_link_beg && closer->end < last_link_end) ||
+ (opener->beg < last_img_beg && closer->end < last_img_end) ||
+ (opener->beg < last_link_end && opener->ch == '['))
+ {
+ opener_index = next_index;
+ continue;
+ }
+
+ if(next_opener != NULL && next_opener->beg == closer->end) {
+ if(next_closer->beg > closer->end + 1) {
+ /* Might be full reference link. */
+ is_link = md_is_link_reference(ctx, lines, n_lines, next_opener->beg, next_closer->end, &attr);
+ } else {
+ /* Might be shortcut reference link. */
+ is_link = md_is_link_reference(ctx, lines, n_lines, opener->beg, closer->end, &attr);
+ }
+
+ if(is_link < 0)
+ return -1;
+
+ if(is_link) {
+ /* Eat the 2nd "[...]". */
+ closer->end = next_closer->end;
+ }
+ } else {
+ if(closer->end < ctx->size && CH(closer->end) == _T('(')) {
+ /* Might be inline link. */
+ OFF inline_link_end = UINT_MAX;
+
+ is_link = md_is_inline_link_spec(ctx, lines, n_lines, closer->end, &inline_link_end, &attr);
+ if(is_link < 0)
+ return -1;
+
+ /* Check the closing ')' is not inside an already resolved range
+ * (i.e. a range with a higher priority), e.g. a code span. */
+ if(is_link) {
+ int i = closer_index + 1;
+
+ while(i < ctx->n_marks) {
+ MD_MARK* mark = &ctx->marks[i];
+
+ if(mark->beg >= inline_link_end)
+ break;
+ if((mark->flags & (MD_MARK_OPENER | MD_MARK_RESOLVED)) == (MD_MARK_OPENER | MD_MARK_RESOLVED)) {
+ if(ctx->marks[mark->next].beg >= inline_link_end) {
+ /* Cancel the link status. */
+ if(attr.title_needs_free)
+ free(attr.title);
+ is_link = FALSE;
+ break;
+ }
+
+ i = mark->next + 1;
+ } else {
+ i++;
+ }
+ }
+ }
+
+ if(is_link) {
+ /* Eat the "(...)" */
+ closer->end = inline_link_end;
+ }
+ }
+
+ if(!is_link) {
+ /* Might be collapsed reference link. */
+ is_link = md_is_link_reference(ctx, lines, n_lines, opener->beg, closer->end, &attr);
+ if(is_link < 0)
+ return -1;
+ }
+ }
+
+ if(is_link) {
+ /* Resolve the brackets as a link. */
+ opener->flags |= MD_MARK_OPENER | MD_MARK_RESOLVED;
+ closer->flags |= MD_MARK_CLOSER | MD_MARK_RESOLVED;
+
+ /* If it is a link, we store the destination and title in the two
+ * dummy marks after the opener. */
+ MD_ASSERT(ctx->marks[opener_index+1].ch == 'D');
+ ctx->marks[opener_index+1].beg = attr.dest_beg;
+ ctx->marks[opener_index+1].end = attr.dest_end;
+
+ MD_ASSERT(ctx->marks[opener_index+2].ch == 'D');
+ md_mark_store_ptr(ctx, opener_index+2, attr.title);
+ if(attr.title_needs_free)
+ md_mark_chain_append(ctx, &PTR_CHAIN, opener_index+2);
+ ctx->marks[opener_index+2].prev = attr.title_size;
+
+ if(opener->ch == '[') {
+ last_link_beg = opener->beg;
+ last_link_end = closer->end;
+ } else {
+ last_img_beg = opener->beg;
+ last_img_end = closer->end;
+ }
+
+ md_analyze_link_contents(ctx, lines, n_lines, opener_index+1, closer_index);
+ }
+
+ opener_index = next_index;
+ }
+
+ return 0;
+}
+
+/* Analyze whether the mark '&' starts a HTML entity.
+ * If so, update its flags as well as flags of corresponding closer ';'. */
+static void
+md_analyze_entity(MD_CTX* ctx, int mark_index)
+{
+ MD_MARK* opener = &ctx->marks[mark_index];
+ MD_MARK* closer;
+ OFF off;
+
+ /* Cannot be entity if there is no closer as the next mark.
+ * (Any other mark between would mean strange character which cannot be
+ * part of the entity.
+ *
+ * So we can do all the work on '&' and do not call this later for the
+ * closing mark ';'.
+ */
+ if(mark_index + 1 >= ctx->n_marks)
+ return;
+ closer = &ctx->marks[mark_index+1];
+ if(closer->ch != ';')
+ return;
+
+ if(md_is_entity(ctx, opener->beg, closer->end, &off)) {
+ MD_ASSERT(off == closer->end);
+
+ md_resolve_range(ctx, NULL, mark_index, mark_index+1);
+ opener->end = closer->end;
+ }
+}
+
+static void
+md_analyze_table_cell_boundary(MD_CTX* ctx, int mark_index)
+{
+ MD_MARK* mark = &ctx->marks[mark_index];
+ mark->flags |= MD_MARK_RESOLVED;
+
+ md_mark_chain_append(ctx, &TABLECELLBOUNDARIES, mark_index);
+ ctx->n_table_cell_boundaries++;
+}
+
+/* Split a longer mark into two. The new mark takes the given count of
+ * characters. May only be called if an adequate number of dummy 'D' marks
+ * follows.
+ */
+static int
+md_split_emph_mark(MD_CTX* ctx, int mark_index, SZ n)
+{
+ MD_MARK* mark = &ctx->marks[mark_index];
+ int new_mark_index = mark_index + (mark->end - mark->beg - n);
+ MD_MARK* dummy = &ctx->marks[new_mark_index];
+
+ MD_ASSERT(mark->end - mark->beg > n);
+ MD_ASSERT(dummy->ch == 'D');
+
+ memcpy(dummy, mark, sizeof(MD_MARK));
+ mark->end -= n;
+ dummy->beg = mark->end;
+
+ return new_mark_index;
+}
+
+static void
+md_analyze_emph(MD_CTX* ctx, int mark_index)
+{
+ MD_MARK* mark = &ctx->marks[mark_index];
+ MD_MARKCHAIN* chain = md_mark_chain(ctx, mark_index);
+
+ /* If we can be a closer, try to resolve with the preceding opener. */
+ if(mark->flags & MD_MARK_POTENTIAL_CLOSER) {
+ MD_MARK* opener = NULL;
+ int opener_index;
+
+ if(mark->ch == _T('*')) {
+ MD_MARKCHAIN* opener_chains[6];
+ int i, n_opener_chains;
+ unsigned flags = mark->flags;
+
+ /* Apply "rule of three". (This is why we break asterisk opener
+ * marks into multiple chains.) */
+ n_opener_chains = 0;
+ opener_chains[n_opener_chains++] = &ASTERISK_OPENERS_intraword_mod3_0;
+ if((flags & MD_MARK_EMPH_MOD3_MASK) != MD_MARK_EMPH_MOD3_2)
+ opener_chains[n_opener_chains++] = &ASTERISK_OPENERS_intraword_mod3_1;
+ if((flags & MD_MARK_EMPH_MOD3_MASK) != MD_MARK_EMPH_MOD3_1)
+ opener_chains[n_opener_chains++] = &ASTERISK_OPENERS_intraword_mod3_2;
+ opener_chains[n_opener_chains++] = &ASTERISK_OPENERS_extraword_mod3_0;
+ if(!(flags & MD_MARK_EMPH_INTRAWORD) || (flags & MD_MARK_EMPH_MOD3_MASK) != MD_MARK_EMPH_MOD3_2)
+ opener_chains[n_opener_chains++] = &ASTERISK_OPENERS_extraword_mod3_1;
+ if(!(flags & MD_MARK_EMPH_INTRAWORD) || (flags & MD_MARK_EMPH_MOD3_MASK) != MD_MARK_EMPH_MOD3_1)
+ opener_chains[n_opener_chains++] = &ASTERISK_OPENERS_extraword_mod3_2;
+
+ /* Opener is the most recent mark from the allowed chains. */
+ for(i = 0; i < n_opener_chains; i++) {
+ if(opener_chains[i]->tail >= 0) {
+ int tmp_index = opener_chains[i]->tail;
+ MD_MARK* tmp_mark = &ctx->marks[tmp_index];
+ if(opener == NULL || tmp_mark->end > opener->end) {
+ opener_index = tmp_index;
+ opener = tmp_mark;
+ }
+ }
+ }
+ } else {
+ /* Simple emph. mark */
+ if(chain->tail >= 0) {
+ opener_index = chain->tail;
+ opener = &ctx->marks[opener_index];
+ }
+ }
+
+ /* Resolve, if we have found matching opener. */
+ if(opener != NULL) {
+ SZ opener_size = opener->end - opener->beg;
+ SZ closer_size = mark->end - mark->beg;
+
+ if(opener_size > closer_size) {
+ opener_index = md_split_emph_mark(ctx, opener_index, closer_size);
+ md_mark_chain_append(ctx, md_mark_chain(ctx, opener_index), opener_index);
+ } else if(opener_size < closer_size) {
+ md_split_emph_mark(ctx, mark_index, closer_size - opener_size);
+ }
+
+ md_rollback(ctx, opener_index, mark_index, MD_ROLLBACK_CROSSING);
+ md_resolve_range(ctx, chain, opener_index, mark_index);
+ return;
+ }
+ }
+
+ /* If we could not resolve as closer, we may be yet be an opener. */
+ if(mark->flags & MD_MARK_POTENTIAL_OPENER)
+ md_mark_chain_append(ctx, chain, mark_index);
+}
+
+static void
+md_analyze_tilde(MD_CTX* ctx, int mark_index)
+{
+ /* We attempt to be Github Flavored Markdown compatible here. GFM says
+ * that length of the tilde sequence is not important at all. Note that
+ * implies the TILDE_OPENERS chain can have at most one item. */
+
+ if(TILDE_OPENERS.head >= 0) {
+ /* The chain already contains an opener, so we may resolve the span. */
+ int opener_index = TILDE_OPENERS.head;
+
+ md_rollback(ctx, opener_index, mark_index, MD_ROLLBACK_CROSSING);
+ md_resolve_range(ctx, &TILDE_OPENERS, opener_index, mark_index);
+ } else {
+ /* We can only be opener. */
+ md_mark_chain_append(ctx, &TILDE_OPENERS, mark_index);
+ }
+}
+
+static void
+md_analyze_dollar(MD_CTX* ctx, int mark_index)
+{
+ /* This should mimic the way inline equations work in LaTeX, so there
+ * can only ever be one item in the chain (i.e. the dollars can't be
+ * nested). This is basically the same as the md_analyze_tilde function,
+ * except that we require matching openers and closers to be of the same
+ * length.
+ *
+ * E.g.: $abc$$def$$ => abc (display equation) def (end equation) */
+ if(DOLLAR_OPENERS.head >= 0) {
+ /* If the potential closer has a non-matching number of $, discard */
+ MD_MARK* open = &ctx->marks[DOLLAR_OPENERS.head];
+ MD_MARK* close = &ctx->marks[mark_index];
+
+ int opener_index = DOLLAR_OPENERS.head;
+ md_rollback(ctx, opener_index, mark_index, MD_ROLLBACK_ALL);
+ if (open->end - open->beg == close->end - close->beg) {
+ /* We are the matching closer */
+ md_resolve_range(ctx, &DOLLAR_OPENERS, opener_index, mark_index);
+ } else {
+ /* We don't match the opener, so discard old opener and insert as opener */
+ md_mark_chain_append(ctx, &DOLLAR_OPENERS, mark_index);
+ }
+ } else {
+ /* No unmatched openers, so we are opener */
+ md_mark_chain_append(ctx, &DOLLAR_OPENERS, mark_index);
+ }
+}
+
+static void
+md_analyze_permissive_url_autolink(MD_CTX* ctx, int mark_index)
+{
+ MD_MARK* opener = &ctx->marks[mark_index];
+ int closer_index = mark_index + 1;
+ MD_MARK* closer = &ctx->marks[closer_index];
+ MD_MARK* next_resolved_mark;
+ OFF off = opener->end;
+ int n_dots = FALSE;
+ int has_underscore_in_last_seg = FALSE;
+ int has_underscore_in_next_to_last_seg = FALSE;
+ int n_opened_parenthesis = 0;
+
+ /* Check for domain. */
+ while(off < ctx->size) {
+ if(ISALNUM(off) || CH(off) == _T('-')) {
+ off++;
+ } else if(CH(off) == _T('.')) {
+ /* We must see at least one period. */
+ n_dots++;
+ has_underscore_in_next_to_last_seg = has_underscore_in_last_seg;
+ has_underscore_in_last_seg = FALSE;
+ off++;
+ } else if(CH(off) == _T('_')) {
+ /* No underscore may be present in the last two domain segments. */
+ has_underscore_in_last_seg = TRUE;
+ off++;
+ } else {
+ break;
+ }
+ }
+ if(off > opener->end && CH(off-1) == _T('.')) {
+ off--;
+ n_dots--;
+ }
+ if(off <= opener->end || n_dots == 0 || has_underscore_in_next_to_last_seg || has_underscore_in_last_seg)
+ return;
+
+ /* Check for path. */
+ next_resolved_mark = closer + 1;
+ while(next_resolved_mark->ch == 'D' || !(next_resolved_mark->flags & MD_MARK_RESOLVED))
+ next_resolved_mark++;
+ while(off < next_resolved_mark->beg && CH(off) != _T('<') && !ISWHITESPACE(off) && !ISNEWLINE(off)) {
+ /* Parenthesis must be balanced. */
+ if(CH(off) == _T('(')) {
+ n_opened_parenthesis++;
+ } else if(CH(off) == _T(')')) {
+ if(n_opened_parenthesis > 0)
+ n_opened_parenthesis--;
+ else
+ break;
+ }
+
+ off++;
+ }
+ /* These cannot be last char In such case they are more likely normal
+ * punctuation. */
+ if(ISANYOF(off-1, _T("?!.,:*_~")))
+ off--;
+
+ /* Ok. Lets call it auto-link. Adapt opener and create closer to zero
+ * length so all the contents becomes the link text. */
+ MD_ASSERT(closer->ch == 'D');
+ opener->end = opener->beg;
+ closer->ch = opener->ch;
+ closer->beg = off;
+ closer->end = off;
+ md_resolve_range(ctx, NULL, mark_index, closer_index);
+}
+
+/* The permissive autolinks do not have to be enclosed in '<' '>' but we
+ * instead impose stricter rules what is understood as an e-mail address
+ * here. Actually any non-alphanumeric characters with exception of '.'
+ * are prohibited both in username and after '@'. */
+static void
+md_analyze_permissive_email_autolink(MD_CTX* ctx, int mark_index)
+{
+ MD_MARK* opener = &ctx->marks[mark_index];
+ int closer_index;
+ MD_MARK* closer;
+ OFF beg = opener->beg;
+ OFF end = opener->end;
+ int dot_count = 0;
+
+ MD_ASSERT(CH(beg) == _T('@'));
+
+ /* Scan for name before '@'. */
+ while(beg > 0 && (ISALNUM(beg-1) || ISANYOF(beg-1, _T(".-_+"))))
+ beg--;
+
+ /* Scan for domain after '@'. */
+ while(end < ctx->size && (ISALNUM(end) || ISANYOF(end, _T(".-_")))) {
+ if(CH(end) == _T('.'))
+ dot_count++;
+ end++;
+ }
+ if(CH(end-1) == _T('.')) { /* Final '.' not part of it. */
+ dot_count--;
+ end--;
+ }
+ else if(ISANYOF2(end-1, _T('-'), _T('_'))) /* These are forbidden at the end. */
+ return;
+ if(CH(end-1) == _T('@') || dot_count == 0)
+ return;
+
+ /* Ok. Lets call it auto-link. Adapt opener and create closer to zero
+ * length so all the contents becomes the link text. */
+ closer_index = mark_index + 1;
+ closer = &ctx->marks[closer_index];
+ MD_ASSERT(closer->ch == 'D');
+
+ opener->beg = beg;
+ opener->end = beg;
+ closer->ch = opener->ch;
+ closer->beg = end;
+ closer->end = end;
+ md_resolve_range(ctx, NULL, mark_index, closer_index);
+}
+
+static inline void
+md_analyze_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines,
+ int mark_beg, int mark_end, const CHAR* mark_chars)
+{
+ int i = mark_beg;
+
+ while(i < mark_end) {
+ MD_MARK* mark = &ctx->marks[i];
+
+ /* Skip resolved spans. */
+ if(mark->flags & MD_MARK_RESOLVED) {
+ if(mark->flags & MD_MARK_OPENER) {
+ MD_ASSERT(i < mark->next);
+ i = mark->next + 1;
+ } else {
+ i++;
+ }
+ continue;
+ }
+
+ /* Skip marks we do not want to deal with. */
+ if(!ISANYOF_(mark->ch, mark_chars)) {
+ i++;
+ continue;
+ }
+
+ /* Analyze the mark. */
+ switch(mark->ch) {
+ case '[': /* Pass through. */
+ case '!': /* Pass through. */
+ case ']': md_analyze_bracket(ctx, i); break;
+ case '&': md_analyze_entity(ctx, i); break;
+ case '|': md_analyze_table_cell_boundary(ctx, i); break;
+ case '_': /* Pass through. */
+ case '*': md_analyze_emph(ctx, i); break;
+ case '~': md_analyze_tilde(ctx, i); break;
+ case '$': md_analyze_dollar(ctx, i); break;
+ case '.': /* Pass through. */
+ case ':': md_analyze_permissive_url_autolink(ctx, i); break;
+ case '@': md_analyze_permissive_email_autolink(ctx, i); break;
+ }
+
+ i++;
+ }
+}
+
+/* Analyze marks (build ctx->marks). */
+static int
+md_analyze_inlines(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode)
+{
+ int ret;
+
+ /* Reset the previously collected stack of marks. */
+ ctx->n_marks = 0;
+
+ /* Collect all marks. */
+ MD_CHECK(md_collect_marks(ctx, lines, n_lines, table_mode));
+
+ /* We analyze marks in few groups to handle their precedence. */
+ /* (1) Entities; code spans; autolinks; raw HTML. */
+ md_analyze_marks(ctx, lines, n_lines, 0, ctx->n_marks, _T("&"));
+
+ if(table_mode) {
+ /* (2) Analyze table cell boundaries.
+ * Note we reset TABLECELLBOUNDARIES chain prior to the call md_analyze_marks(),
+ * not after, because caller may need it. */
+ MD_ASSERT(n_lines == 1);
+ TABLECELLBOUNDARIES.head = -1;
+ TABLECELLBOUNDARIES.tail = -1;
+ ctx->n_table_cell_boundaries = 0;
+ md_analyze_marks(ctx, lines, n_lines, 0, ctx->n_marks, _T("|"));
+ return ret;
+ }
+
+ /* (3) Links. */
+ md_analyze_marks(ctx, lines, n_lines, 0, ctx->n_marks, _T("[]!"));
+ MD_CHECK(md_resolve_links(ctx, lines, n_lines));
+ BRACKET_OPENERS.head = -1;
+ BRACKET_OPENERS.tail = -1;
+ ctx->unresolved_link_head = -1;
+ ctx->unresolved_link_tail = -1;
+
+ /* (4) Emphasis and strong emphasis; permissive autolinks. */
+ md_analyze_link_contents(ctx, lines, n_lines, 0, ctx->n_marks);
+
+abort:
+ return ret;
+}
+
+static void
+md_analyze_link_contents(MD_CTX* ctx, const MD_LINE* lines, int n_lines,
+ int mark_beg, int mark_end)
+{
+ md_analyze_marks(ctx, lines, n_lines, mark_beg, mark_end, _T("*_~$@:."));
+ ASTERISK_OPENERS_extraword_mod3_0.head = -1;
+ ASTERISK_OPENERS_extraword_mod3_0.tail = -1;
+ ASTERISK_OPENERS_extraword_mod3_1.head = -1;
+ ASTERISK_OPENERS_extraword_mod3_1.tail = -1;
+ ASTERISK_OPENERS_extraword_mod3_2.head = -1;
+ ASTERISK_OPENERS_extraword_mod3_2.tail = -1;
+ ASTERISK_OPENERS_intraword_mod3_0.head = -1;
+ ASTERISK_OPENERS_intraword_mod3_0.tail = -1;
+ ASTERISK_OPENERS_intraword_mod3_1.head = -1;
+ ASTERISK_OPENERS_intraword_mod3_1.tail = -1;
+ ASTERISK_OPENERS_intraword_mod3_2.head = -1;
+ ASTERISK_OPENERS_intraword_mod3_2.tail = -1;
+ UNDERSCORE_OPENERS.head = -1;
+ UNDERSCORE_OPENERS.tail = -1;
+ TILDE_OPENERS.head = -1;
+ TILDE_OPENERS.tail = -1;
+ DOLLAR_OPENERS.head = -1;
+ DOLLAR_OPENERS.tail = -1;
+}
+
+static int
+md_enter_leave_span_a(MD_CTX* ctx, int enter, MD_SPANTYPE type,
+ const CHAR* dest, SZ dest_size, int prohibit_escapes_in_dest,
+ const CHAR* title, SZ title_size)
+{
+ MD_ATTRIBUTE_BUILD href_build = { 0 };
+ MD_ATTRIBUTE_BUILD title_build = { 0 };
+ MD_SPAN_A_DETAIL det;
+ int ret = 0;
+
+ /* Note we here rely on fact that MD_SPAN_A_DETAIL and
+ * MD_SPAN_IMG_DETAIL are binary-compatible. */
+ memset(&det, 0, sizeof(MD_SPAN_A_DETAIL));
+ MD_CHECK(md_build_attribute(ctx, dest, dest_size,
+ (prohibit_escapes_in_dest ? MD_BUILD_ATTR_NO_ESCAPES : 0),
+ &det.href, &href_build));
+ MD_CHECK(md_build_attribute(ctx, title, title_size, 0, &det.title, &title_build));
+
+ if(enter)
+ MD_ENTER_SPAN(type, &det);
+ else
+ MD_LEAVE_SPAN(type, &det);
+
+abort:
+ md_free_attribute(ctx, &href_build);
+ md_free_attribute(ctx, &title_build);
+ return ret;
+}
+
+/* Render the output, accordingly to the analyzed ctx->marks. */
+static int
+md_process_inlines(MD_CTX* ctx, const MD_LINE* lines, int n_lines)
+{
+ MD_TEXTTYPE text_type;
+ const MD_LINE* line = lines;
+ MD_MARK* prev_mark = NULL;
+ MD_MARK* mark;
+ OFF off = lines[0].beg;
+ OFF end = lines[n_lines-1].end;
+ int enforce_hardbreak = 0;
+ int ret = 0;
+
+ /* Find first resolved mark. Note there is always at least one resolved
+ * mark, the dummy last one after the end of the latest line we actually
+ * never really reach. This saves us of a lot of special checks and cases
+ * in this function. */
+ mark = ctx->marks;
+ while(!(mark->flags & MD_MARK_RESOLVED))
+ mark++;
+
+ text_type = MD_TEXT_NORMAL;
+
+ while(1) {
+ /* Process the text up to the next mark or end-of-line. */
+ OFF tmp = (line->end < mark->beg ? line->end : mark->beg);
+ if(tmp > off) {
+ MD_TEXT(text_type, STR(off), tmp - off);
+ off = tmp;
+ }
+
+ /* If reached the mark, process it and move to next one. */
+ if(off >= mark->beg) {
+ switch(mark->ch) {
+ case '\\': /* Backslash escape. */
+ if(ISNEWLINE(mark->beg+1))
+ enforce_hardbreak = 1;
+ else
+ MD_TEXT(text_type, STR(mark->beg+1), 1);
+ break;
+
+ case ' ': /* Non-trivial space. */
+ MD_TEXT(text_type, _T(" "), 1);
+ break;
+
+ case '`': /* Code span. */
+ if(mark->flags & MD_MARK_OPENER) {
+ MD_ENTER_SPAN(MD_SPAN_CODE, NULL);
+ text_type = MD_TEXT_CODE;
+ } else {
+ MD_LEAVE_SPAN(MD_SPAN_CODE, NULL);
+ text_type = MD_TEXT_NORMAL;
+ }
+ break;
+
+ case '_':
+ case '*': /* Emphasis, strong emphasis. */
+ if(mark->flags & MD_MARK_OPENER) {
+ if((mark->end - off) % 2) {
+ MD_ENTER_SPAN(MD_SPAN_EM, NULL);
+ off++;
+ }
+ while(off + 1 < mark->end) {
+ MD_ENTER_SPAN(MD_SPAN_STRONG, NULL);
+ off += 2;
+ }
+ } else {
+ while(off + 1 < mark->end) {
+ MD_LEAVE_SPAN(MD_SPAN_STRONG, NULL);
+ off += 2;
+ }
+ if((mark->end - off) % 2) {
+ MD_LEAVE_SPAN(MD_SPAN_EM, NULL);
+ off++;
+ }
+ }
+ break;
+
+ case '~':
+ if(mark->flags & MD_MARK_OPENER)
+ MD_ENTER_SPAN(MD_SPAN_DEL, NULL);
+ else
+ MD_LEAVE_SPAN(MD_SPAN_DEL, NULL);
+ break;
+
+ case '$':
+ if(mark->flags & MD_MARK_OPENER) {
+ MD_ENTER_SPAN((mark->end - off) % 2 ? MD_SPAN_LATEXMATH : MD_SPAN_LATEXMATH_DISPLAY, NULL);
+ text_type = MD_TEXT_LATEXMATH;
+ } else {
+ MD_LEAVE_SPAN((mark->end - off) % 2 ? MD_SPAN_LATEXMATH : MD_SPAN_LATEXMATH_DISPLAY, NULL);
+ text_type = MD_TEXT_NORMAL;
+ }
+ break;
+
+ case '[': /* Link, image. */
+ case '!':
+ case ']':
+ {
+ const MD_MARK* opener = (mark->ch != ']' ? mark : &ctx->marks[mark->prev]);
+ const MD_MARK* dest_mark = opener+1;
+ const MD_MARK* title_mark = opener+2;
+
+ MD_ASSERT(dest_mark->ch == 'D');
+ MD_ASSERT(title_mark->ch == 'D');
+
+ MD_CHECK(md_enter_leave_span_a(ctx, (mark->ch != ']'),
+ (opener->ch == '!' ? MD_SPAN_IMG : MD_SPAN_A),
+ STR(dest_mark->beg), dest_mark->end - dest_mark->beg, FALSE,
+ md_mark_get_ptr(ctx, title_mark - ctx->marks), title_mark->prev));
+
+ /* link/image closer may span multiple lines. */
+ if(mark->ch == ']') {
+ while(mark->end > line->end)
+ line++;
+ }
+
+ break;
+ }
+
+ case '<':
+ case '>': /* Autolink or raw HTML. */
+ if(!(mark->flags & MD_MARK_AUTOLINK)) {
+ /* Raw HTML. */
+ if(mark->flags & MD_MARK_OPENER)
+ text_type = MD_TEXT_HTML;
+ else
+ text_type = MD_TEXT_NORMAL;
+ break;
+ }
+ /* Pass through, if auto-link. */
+
+ case '@': /* Permissive e-mail autolink. */
+ case ':': /* Permissive URL autolink. */
+ case '.': /* Permissive WWW autolink. */
+ {
+ MD_MARK* opener = ((mark->flags & MD_MARK_OPENER) ? mark : &ctx->marks[mark->prev]);
+ MD_MARK* closer = &ctx->marks[opener->next];
+ const CHAR* dest = STR(opener->end);
+ SZ dest_size = closer->beg - opener->end;
+
+ /* For permissive auto-links we do not know closer mark
+ * position at the time of md_collect_marks(), therefore
+ * it can be out-of-order in ctx->marks[].
+ *
+ * With this flag, we make sure that we output the closer
+ * only if we processed the opener. */
+ if(mark->flags & MD_MARK_OPENER)
+ closer->flags |= MD_MARK_VALIDPERMISSIVEAUTOLINK;
+
+ if(opener->ch == '@' || opener->ch == '.') {
+ dest_size += 7;
+ MD_TEMP_BUFFER(dest_size * sizeof(CHAR));
+ memcpy(ctx->buffer,
+ (opener->ch == '@' ? _T("mailto:") : _T("http://")),
+ 7 * sizeof(CHAR));
+ memcpy(ctx->buffer + 7, dest, (dest_size-7) * sizeof(CHAR));
+ dest = ctx->buffer;
+ }
+
+ if(closer->flags & MD_MARK_VALIDPERMISSIVEAUTOLINK)
+ MD_CHECK(md_enter_leave_span_a(ctx, (mark->flags & MD_MARK_OPENER),
+ MD_SPAN_A, dest, dest_size, TRUE, NULL, 0));
+ break;
+ }
+
+ case '&': /* Entity. */
+ MD_TEXT(MD_TEXT_ENTITY, STR(mark->beg), mark->end - mark->beg);
+ break;
+
+ case '\0':
+ MD_TEXT(MD_TEXT_NULLCHAR, _T(""), 1);
+ break;
+
+ case 127:
+ goto abort;
+ }
+
+ off = mark->end;
+
+ /* Move to next resolved mark. */
+ prev_mark = mark;
+ mark++;
+ while(!(mark->flags & MD_MARK_RESOLVED) || mark->beg < off)
+ mark++;
+ }
+
+ /* If reached end of line, move to next one. */
+ if(off >= line->end) {
+ /* If it is the last line, we are done. */
+ if(off >= end)
+ break;
+
+ if(text_type == MD_TEXT_CODE || text_type == MD_TEXT_LATEXMATH) {
+ OFF tmp;
+
+ MD_ASSERT(prev_mark != NULL);
+ MD_ASSERT(ISANYOF2_(prev_mark->ch, '`', '$') && (prev_mark->flags & MD_MARK_OPENER));
+ MD_ASSERT(ISANYOF2_(mark->ch, '`', '$') && (mark->flags & MD_MARK_CLOSER));
+
+ /* Inside a code span, trailing line whitespace has to be
+ * outputted. */
+ tmp = off;
+ while(off < ctx->size && ISBLANK(off))
+ off++;
+ if(off > tmp)
+ MD_TEXT(text_type, STR(tmp), off-tmp);
+
+ /* and new lines are transformed into single spaces. */
+ if(prev_mark->end < off && off < mark->beg)
+ MD_TEXT(text_type, _T(" "), 1);
+ } else if(text_type == MD_TEXT_HTML) {
+ /* Inside raw HTML, we output the new line verbatim, including
+ * any trailing spaces. */
+ OFF tmp = off;
+
+ while(tmp < end && ISBLANK(tmp))
+ tmp++;
+ if(tmp > off)
+ MD_TEXT(MD_TEXT_HTML, STR(off), tmp - off);
+ MD_TEXT(MD_TEXT_HTML, _T("\n"), 1);
+ } else {
+ /* Output soft or hard line break. */
+ MD_TEXTTYPE break_type = MD_TEXT_SOFTBR;
+
+ if(text_type == MD_TEXT_NORMAL) {
+ if(enforce_hardbreak)
+ break_type = MD_TEXT_BR;
+ else if((CH(line->end) == _T(' ') && CH(line->end+1) == _T(' ')))
+ break_type = MD_TEXT_BR;
+ }
+
+ MD_TEXT(break_type, _T("\n"), 1);
+ }
+
+ /* Move to the next line. */
+ line++;
+ off = line->beg;
+
+ enforce_hardbreak = 0;
+ }
+ }
+
+abort:
+ return ret;
+}
+
+
+/***************************
+ *** Processing Tables ***
+ ***************************/
+
+static void
+md_analyze_table_alignment(MD_CTX* ctx, OFF beg, OFF end, MD_ALIGN* align, int n_align)
+{
+ static const MD_ALIGN align_map[] = { MD_ALIGN_DEFAULT, MD_ALIGN_LEFT, MD_ALIGN_RIGHT, MD_ALIGN_CENTER };
+ OFF off = beg;
+
+ while(n_align > 0) {
+ int index = 0; /* index into align_map[] */
+
+ while(CH(off) != _T('-'))
+ off++;
+ if(off > beg && CH(off-1) == _T(':'))
+ index |= 1;
+ while(off < end && CH(off) == _T('-'))
+ off++;
+ if(off < end && CH(off) == _T(':'))
+ index |= 2;
+
+ *align = align_map[index];
+ align++;
+ n_align--;
+ }
+
+}
+
+/* Forward declaration. */
+static int md_process_normal_block_contents(MD_CTX* ctx, const MD_LINE* lines, int n_lines);
+
+static int
+md_process_table_cell(MD_CTX* ctx, MD_BLOCKTYPE cell_type, MD_ALIGN align, OFF beg, OFF end)
+{
+ MD_LINE line;
+ MD_BLOCK_TD_DETAIL det;
+ int ret = 0;
+
+ while(beg < end && ISWHITESPACE(beg))
+ beg++;
+ while(end > beg && ISWHITESPACE(end-1))
+ end--;
+
+ det.align = align;
+ line.beg = beg;
+ line.end = end;
+
+ MD_ENTER_BLOCK(cell_type, &det);
+ MD_CHECK(md_process_normal_block_contents(ctx, &line, 1));
+ MD_LEAVE_BLOCK(cell_type, &det);
+
+abort:
+ return ret;
+}
+
+static int
+md_process_table_row(MD_CTX* ctx, MD_BLOCKTYPE cell_type, OFF beg, OFF end,
+ const MD_ALIGN* align, int col_count)
+{
+ MD_LINE line;
+ OFF* pipe_offs = NULL;
+ int i, j, n;
+ int ret = 0;
+
+ line.beg = beg;
+ line.end = end;
+
+ /* Break the line into table cells by identifying pipe characters who
+ * form the cell boundary. */
+ MD_CHECK(md_analyze_inlines(ctx, &line, 1, TRUE));
+
+ /* We have to remember the cell boundaries in local buffer because
+ * ctx->marks[] shall be reused during cell contents processing. */
+ n = ctx->n_table_cell_boundaries;
+ pipe_offs = (OFF*) malloc(n * sizeof(OFF));
+ if(pipe_offs == NULL) {
+ MD_LOG("malloc() failed.");
+ ret = -1;
+ goto abort;
+ }
+ for(i = TABLECELLBOUNDARIES.head, j = 0; i >= 0; i = ctx->marks[i].next) {
+ MD_MARK* mark = &ctx->marks[i];
+ pipe_offs[j++] = mark->beg;
+ }
+
+ /* Process cells. */
+ MD_ENTER_BLOCK(MD_BLOCK_TR, NULL);
+ j = 0;
+ if(beg < pipe_offs[0] && j < col_count)
+ MD_CHECK(md_process_table_cell(ctx, cell_type, align[j++], beg, pipe_offs[0]));
+ for(i = 0; i < n-1 && j < col_count; i++)
+ MD_CHECK(md_process_table_cell(ctx, cell_type, align[j++], pipe_offs[i]+1, pipe_offs[i+1]));
+ if(pipe_offs[n-1] < end-1 && j < col_count)
+ MD_CHECK(md_process_table_cell(ctx, cell_type, align[j++], pipe_offs[n-1]+1, end));
+ /* Make sure we call enough table cells even if the current table contains
+ * too few of them. */
+ while(j < col_count)
+ MD_CHECK(md_process_table_cell(ctx, cell_type, align[j++], 0, 0));
+
+ MD_LEAVE_BLOCK(MD_BLOCK_TR, NULL);
+
+abort:
+ free(pipe_offs);
+
+ /* Free any temporary memory blocks stored within some dummy marks. */
+ for(i = PTR_CHAIN.head; i >= 0; i = ctx->marks[i].next)
+ free(md_mark_get_ptr(ctx, i));
+ PTR_CHAIN.head = -1;
+ PTR_CHAIN.tail = -1;
+
+ return ret;
+}
+
+static int
+md_process_table_block_contents(MD_CTX* ctx, int col_count, const MD_LINE* lines, int n_lines)
+{
+ MD_ALIGN* align;
+ int i;
+ int ret = 0;
+
+ /* At least two lines have to be present: The column headers and the line
+ * with the underlines. */
+ MD_ASSERT(n_lines >= 2);
+
+ align = malloc(col_count * sizeof(MD_ALIGN));
+ if(align == NULL) {
+ MD_LOG("malloc() failed.");
+ ret = -1;
+ goto abort;
+ }
+
+ md_analyze_table_alignment(ctx, lines[1].beg, lines[1].end, align, col_count);
+
+ MD_ENTER_BLOCK(MD_BLOCK_THEAD, NULL);
+ MD_CHECK(md_process_table_row(ctx, MD_BLOCK_TH,
+ lines[0].beg, lines[0].end, align, col_count));
+ MD_LEAVE_BLOCK(MD_BLOCK_THEAD, NULL);
+
+ MD_ENTER_BLOCK(MD_BLOCK_TBODY, NULL);
+ for(i = 2; i < n_lines; i++) {
+ MD_CHECK(md_process_table_row(ctx, MD_BLOCK_TD,
+ lines[i].beg, lines[i].end, align, col_count));
+ }
+ MD_LEAVE_BLOCK(MD_BLOCK_TBODY, NULL);
+
+abort:
+ free(align);
+ return ret;
+}
+
+static int
+md_is_table_row(MD_CTX* ctx, OFF beg, OFF* p_end)
+{
+ MD_LINE line;
+ int i;
+ int ret = FALSE;
+
+ line.beg = beg;
+ line.end = beg;
+
+ /* Find end of line. */
+ while(line.end < ctx->size && !ISNEWLINE(line.end))
+ line.end++;
+
+ MD_CHECK(md_analyze_inlines(ctx, &line, 1, TRUE));
+
+ if(TABLECELLBOUNDARIES.head >= 0) {
+ if(p_end != NULL)
+ *p_end = line.end;
+ ret = TRUE;
+ }
+
+abort:
+ /* Free any temporary memory blocks stored within some dummy marks. */
+ for(i = PTR_CHAIN.head; i >= 0; i = ctx->marks[i].next)
+ free(md_mark_get_ptr(ctx, i));
+ PTR_CHAIN.head = -1;
+ PTR_CHAIN.tail = -1;
+
+ return ret;
+}
+
+
+/**************************
+ *** Processing Block ***
+ **************************/
+
+#define MD_BLOCK_CONTAINER_OPENER 0x01
+#define MD_BLOCK_CONTAINER_CLOSER 0x02
+#define MD_BLOCK_CONTAINER (MD_BLOCK_CONTAINER_OPENER | MD_BLOCK_CONTAINER_CLOSER)
+#define MD_BLOCK_LOOSE_LIST 0x04
+#define MD_BLOCK_SETEXT_HEADER 0x08
+
+struct MD_BLOCK_tag {
+ MD_BLOCKTYPE type : 8;
+ unsigned flags : 8;
+
+ /* MD_BLOCK_H: Header level (1 - 6)
+ * MD_BLOCK_CODE: Non-zero if fenced, zero if indented.
+ * MD_BLOCK_LI: Task mark character (0 if not task list item, 'x', 'X' or ' ').
+ * MD_BLOCK_TABLE: Column count (as determined by the table underline).
+ */
+ unsigned data : 16;
+
+ /* Leaf blocks: Count of lines (MD_LINE or MD_VERBATIMLINE) on the block.
+ * MD_BLOCK_LI: Task mark offset in the input doc.
+ * MD_BLOCK_OL: Start item number.
+ */
+ unsigned n_lines;
+};
+
+struct MD_CONTAINER_tag {
+ CHAR ch;
+ unsigned is_loose : 8;
+ unsigned is_task : 8;
+ unsigned start;
+ unsigned mark_indent;
+ unsigned contents_indent;
+ OFF block_byte_off;
+ OFF task_mark_off;
+};
+
+
+static int
+md_process_normal_block_contents(MD_CTX* ctx, const MD_LINE* lines, int n_lines)
+{
+ int i;
+ int ret;
+
+ MD_CHECK(md_analyze_inlines(ctx, lines, n_lines, FALSE));
+ MD_CHECK(md_process_inlines(ctx, lines, n_lines));
+
+abort:
+ /* Free any temporary memory blocks stored within some dummy marks. */
+ for(i = PTR_CHAIN.head; i >= 0; i = ctx->marks[i].next)
+ free(md_mark_get_ptr(ctx, i));
+ PTR_CHAIN.head = -1;
+ PTR_CHAIN.tail = -1;
+
+ return ret;
+}
+
+static int
+md_process_verbatim_block_contents(MD_CTX* ctx, MD_TEXTTYPE text_type, const MD_VERBATIMLINE* lines, int n_lines)
+{
+ static const CHAR indent_chunk_str[] = _T(" ");
+ static const SZ indent_chunk_size = SIZEOF_ARRAY(indent_chunk_str) - 1;
+
+ int i;
+ int ret = 0;
+
+ for(i = 0; i < n_lines; i++) {
+ const MD_VERBATIMLINE* line = &lines[i];
+ int indent = line->indent;
+
+ MD_ASSERT(indent >= 0);
+
+ /* Output code indentation. */
+ while(indent > (int) SIZEOF_ARRAY(indent_chunk_str)) {
+ MD_TEXT(text_type, indent_chunk_str, indent_chunk_size);
+ indent -= SIZEOF_ARRAY(indent_chunk_str);
+ }
+ if(indent > 0)
+ MD_TEXT(text_type, indent_chunk_str, indent);
+
+ /* Output the code line itself. */
+ MD_TEXT_INSECURE(text_type, STR(line->beg), line->end - line->beg);
+
+ /* Enforce end-of-line. */
+ MD_TEXT(text_type, _T("\n"), 1);
+ }
+
+abort:
+ return ret;
+}
+
+static int
+md_process_code_block_contents(MD_CTX* ctx, int is_fenced, const MD_VERBATIMLINE* lines, int n_lines)
+{
+ if(is_fenced) {
+ /* Skip the first line in case of fenced code: It is the fence.
+ * (Only the starting fence is present due to logic in md_analyze_line().) */
+ lines++;
+ n_lines--;
+ } else {
+ /* Ignore blank lines at start/end of indented code block. */
+ while(n_lines > 0 && lines[0].beg == lines[0].end) {
+ lines++;
+ n_lines--;
+ }
+ while(n_lines > 0 && lines[n_lines-1].beg == lines[n_lines-1].end) {
+ n_lines--;
+ }
+ }
+
+ if(n_lines == 0)
+ return 0;
+
+ return md_process_verbatim_block_contents(ctx, MD_TEXT_CODE, lines, n_lines);
+}
+
+static int
+md_setup_fenced_code_detail(MD_CTX* ctx, const MD_BLOCK* block, MD_BLOCK_CODE_DETAIL* det,
+ MD_ATTRIBUTE_BUILD* info_build, MD_ATTRIBUTE_BUILD* lang_build)
+{
+ const MD_VERBATIMLINE* fence_line = (const MD_VERBATIMLINE*)(block + 1);
+ OFF beg = fence_line->beg;
+ OFF end = fence_line->end;
+ OFF lang_end;
+ CHAR fence_ch = CH(fence_line->beg);
+ int ret = 0;
+
+ /* Skip the fence itself. */
+ while(beg < ctx->size && CH(beg) == fence_ch)
+ beg++;
+ /* Trim initial spaces. */
+ while(beg < ctx->size && CH(beg) == _T(' '))
+ beg++;
+
+ /* Trim trailing spaces. */
+ while(end > beg && CH(end-1) == _T(' '))
+ end--;
+
+ /* Build info string attribute. */
+ MD_CHECK(md_build_attribute(ctx, STR(beg), end - beg, 0, &det->info, info_build));
+
+ /* Build info string attribute. */
+ lang_end = beg;
+ while(lang_end < end && !ISWHITESPACE(lang_end))
+ lang_end++;
+ MD_CHECK(md_build_attribute(ctx, STR(beg), lang_end - beg, 0, &det->lang, lang_build));
+
+ det->fence_char = fence_ch;
+
+abort:
+ return ret;
+}
+
+static int
+md_process_leaf_block(MD_CTX* ctx, const MD_BLOCK* block)
+{
+ union {
+ MD_BLOCK_H_DETAIL header;
+ MD_BLOCK_CODE_DETAIL code;
+ } det;
+ MD_ATTRIBUTE_BUILD info_build;
+ MD_ATTRIBUTE_BUILD lang_build;
+ int is_in_tight_list;
+ int clean_fence_code_detail = FALSE;
+ int ret = 0;
+
+ memset(&det, 0, sizeof(det));
+
+ if(ctx->n_containers == 0)
+ is_in_tight_list = FALSE;
+ else
+ is_in_tight_list = !ctx->containers[ctx->n_containers-1].is_loose;
+
+ switch(block->type) {
+ case MD_BLOCK_H:
+ det.header.level = block->data;
+ break;
+
+ case MD_BLOCK_CODE:
+ /* For fenced code block, we may need to set the info string. */
+ if(block->data != 0) {
+ memset(&det.code, 0, sizeof(MD_BLOCK_CODE_DETAIL));
+ clean_fence_code_detail = TRUE;
+ MD_CHECK(md_setup_fenced_code_detail(ctx, block, &det.code, &info_build, &lang_build));
+ }
+ break;
+
+ default:
+ /* Noop. */
+ break;
+ }
+
+ if(!is_in_tight_list || block->type != MD_BLOCK_P)
+ MD_ENTER_BLOCK(block->type, (void*) &det);
+
+ /* Process the block contents accordingly to is type. */
+ switch(block->type) {
+ case MD_BLOCK_HR:
+ /* noop */
+ break;
+
+ case MD_BLOCK_CODE:
+ MD_CHECK(md_process_code_block_contents(ctx, (block->data != 0),
+ (const MD_VERBATIMLINE*)(block + 1), block->n_lines));
+ break;
+
+ case MD_BLOCK_HTML:
+ MD_CHECK(md_process_verbatim_block_contents(ctx, MD_TEXT_HTML,
+ (const MD_VERBATIMLINE*)(block + 1), block->n_lines));
+ break;
+
+ case MD_BLOCK_TABLE:
+ MD_CHECK(md_process_table_block_contents(ctx, block->data,
+ (const MD_LINE*)(block + 1), block->n_lines));
+ break;
+
+ default:
+ MD_CHECK(md_process_normal_block_contents(ctx,
+ (const MD_LINE*)(block + 1), block->n_lines));
+ break;
+ }
+
+ if(!is_in_tight_list || block->type != MD_BLOCK_P)
+ MD_LEAVE_BLOCK(block->type, (void*) &det);
+
+abort:
+ if(clean_fence_code_detail) {
+ md_free_attribute(ctx, &info_build);
+ md_free_attribute(ctx, &lang_build);
+ }
+ return ret;
+}
+
+static int
+md_process_all_blocks(MD_CTX* ctx)
+{
+ int byte_off = 0;
+ int ret = 0;
+
+ /* ctx->containers now is not needed for detection of lists and list items
+ * so we reuse it for tracking what lists are loose or tight. We rely
+ * on the fact the vector is large enough to hold the deepest nesting
+ * level of lists. */
+ ctx->n_containers = 0;
+
+ while(byte_off < ctx->n_block_bytes) {
+ MD_BLOCK* block = (MD_BLOCK*)((char*)ctx->block_bytes + byte_off);
+ union {
+ MD_BLOCK_UL_DETAIL ul;
+ MD_BLOCK_OL_DETAIL ol;
+ MD_BLOCK_LI_DETAIL li;
+ } det;
+
+ switch(block->type) {
+ case MD_BLOCK_UL:
+ det.ul.is_tight = (block->flags & MD_BLOCK_LOOSE_LIST) ? FALSE : TRUE;
+ det.ul.mark = (CHAR) block->data;
+ break;
+
+ case MD_BLOCK_OL:
+ det.ol.start = block->n_lines;
+ det.ol.is_tight = (block->flags & MD_BLOCK_LOOSE_LIST) ? FALSE : TRUE;
+ det.ol.mark_delimiter = (CHAR) block->data;
+ break;
+
+ case MD_BLOCK_LI:
+ det.li.is_task = (block->data != 0);
+ det.li.task_mark = (CHAR) block->data;
+ det.li.task_mark_offset = (OFF) block->n_lines;
+ break;
+
+ default:
+ /* noop */
+ break;
+ }
+
+ if(block->flags & MD_BLOCK_CONTAINER) {
+ if(block->flags & MD_BLOCK_CONTAINER_CLOSER) {
+ MD_LEAVE_BLOCK(block->type, &det);
+
+ if(block->type == MD_BLOCK_UL || block->type == MD_BLOCK_OL || block->type == MD_BLOCK_QUOTE)
+ ctx->n_containers--;
+ }
+
+ if(block->flags & MD_BLOCK_CONTAINER_OPENER) {
+ MD_ENTER_BLOCK(block->type, &det);
+
+ if(block->type == MD_BLOCK_UL || block->type == MD_BLOCK_OL) {
+ ctx->containers[ctx->n_containers].is_loose = (block->flags & MD_BLOCK_LOOSE_LIST);
+ ctx->n_containers++;
+ } else if(block->type == MD_BLOCK_QUOTE) {
+ /* This causes that any text in a block quote, even if
+ * nested inside a tight list item, is wrapped with
+ * <p>...</p>. */
+ ctx->containers[ctx->n_containers].is_loose = TRUE;
+ ctx->n_containers++;
+ }
+ }
+ } else {
+ MD_CHECK(md_process_leaf_block(ctx, block));
+
+ if(block->type == MD_BLOCK_CODE || block->type == MD_BLOCK_HTML)
+ byte_off += block->n_lines * sizeof(MD_VERBATIMLINE);
+ else
+ byte_off += block->n_lines * sizeof(MD_LINE);
+ }
+
+ byte_off += sizeof(MD_BLOCK);
+ }
+
+ ctx->n_block_bytes = 0;
+
+abort:
+ return ret;
+}
+
+
+/************************************
+ *** Grouping Lines into Blocks ***
+ ************************************/
+
+static void*
+md_push_block_bytes(MD_CTX* ctx, int n_bytes)
+{
+ void* ptr;
+
+ if(ctx->n_block_bytes + n_bytes > ctx->alloc_block_bytes) {
+ void* new_block_bytes;
+
+ ctx->alloc_block_bytes = (ctx->alloc_block_bytes > 0 ? ctx->alloc_block_bytes * 2 : 512);
+ new_block_bytes = realloc(ctx->block_bytes, ctx->alloc_block_bytes);
+ if(new_block_bytes == NULL) {
+ MD_LOG("realloc() failed.");
+ return NULL;
+ }
+
+ /* Fix the ->current_block after the reallocation. */
+ if(ctx->current_block != NULL) {
+ OFF off_current_block = (char*) ctx->current_block - (char*) ctx->block_bytes;
+ ctx->current_block = (MD_BLOCK*) ((char*) new_block_bytes + off_current_block);
+ }
+
+ ctx->block_bytes = new_block_bytes;
+ }
+
+ ptr = (char*)ctx->block_bytes + ctx->n_block_bytes;
+ ctx->n_block_bytes += n_bytes;
+ return ptr;
+}
+
+static int
+md_start_new_block(MD_CTX* ctx, const MD_LINE_ANALYSIS* line)
+{
+ MD_BLOCK* block;
+
+ MD_ASSERT(ctx->current_block == NULL);
+
+ block = (MD_BLOCK*) md_push_block_bytes(ctx, sizeof(MD_BLOCK));
+ if(block == NULL)
+ return -1;
+
+ switch(line->type) {
+ case MD_LINE_HR:
+ block->type = MD_BLOCK_HR;
+ break;
+
+ case MD_LINE_ATXHEADER:
+ case MD_LINE_SETEXTHEADER:
+ block->type = MD_BLOCK_H;
+ break;
+
+ case MD_LINE_FENCEDCODE:
+ case MD_LINE_INDENTEDCODE:
+ block->type = MD_BLOCK_CODE;
+ break;
+
+ case MD_LINE_TEXT:
+ block->type = MD_BLOCK_P;
+ break;
+
+ case MD_LINE_HTML:
+ block->type = MD_BLOCK_HTML;
+ break;
+
+ case MD_LINE_BLANK:
+ case MD_LINE_SETEXTUNDERLINE:
+ case MD_LINE_TABLEUNDERLINE:
+ default:
+ MD_UNREACHABLE();
+ break;
+ }
+
+ block->flags = 0;
+ block->data = line->data;
+ block->n_lines = 0;
+
+ ctx->current_block = block;
+ return 0;
+}
+
+/* Eat from start of current (textual) block any reference definitions and
+ * remember them so we can resolve any links referring to them.
+ *
+ * (Reference definitions can only be at start of it as they cannot break
+ * a paragraph.)
+ */
+static int
+md_consume_link_reference_definitions(MD_CTX* ctx)
+{
+ MD_LINE* lines = (MD_LINE*) (ctx->current_block + 1);
+ int n_lines = ctx->current_block->n_lines;
+ int n = 0;
+
+ /* Compute how many lines at the start of the block form one or more
+ * reference definitions. */
+ while(n < n_lines) {
+ int n_link_ref_lines;
+
+ n_link_ref_lines = md_is_link_reference_definition(ctx,
+ lines + n, n_lines - n);
+ /* Not a reference definition? */
+ if(n_link_ref_lines == 0)
+ break;
+
+ /* We fail if it is the ref. def. but it could not be stored due
+ * a memory allocation error. */
+ if(n_link_ref_lines < 0)
+ return -1;
+
+ n += n_link_ref_lines;
+ }
+
+ /* If there was at least one reference definition, we need to remove
+ * its lines from the block, or perhaps even the whole block. */
+ if(n > 0) {
+ if(n == n_lines) {
+ /* Remove complete block. */
+ ctx->n_block_bytes -= n * sizeof(MD_LINE);
+ ctx->n_block_bytes -= sizeof(MD_BLOCK);
+ ctx->current_block = NULL;
+ } else {
+ /* Remove just some initial lines from the block. */
+ memmove(lines, lines + n, (n_lines - n) * sizeof(MD_LINE));
+ ctx->current_block->n_lines -= n;
+ ctx->n_block_bytes -= n * sizeof(MD_LINE);
+ }
+ }
+
+ return 0;
+}
+
+static int
+md_end_current_block(MD_CTX* ctx)
+{
+ int ret = 0;
+
+ if(ctx->current_block == NULL)
+ return ret;
+
+ /* Check whether there is a reference definition. (We do this here instead
+ * of in md_analyze_line() because reference definition can take multiple
+ * lines.) */
+ if(ctx->current_block->type == MD_BLOCK_P ||
+ (ctx->current_block->type == MD_BLOCK_H && (ctx->current_block->flags & MD_BLOCK_SETEXT_HEADER)))
+ {
+ MD_LINE* lines = (MD_LINE*) (ctx->current_block + 1);
+ if(CH(lines[0].beg) == _T('[')) {
+ MD_CHECK(md_consume_link_reference_definitions(ctx));
+ if(ctx->current_block == NULL)
+ return ret;
+ }
+ }
+
+ if(ctx->current_block->type == MD_BLOCK_H && (ctx->current_block->flags & MD_BLOCK_SETEXT_HEADER)) {
+ int n_lines = ctx->current_block->n_lines;
+
+ if(n_lines > 1) {
+ /* Get rid of the underline. */
+ ctx->current_block->n_lines--;
+ ctx->n_block_bytes -= sizeof(MD_LINE);
+ } else {
+ /* Only the underline has left after eating the ref. defs.
+ * Keep the line as beginning of a new ordinary paragraph. */
+ ctx->current_block->type = MD_BLOCK_P;
+ return 0;
+ }
+ }
+
+ /* Mark we are not building any block anymore. */
+ ctx->current_block = NULL;
+
+abort:
+ return ret;
+}
+
+static int
+md_add_line_into_current_block(MD_CTX* ctx, const MD_LINE_ANALYSIS* analysis)
+{
+ MD_ASSERT(ctx->current_block != NULL);
+
+ if(ctx->current_block->type == MD_BLOCK_CODE || ctx->current_block->type == MD_BLOCK_HTML) {
+ MD_VERBATIMLINE* line;
+
+ line = (MD_VERBATIMLINE*) md_push_block_bytes(ctx, sizeof(MD_VERBATIMLINE));
+ if(line == NULL)
+ return -1;
+
+ line->indent = analysis->indent;
+ line->beg = analysis->beg;
+ line->end = analysis->end;
+ } else {
+ MD_LINE* line;
+
+ line = (MD_LINE*) md_push_block_bytes(ctx, sizeof(MD_LINE));
+ if(line == NULL)
+ return -1;
+
+ line->beg = analysis->beg;
+ line->end = analysis->end;
+ }
+ ctx->current_block->n_lines++;
+
+ return 0;
+}
+
+static int
+md_push_container_bytes(MD_CTX* ctx, MD_BLOCKTYPE type, unsigned start,
+ unsigned data, unsigned flags)
+{
+ MD_BLOCK* block;
+ int ret = 0;
+
+ MD_CHECK(md_end_current_block(ctx));
+
+ block = (MD_BLOCK*) md_push_block_bytes(ctx, sizeof(MD_BLOCK));
+ if(block == NULL)
+ return -1;
+
+ block->type = type;
+ block->flags = flags;
+ block->data = data;
+ block->n_lines = start;
+
+abort:
+ return ret;
+}
+
+
+
+/***********************
+ *** Line Analysis ***
+ ***********************/
+
+static int
+md_is_hr_line(MD_CTX* ctx, OFF beg, OFF* p_end, OFF* p_killer)
+{
+ OFF off = beg + 1;
+ int n = 1;
+
+ while(off < ctx->size && (CH(off) == CH(beg) || CH(off) == _T(' ') || CH(off) == _T('\t'))) {
+ if(CH(off) == CH(beg))
+ n++;
+ off++;
+ }
+
+ if(n < 3) {
+ *p_killer = off;
+ return FALSE;
+ }
+
+ /* Nothing else can be present on the line. */
+ if(off < ctx->size && !ISNEWLINE(off)) {
+ *p_killer = off;
+ return FALSE;
+ }
+
+ *p_end = off;
+ return TRUE;
+}
+
+static int
+md_is_atxheader_line(MD_CTX* ctx, OFF beg, OFF* p_beg, OFF* p_end, unsigned* p_level)
+{
+ int n;
+ OFF off = beg + 1;
+
+ while(off < ctx->size && CH(off) == _T('#') && off - beg < 7)
+ off++;
+ n = off - beg;
+
+ if(n > 6)
+ return FALSE;
+ *p_level = n;
+
+ if(!(ctx->parser.flags & MD_FLAG_PERMISSIVEATXHEADERS) && off < ctx->size &&
+ CH(off) != _T(' ') && CH(off) != _T('\t') && !ISNEWLINE(off))
+ return FALSE;
+
+ while(off < ctx->size && CH(off) == _T(' '))
+ off++;
+ *p_beg = off;
+ *p_end = off;
+ return TRUE;
+}
+
+static int
+md_is_setext_underline(MD_CTX* ctx, OFF beg, OFF* p_end, unsigned* p_level)
+{
+ OFF off = beg + 1;
+
+ while(off < ctx->size && CH(off) == CH(beg))
+ off++;
+
+ /* Optionally, space(s) can follow. */
+ while(off < ctx->size && CH(off) == _T(' '))
+ off++;
+
+ /* But nothing more is allowed on the line. */
+ if(off < ctx->size && !ISNEWLINE(off))
+ return FALSE;
+
+ *p_level = (CH(beg) == _T('=') ? 1 : 2);
+ *p_end = off;
+ return TRUE;
+}
+
+static int
+md_is_table_underline(MD_CTX* ctx, OFF beg, OFF* p_end, unsigned* p_col_count)
+{
+ OFF off = beg;
+ int found_pipe = FALSE;
+ unsigned col_count = 0;
+
+ if(off < ctx->size && CH(off) == _T('|')) {
+ found_pipe = TRUE;
+ off++;
+ while(off < ctx->size && ISWHITESPACE(off))
+ off++;
+ }
+
+ while(1) {
+ OFF cell_beg;
+ int delimited = FALSE;
+
+ /* Cell underline ("-----", ":----", "----:" or ":----:") */
+ cell_beg = off;
+ if(off < ctx->size && CH(off) == _T(':'))
+ off++;
+ while(off < ctx->size && CH(off) == _T('-'))
+ off++;
+ if(off < ctx->size && CH(off) == _T(':'))
+ off++;
+ if(off - cell_beg < 3)
+ return FALSE;
+
+ col_count++;
+
+ /* Pipe delimiter (optional at the end of line). */
+ while(off < ctx->size && ISWHITESPACE(off))
+ off++;
+ if(off < ctx->size && CH(off) == _T('|')) {
+ delimited = TRUE;
+ found_pipe = TRUE;
+ off++;
+ while(off < ctx->size && ISWHITESPACE(off))
+ off++;
+ }
+
+ /* Success, if we reach end of line. */
+ if(off >= ctx->size || ISNEWLINE(off))
+ break;
+
+ if(!delimited)
+ return FALSE;
+ }
+
+ if(!found_pipe)
+ return FALSE;
+
+ *p_end = off;
+ *p_col_count = col_count;
+ return TRUE;
+}
+
+static int
+md_is_opening_code_fence(MD_CTX* ctx, OFF beg, OFF* p_end)
+{
+ OFF off = beg;
+
+ while(off < ctx->size && CH(off) == CH(beg))
+ off++;
+
+ /* Fence must have at least three characters. */
+ if(off - beg < 3)
+ return FALSE;
+
+ ctx->code_fence_length = off - beg;
+
+ /* Optionally, space(s) can follow. */
+ while(off < ctx->size && CH(off) == _T(' '))
+ off++;
+
+ /* Optionally, an info string can follow. */
+ while(off < ctx->size && !ISNEWLINE(off)) {
+ /* Backtick-based fence must not contain '`' in the info string. */
+ if(CH(beg) == _T('`') && CH(off) == _T('`'))
+ return FALSE;
+ off++;
+ }
+
+ *p_end = off;
+ return TRUE;
+}
+
+static int
+md_is_closing_code_fence(MD_CTX* ctx, CHAR ch, OFF beg, OFF* p_end)
+{
+ OFF off = beg;
+ int ret = FALSE;
+
+ /* Closing fence must have at least the same length and use same char as
+ * opening one. */
+ while(off < ctx->size && CH(off) == ch)
+ off++;
+ if(off - beg < ctx->code_fence_length)
+ goto out;
+
+ /* Optionally, space(s) can follow */
+ while(off < ctx->size && CH(off) == _T(' '))
+ off++;
+
+ /* But nothing more is allowed on the line. */
+ if(off < ctx->size && !ISNEWLINE(off))
+ goto out;
+
+ ret = TRUE;
+
+out:
+ /* Note we set *p_end even on failure: If we are not closing fence, caller
+ * would eat the line anyway without any parsing. */
+ *p_end = off;
+ return ret;
+}
+
+/* Returns type of the raw HTML block, or FALSE if it is not HTML block.
+ * (Refer to CommonMark specification for details about the types.)
+ */
+static int
+md_is_html_block_start_condition(MD_CTX* ctx, OFF beg)
+{
+ typedef struct TAG_tag TAG;
+ struct TAG_tag {
+ const CHAR* name;
+ unsigned len : 8;
+ };
+
+ /* Type 6 is started by a long list of allowed tags. We use two-level
+ * tree to speed-up the search. */
+#ifdef X
+ #undef X
+#endif
+#define X(name) { _T(name), sizeof(name)-1 }
+#define Xend { NULL, 0 }
+ static const TAG t1[] = { X("script"), X("pre"), X("style"), Xend };
+
+ static const TAG a6[] = { X("address"), X("article"), X("aside"), Xend };
+ static const TAG b6[] = { X("base"), X("basefont"), X("blockquote"), X("body"), Xend };
+ static const TAG c6[] = { X("caption"), X("center"), X("col"), X("colgroup"), Xend };
+ static const TAG d6[] = { X("dd"), X("details"), X("dialog"), X("dir"),
+ X("div"), X("dl"), X("dt"), Xend };
+ static const TAG f6[] = { X("fieldset"), X("figcaption"), X("figure"), X("footer"),
+ X("form"), X("frame"), X("frameset"), Xend };
+ static const TAG h6[] = { X("h1"), X("head"), X("header"), X("hr"), X("html"), Xend };
+ static const TAG i6[] = { X("iframe"), Xend };
+ static const TAG l6[] = { X("legend"), X("li"), X("link"), Xend };
+ static const TAG m6[] = { X("main"), X("menu"), X("menuitem"), Xend };
+ static const TAG n6[] = { X("nav"), X("noframes"), Xend };
+ static const TAG o6[] = { X("ol"), X("optgroup"), X("option"), Xend };
+ static const TAG p6[] = { X("p"), X("param"), Xend };
+ static const TAG s6[] = { X("section"), X("source"), X("summary"), Xend };
+ static const TAG t6[] = { X("table"), X("tbody"), X("td"), X("tfoot"), X("th"),
+ X("thead"), X("title"), X("tr"), X("track"), Xend };
+ static const TAG u6[] = { X("ul"), Xend };
+ static const TAG xx[] = { Xend };
+#undef X
+
+ static const TAG* map6[26] = {
+ a6, b6, c6, d6, xx, f6, xx, h6, i6, xx, xx, l6, m6,
+ n6, o6, p6, xx, xx, s6, t6, u6, xx, xx, xx, xx, xx
+ };
+ OFF off = beg + 1;
+ int i;
+
+ /* Check for type 1: <script, <pre, or <style */
+ for(i = 0; t1[i].name != NULL; i++) {
+ if(off + t1[i].len <= ctx->size) {
+ if(md_ascii_case_eq(STR(off), t1[i].name, t1[i].len))
+ return 1;
+ }
+ }
+
+ /* Check for type 2: <!-- */
+ if(off + 3 < ctx->size && CH(off) == _T('!') && CH(off+1) == _T('-') && CH(off+2) == _T('-'))
+ return 2;
+
+ /* Check for type 3: <? */
+ if(off < ctx->size && CH(off) == _T('?'))
+ return 3;
+
+ /* Check for type 4 or 5: <! */
+ if(off < ctx->size && CH(off) == _T('!')) {
+ /* Check for type 4: <! followed by uppercase letter. */
+ if(off + 1 < ctx->size && ISUPPER(off+1))
+ return 4;
+
+ /* Check for type 5: <![CDATA[ */
+ if(off + 8 < ctx->size) {
+ if(md_ascii_eq(STR(off), _T("![CDATA["), 8 * sizeof(CHAR)))
+ return 5;
+ }
+ }
+
+ /* Check for type 6: Many possible starting tags listed above. */
+ if(off + 1 < ctx->size && (ISALPHA(off) || (CH(off) == _T('/') && ISALPHA(off+1)))) {
+ int slot;
+ const TAG* tags;
+
+ if(CH(off) == _T('/'))
+ off++;
+
+ slot = (ISUPPER(off) ? CH(off) - 'A' : CH(off) - 'a');
+ tags = map6[slot];
+
+ for(i = 0; tags[i].name != NULL; i++) {
+ if(off + tags[i].len <= ctx->size) {
+ if(md_ascii_case_eq(STR(off), tags[i].name, tags[i].len)) {
+ OFF tmp = off + tags[i].len;
+ if(tmp >= ctx->size)
+ return 6;
+ if(ISBLANK(tmp) || ISNEWLINE(tmp) || CH(tmp) == _T('>'))
+ return 6;
+ if(tmp+1 < ctx->size && CH(tmp) == _T('/') && CH(tmp+1) == _T('>'))
+ return 6;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Check for type 7: any COMPLETE other opening or closing tag. */
+ if(off + 1 < ctx->size) {
+ OFF end;
+
+ if(md_is_html_tag(ctx, NULL, 0, beg, ctx->size, &end)) {
+ /* Only optional whitespace and new line may follow. */
+ while(end < ctx->size && ISWHITESPACE(end))
+ end++;
+ if(end >= ctx->size || ISNEWLINE(end))
+ return 7;
+ }
+ }
+
+ return FALSE;
+}
+
+/* Case sensitive check whether there is a substring 'what' between 'beg'
+ * and end of line. */
+static int
+md_line_contains(MD_CTX* ctx, OFF beg, const CHAR* what, SZ what_len, OFF* p_end)
+{
+ OFF i;
+ for(i = beg; i + what_len < ctx->size; i++) {
+ if(ISNEWLINE(i))
+ break;
+ if(memcmp(STR(i), what, what_len * sizeof(CHAR)) == 0) {
+ *p_end = i + what_len;
+ return TRUE;
+ }
+ }
+
+ *p_end = i;
+ return FALSE;
+}
+
+/* Returns type of HTML block end condition or FALSE if not an end condition.
+ *
+ * Note it fills p_end even when it is not end condition as the caller
+ * does not need to analyze contents of a raw HTML block.
+ */
+static int
+md_is_html_block_end_condition(MD_CTX* ctx, OFF beg, OFF* p_end)
+{
+ switch(ctx->html_block_type) {
+ case 1:
+ {
+ OFF off = beg;
+
+ while(off < ctx->size && !ISNEWLINE(off)) {
+ if(CH(off) == _T('<')) {
+ if(md_ascii_case_eq(STR(off), _T("</script>"), 9)) {
+ *p_end = off + 9;
+ return TRUE;
+ }
+
+ if(md_ascii_case_eq(STR(off), _T("</style>"), 8)) {
+ *p_end = off + 8;
+ return TRUE;
+ }
+
+ if(md_ascii_case_eq(STR(off), _T("</pre>"), 6)) {
+ *p_end = off + 6;
+ return TRUE;
+ }
+ }
+
+ off++;
+ }
+ *p_end = off;
+ return FALSE;
+ }
+
+ case 2:
+ return (md_line_contains(ctx, beg, _T("-->"), 3, p_end) ? 2 : FALSE);
+
+ case 3:
+ return (md_line_contains(ctx, beg, _T("?>"), 2, p_end) ? 3 : FALSE);
+
+ case 4:
+ return (md_line_contains(ctx, beg, _T(">"), 1, p_end) ? 4 : FALSE);
+
+ case 5:
+ return (md_line_contains(ctx, beg, _T("]]>"), 3, p_end) ? 5 : FALSE);
+
+ case 6: /* Pass through */
+ case 7:
+ *p_end = beg;
+ return (ISNEWLINE(beg) ? ctx->html_block_type : FALSE);
+
+ default:
+ MD_UNREACHABLE();
+ }
+ return FALSE;
+}
+
+
+static int
+md_is_container_compatible(const MD_CONTAINER* pivot, const MD_CONTAINER* container)
+{
+ /* Block quote has no "items" like lists. */
+ if(container->ch == _T('>'))
+ return FALSE;
+
+ if(container->ch != pivot->ch)
+ return FALSE;
+ if(container->mark_indent > pivot->contents_indent)
+ return FALSE;
+
+ return TRUE;
+}
+
+static int
+md_push_container(MD_CTX* ctx, const MD_CONTAINER* container)
+{
+ if(ctx->n_containers >= ctx->alloc_containers) {
+ MD_CONTAINER* new_containers;
+
+ ctx->alloc_containers = (ctx->alloc_containers > 0 ? ctx->alloc_containers * 2 : 16);
+ new_containers = realloc(ctx->containers, ctx->alloc_containers * sizeof(MD_CONTAINER));
+ if(new_containers == NULL) {
+ MD_LOG("realloc() failed.");
+ return -1;
+ }
+
+ ctx->containers = new_containers;
+ }
+
+ memcpy(&ctx->containers[ctx->n_containers++], container, sizeof(MD_CONTAINER));
+ return 0;
+}
+
+static int
+md_enter_child_containers(MD_CTX* ctx, int n_children, unsigned data)
+{
+ int i;
+ int ret = 0;
+
+ for(i = ctx->n_containers - n_children; i < ctx->n_containers; i++) {
+ MD_CONTAINER* c = &ctx->containers[i];
+ int is_ordered_list = FALSE;
+
+ switch(c->ch) {
+ case _T(')'):
+ case _T('.'):
+ is_ordered_list = TRUE;
+ /* Pass through */
+
+ case _T('-'):
+ case _T('+'):
+ case _T('*'):
+ /* Remember offset in ctx->block_bytes so we can revisit the
+ * block if we detect it is a loose list. */
+ md_end_current_block(ctx);
+ c->block_byte_off = ctx->n_block_bytes;
+
+ MD_CHECK(md_push_container_bytes(ctx,
+ (is_ordered_list ? MD_BLOCK_OL : MD_BLOCK_UL),
+ c->start, data, MD_BLOCK_CONTAINER_OPENER));
+ MD_CHECK(md_push_container_bytes(ctx, MD_BLOCK_LI,
+ c->task_mark_off,
+ (c->is_task ? CH(c->task_mark_off) : 0),
+ MD_BLOCK_CONTAINER_OPENER));
+ break;
+
+ case _T('>'):
+ MD_CHECK(md_push_container_bytes(ctx, MD_BLOCK_QUOTE, 0, 0, MD_BLOCK_CONTAINER_OPENER));
+ break;
+
+ default:
+ MD_UNREACHABLE();
+ break;
+ }
+ }
+
+abort:
+ return ret;
+}
+
+static int
+md_leave_child_containers(MD_CTX* ctx, int n_keep)
+{
+ int ret = 0;
+
+ while(ctx->n_containers > n_keep) {
+ MD_CONTAINER* c = &ctx->containers[ctx->n_containers-1];
+ int is_ordered_list = FALSE;
+
+ switch(c->ch) {
+ case _T(')'):
+ case _T('.'):
+ is_ordered_list = TRUE;
+ /* Pass through */
+
+ case _T('-'):
+ case _T('+'):
+ case _T('*'):
+ MD_CHECK(md_push_container_bytes(ctx, MD_BLOCK_LI,
+ c->task_mark_off, (c->is_task ? CH(c->task_mark_off) : 0),
+ MD_BLOCK_CONTAINER_CLOSER));
+ MD_CHECK(md_push_container_bytes(ctx,
+ (is_ordered_list ? MD_BLOCK_OL : MD_BLOCK_UL), 0,
+ c->ch, MD_BLOCK_CONTAINER_CLOSER));
+ break;
+
+ case _T('>'):
+ MD_CHECK(md_push_container_bytes(ctx, MD_BLOCK_QUOTE, 0,
+ 0, MD_BLOCK_CONTAINER_CLOSER));
+ break;
+
+ default:
+ MD_UNREACHABLE();
+ break;
+ }
+
+ ctx->n_containers--;
+ }
+
+abort:
+ return ret;
+}
+
+static int
+md_is_container_mark(MD_CTX* ctx, unsigned indent, OFF beg, OFF* p_end, MD_CONTAINER* p_container)
+{
+ OFF off = beg;
+ OFF max_end;
+
+ if(indent >= ctx->code_indent_offset)
+ return FALSE;
+
+ /* Check for block quote mark. */
+ if(off < ctx->size && CH(off) == _T('>')) {
+ off++;
+ p_container->ch = _T('>');
+ p_container->is_loose = FALSE;
+ p_container->is_task = FALSE;
+ p_container->mark_indent = indent;
+ p_container->contents_indent = indent + 1;
+ *p_end = off;
+ return TRUE;
+ }
+
+ /* Check for list item bullet mark. */
+ if(off+1 < ctx->size && ISANYOF(off, _T("-+*")) && (ISBLANK(off+1) || ISNEWLINE(off+1))) {
+ p_container->ch = CH(off);
+ p_container->is_loose = FALSE;
+ p_container->is_task = FALSE;
+ p_container->mark_indent = indent;
+ p_container->contents_indent = indent + 1;
+ *p_end = off + 1;
+ return TRUE;
+ }
+
+ /* Check for ordered list item marks. */
+ max_end = off + 9;
+ if(max_end > ctx->size)
+ max_end = ctx->size;
+ p_container->start = 0;
+ while(off < max_end && ISDIGIT(off)) {
+ p_container->start = p_container->start * 10 + CH(off) - _T('0');
+ off++;
+ }
+ if(off > beg && off+1 < ctx->size &&
+ (CH(off) == _T('.') || CH(off) == _T(')')) &&
+ (ISBLANK(off+1) || ISNEWLINE(off+1)))
+ {
+ p_container->ch = CH(off);
+ p_container->is_loose = FALSE;
+ p_container->is_task = FALSE;
+ p_container->mark_indent = indent;
+ p_container->contents_indent = indent + off - beg + 1;
+ *p_end = off + 1;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static unsigned
+md_line_indentation(MD_CTX* ctx, unsigned total_indent, OFF beg, OFF* p_end)
+{
+ OFF off = beg;
+ unsigned indent = total_indent;
+
+ while(off < ctx->size && ISBLANK(off)) {
+ if(CH(off) == _T('\t'))
+ indent = (indent + 4) & ~3;
+ else
+ indent++;
+ off++;
+ }
+
+ *p_end = off;
+ return indent - total_indent;
+}
+
+static const MD_LINE_ANALYSIS md_dummy_blank_line = { MD_LINE_BLANK, 0 };
+
+/* Analyze type of the line and find some its properties. This serves as a
+ * main input for determining type and boundaries of a block. */
+static int
+md_analyze_line(MD_CTX* ctx, OFF beg, OFF* p_end,
+ const MD_LINE_ANALYSIS* pivot_line, MD_LINE_ANALYSIS* line)
+{
+ unsigned total_indent = 0;
+ int n_parents = 0;
+ int n_brothers = 0;
+ int n_children = 0;
+ MD_CONTAINER container = { 0 };
+ int prev_line_has_list_loosening_effect = ctx->last_line_has_list_loosening_effect;
+ OFF off = beg;
+ OFF hr_killer = 0;
+ int ret = 0;
+
+ line->indent = md_line_indentation(ctx, total_indent, off, &off);
+ total_indent += line->indent;
+ line->beg = off;
+
+ /* Given the indentation and block quote marks '>', determine how many of
+ * the current containers are our parents. */
+ while(n_parents < ctx->n_containers) {
+ MD_CONTAINER* c = &ctx->containers[n_parents];
+
+ if(c->ch == _T('>') && line->indent < ctx->code_indent_offset &&
+ off < ctx->size && CH(off) == _T('>'))
+ {
+ /* Block quote mark. */
+ off++;
+ total_indent++;
+ line->indent = md_line_indentation(ctx, total_indent, off, &off);
+ total_indent += line->indent;
+
+ /* The optional 1st space after '>' is part of the block quote mark. */
+ if(line->indent > 0)
+ line->indent--;
+
+ line->beg = off;
+ } else if(c->ch != _T('>') && line->indent >= c->contents_indent) {
+ /* List. */
+ line->indent -= c->contents_indent;
+ } else {
+ break;
+ }
+
+ n_parents++;
+ }
+
+ if(off >= ctx->size || ISNEWLINE(off)) {
+ /* Blank line does not need any real indentation to be nested inside
+ * a list. */
+ if(n_brothers + n_children == 0) {
+ while(n_parents < ctx->n_containers && ctx->containers[n_parents].ch != _T('>'))
+ n_parents++;
+ }
+ }
+
+ while(TRUE) {
+ /* Check whether we are fenced code continuation. */
+ if(pivot_line->type == MD_LINE_FENCEDCODE) {
+ line->beg = off;
+
+ /* We are another MD_LINE_FENCEDCODE unless we are closing fence
+ * which we transform into MD_LINE_BLANK. */
+ if(line->indent < ctx->code_indent_offset) {
+ if(md_is_closing_code_fence(ctx, CH(pivot_line->beg), off, &off)) {
+ line->type = MD_LINE_BLANK;
+ ctx->last_line_has_list_loosening_effect = FALSE;
+ break;
+ }
+ }
+
+ /* Change indentation accordingly to the initial code fence. */
+ if(n_parents == ctx->n_containers) {
+ if(line->indent > pivot_line->indent)
+ line->indent -= pivot_line->indent;
+ else
+ line->indent = 0;
+
+ line->type = MD_LINE_FENCEDCODE;
+ break;
+ }
+ }
+
+ /* Check whether we are HTML block continuation. */
+ if(pivot_line->type == MD_LINE_HTML && ctx->html_block_type > 0) {
+ int html_block_type;
+
+ html_block_type = md_is_html_block_end_condition(ctx, off, &off);
+ if(html_block_type > 0) {
+ MD_ASSERT(html_block_type == ctx->html_block_type);
+
+ /* Make sure this is the last line of the block. */
+ ctx->html_block_type = 0;
+
+ /* Some end conditions serve as blank lines at the same time. */
+ if(html_block_type == 6 || html_block_type == 7) {
+ line->type = MD_LINE_BLANK;
+ line->indent = 0;
+ break;
+ }
+ }
+
+ if(n_parents == ctx->n_containers) {
+ line->type = MD_LINE_HTML;
+ break;
+ }
+ }
+
+ /* Check for blank line. */
+ if(off >= ctx->size || ISNEWLINE(off)) {
+ if(pivot_line->type == MD_LINE_INDENTEDCODE && n_parents == ctx->n_containers) {
+ line->type = MD_LINE_INDENTEDCODE;
+ if(line->indent > ctx->code_indent_offset)
+ line->indent -= ctx->code_indent_offset;
+ else
+ line->indent = 0;
+ ctx->last_line_has_list_loosening_effect = FALSE;
+ } else {
+ line->type = MD_LINE_BLANK;
+ ctx->last_line_has_list_loosening_effect = (n_parents > 0 &&
+ n_brothers + n_children == 0 &&
+ ctx->containers[n_parents-1].ch != _T('>'));
+
+ #if 1
+ /* See https://github.com/mity/md4c/issues/6
+ *
+ * This ugly checking tests we are in (yet empty) list item but not
+ * its very first line (with the list item mark).
+ *
+ * If we are such blank line, then any following non-blank line
+ * which would be part of this list item actually ends the list
+ * because "a list item can begin with at most one blank line."
+ */
+ if(n_parents > 0 && ctx->containers[n_parents-1].ch != _T('>') &&
+ n_brothers + n_children == 0 && ctx->current_block == NULL &&
+ ctx->n_block_bytes > (int) sizeof(MD_BLOCK))
+ {
+ MD_BLOCK* top_block = (MD_BLOCK*) ((char*)ctx->block_bytes + ctx->n_block_bytes - sizeof(MD_BLOCK));
+ if(top_block->type == MD_BLOCK_LI)
+ ctx->last_list_item_starts_with_two_blank_lines = TRUE;
+ }
+ #endif
+ }
+ break;
+ } else {
+ #if 1
+ /* This is 2nd half of the hack. If the flag is set (that is there
+ * were 2nd blank line at the start of the list item) and we would also
+ * belonging to such list item, then interrupt the list. */
+ ctx->last_line_has_list_loosening_effect = FALSE;
+ if(ctx->last_list_item_starts_with_two_blank_lines) {
+ if(n_parents > 0 && ctx->containers[n_parents-1].ch != _T('>') &&
+ n_brothers + n_children == 0 && ctx->current_block == NULL &&
+ ctx->n_block_bytes > (int) sizeof(MD_BLOCK))
+ {
+ MD_BLOCK* top_block = (MD_BLOCK*) ((char*)ctx->block_bytes + ctx->n_block_bytes - sizeof(MD_BLOCK));
+ if(top_block->type == MD_BLOCK_LI)
+ n_parents--;
+ }
+
+ ctx->last_list_item_starts_with_two_blank_lines = FALSE;
+ }
+ #endif
+ }
+
+ /* Check whether we are Setext underline. */
+ if(line->indent < ctx->code_indent_offset && pivot_line->type == MD_LINE_TEXT
+ && (CH(off) == _T('=') || CH(off) == _T('-'))
+ && (n_parents == ctx->n_containers))
+ {
+ unsigned level;
+
+ if(md_is_setext_underline(ctx, off, &off, &level)) {
+ line->type = MD_LINE_SETEXTUNDERLINE;
+ line->data = level;
+ break;
+ }
+ }
+
+ /* Check for thematic break line. */
+ if(line->indent < ctx->code_indent_offset && ISANYOF(off, _T("-_*")) && off >= hr_killer) {
+ if(md_is_hr_line(ctx, off, &off, &hr_killer)) {
+ line->type = MD_LINE_HR;
+ break;
+ }
+ }
+
+ /* Check for "brother" container. I.e. whether we are another list item
+ * in already started list. */
+ if(n_parents < ctx->n_containers && n_brothers + n_children == 0) {
+ OFF tmp;
+
+ if(md_is_container_mark(ctx, line->indent, off, &tmp, &container) &&
+ md_is_container_compatible(&ctx->containers[n_parents], &container))
+ {
+ pivot_line = &md_dummy_blank_line;
+
+ off = tmp;
+
+ total_indent += container.contents_indent - container.mark_indent;
+ line->indent = md_line_indentation(ctx, total_indent, off, &off);
+ total_indent += line->indent;
+ line->beg = off;
+
+ /* Some of the following whitespace actually still belongs to the mark. */
+ if(off >= ctx->size || ISNEWLINE(off)) {
+ container.contents_indent++;
+ } else if(line->indent <= ctx->code_indent_offset) {
+ container.contents_indent += line->indent;
+ line->indent = 0;
+ } else {
+ container.contents_indent += 1;
+ line->indent--;
+ }
+
+ ctx->containers[n_parents].mark_indent = container.mark_indent;
+ ctx->containers[n_parents].contents_indent = container.contents_indent;
+
+ n_brothers++;
+ continue;
+ }
+ }
+
+ /* Check for indented code.
+ * Note indented code block cannot interrupt a paragraph. */
+ if(line->indent >= ctx->code_indent_offset &&
+ (pivot_line->type == MD_LINE_BLANK || pivot_line->type == MD_LINE_INDENTEDCODE))
+ {
+ line->type = MD_LINE_INDENTEDCODE;
+ MD_ASSERT(line->indent >= ctx->code_indent_offset);
+ line->indent -= ctx->code_indent_offset;
+ line->data = 0;
+ break;
+ }
+
+ /* Check for start of a new container block. */
+ if(line->indent < ctx->code_indent_offset &&
+ md_is_container_mark(ctx, line->indent, off, &off, &container))
+ {
+ if(pivot_line->type == MD_LINE_TEXT && n_parents == ctx->n_containers &&
+ (off >= ctx->size || ISNEWLINE(off)) && container.ch != _T('>'))
+ {
+ /* Noop. List mark followed by a blank line cannot interrupt a paragraph. */
+ } else if(pivot_line->type == MD_LINE_TEXT && n_parents == ctx->n_containers &&
+ (container.ch == _T('.') || container.ch == _T(')')) && container.start != 1)
+ {
+ /* Noop. Ordered list cannot interrupt a paragraph unless the start index is 1. */
+ } else {
+ total_indent += container.contents_indent - container.mark_indent;
+ line->indent = md_line_indentation(ctx, total_indent, off, &off);
+ total_indent += line->indent;
+
+ line->beg = off;
+ line->data = container.ch;
+
+ /* Some of the following whitespace actually still belongs to the mark. */
+ if(off >= ctx->size || ISNEWLINE(off)) {
+ container.contents_indent++;
+ } else if(line->indent <= ctx->code_indent_offset) {
+ container.contents_indent += line->indent;
+ line->indent = 0;
+ } else {
+ container.contents_indent += 1;
+ line->indent--;
+ }
+
+ if(n_brothers + n_children == 0)
+ pivot_line = &md_dummy_blank_line;
+
+ if(n_children == 0)
+ MD_CHECK(md_leave_child_containers(ctx, n_parents + n_brothers));
+
+ n_children++;
+ MD_CHECK(md_push_container(ctx, &container));
+ continue;
+ }
+ }
+
+ /* Check whether we are table continuation. */
+ if(pivot_line->type == MD_LINE_TABLE && md_is_table_row(ctx, off, &off) &&
+ n_parents == ctx->n_containers)
+ {
+ line->type = MD_LINE_TABLE;
+ break;
+ }
+
+ /* Check for ATX header. */
+ if(line->indent < ctx->code_indent_offset && CH(off) == _T('#')) {
+ unsigned level;
+
+ if(md_is_atxheader_line(ctx, off, &line->beg, &off, &level)) {
+ line->type = MD_LINE_ATXHEADER;
+ line->data = level;
+ break;
+ }
+ }
+
+ /* Check whether we are starting code fence. */
+ if(CH(off) == _T('`') || CH(off) == _T('~')) {
+ if(md_is_opening_code_fence(ctx, off, &off)) {
+ line->type = MD_LINE_FENCEDCODE;
+ line->data = 1;
+ break;
+ }
+ }
+
+ /* Check for start of raw HTML block. */
+ if(CH(off) == _T('<') && !(ctx->parser.flags & MD_FLAG_NOHTMLBLOCKS))
+ {
+ ctx->html_block_type = md_is_html_block_start_condition(ctx, off);
+
+ /* HTML block type 7 cannot interrupt paragraph. */
+ if(ctx->html_block_type == 7 && pivot_line->type == MD_LINE_TEXT)
+ ctx->html_block_type = 0;
+
+ if(ctx->html_block_type > 0) {
+ /* The line itself also may immediately close the block. */
+ if(md_is_html_block_end_condition(ctx, off, &off) == ctx->html_block_type) {
+ /* Make sure this is the last line of the block. */
+ ctx->html_block_type = 0;
+ }
+
+ line->type = MD_LINE_HTML;
+ break;
+ }
+ }
+
+ /* Check for table underline. */
+ if((ctx->parser.flags & MD_FLAG_TABLES) && pivot_line->type == MD_LINE_TEXT &&
+ (CH(off) == _T('|') || CH(off) == _T('-') || CH(off) == _T(':')) &&
+ n_parents == ctx->n_containers)
+ {
+ unsigned col_count;
+
+ if(ctx->current_block != NULL && ctx->current_block->n_lines == 1 &&
+ md_is_table_underline(ctx, off, &off, &col_count) &&
+ md_is_table_row(ctx, pivot_line->beg, NULL))
+ {
+ line->data = col_count;
+ line->type = MD_LINE_TABLEUNDERLINE;
+ break;
+ }
+ }
+
+ /* By default, we are normal text line. */
+ line->type = MD_LINE_TEXT;
+ if(pivot_line->type == MD_LINE_TEXT && n_brothers + n_children == 0) {
+ /* Lazy continuation. */
+ n_parents = ctx->n_containers;
+ }
+
+ /* Check for task mark. */
+ if((ctx->parser.flags & MD_FLAG_TASKLISTS) && n_brothers + n_children > 0 &&
+ ISANYOF_(ctx->containers[ctx->n_containers-1].ch, _T("-+*.)")))
+ {
+ OFF tmp = off;
+
+ while(tmp < ctx->size && tmp < off + 3 && ISBLANK(tmp))
+ tmp++;
+ if(tmp + 2 < ctx->size && CH(tmp) == _T('[') &&
+ ISANYOF(tmp+1, _T("xX ")) && CH(tmp+2) == _T(']') &&
+ (tmp + 3 == ctx->size || ISBLANK(tmp+3) || ISNEWLINE(tmp+3)))
+ {
+ MD_CONTAINER* task_container = (n_children > 0 ? &ctx->containers[ctx->n_containers-1] : &container);
+ task_container->is_task = TRUE;
+ task_container->task_mark_off = tmp + 1;
+ off = tmp + 3;
+ while(ISWHITESPACE(off))
+ off++;
+ line->beg = off;
+ }
+ }
+
+ break;
+ }
+
+ /* Scan for end of the line.
+ *
+ * Note this is quite a bottleneck of the parsing as we here iterate almost
+ * over compete document.
+ */
+#if defined __linux__ && !defined MD4C_USE_UTF16
+ /* Recent glibc versions have superbly optimized strcspn(), even using
+ * vectorization if available. */
+ if(ctx->doc_ends_with_newline && off < ctx->size) {
+ while(TRUE) {
+ off += (OFF) strcspn(STR(off), "\r\n");
+
+ /* strcspn() can stop on zero terminator; but that can appear
+ * anywhere in the Markfown input... */
+ if(CH(off) == _T('\0'))
+ off++;
+ else
+ break;
+ }
+ } else
+#endif
+ {
+ /* Optimization: Use some loop unrolling. */
+ while(off + 3 < ctx->size && !ISNEWLINE(off+0) && !ISNEWLINE(off+1)
+ && !ISNEWLINE(off+2) && !ISNEWLINE(off+3))
+ off += 4;
+ while(off < ctx->size && !ISNEWLINE(off))
+ off++;
+ }
+
+ /* Set end of the line. */
+ line->end = off;
+
+ /* But for ATX header, we should exclude the optional trailing mark. */
+ if(line->type == MD_LINE_ATXHEADER) {
+ OFF tmp = line->end;
+ while(tmp > line->beg && CH(tmp-1) == _T(' '))
+ tmp--;
+ while(tmp > line->beg && CH(tmp-1) == _T('#'))
+ tmp--;
+ if(tmp == line->beg || CH(tmp-1) == _T(' ') || (ctx->parser.flags & MD_FLAG_PERMISSIVEATXHEADERS))
+ line->end = tmp;
+ }
+
+ /* Trim trailing spaces. */
+ if(line->type != MD_LINE_INDENTEDCODE && line->type != MD_LINE_FENCEDCODE) {
+ while(line->end > line->beg && CH(line->end-1) == _T(' '))
+ line->end--;
+ }
+
+ /* Eat also the new line. */
+ if(off < ctx->size && CH(off) == _T('\r'))
+ off++;
+ if(off < ctx->size && CH(off) == _T('\n'))
+ off++;
+
+ *p_end = off;
+
+ /* If we belong to a list after seeing a blank line, the list is loose. */
+ if(prev_line_has_list_loosening_effect && line->type != MD_LINE_BLANK && n_parents + n_brothers > 0) {
+ MD_CONTAINER* c = &ctx->containers[n_parents + n_brothers - 1];
+ if(c->ch != _T('>')) {
+ MD_BLOCK* block = (MD_BLOCK*) (((char*)ctx->block_bytes) + c->block_byte_off);
+ block->flags |= MD_BLOCK_LOOSE_LIST;
+ }
+ }
+
+ /* Leave any containers we are not part of anymore. */
+ if(n_children == 0 && n_parents + n_brothers < ctx->n_containers)
+ MD_CHECK(md_leave_child_containers(ctx, n_parents + n_brothers));
+
+ /* Enter any container we found a mark for. */
+ if(n_brothers > 0) {
+ MD_ASSERT(n_brothers == 1);
+ MD_CHECK(md_push_container_bytes(ctx, MD_BLOCK_LI,
+ ctx->containers[n_parents].task_mark_off,
+ (ctx->containers[n_parents].is_task ? CH(ctx->containers[n_parents].task_mark_off) : 0),
+ MD_BLOCK_CONTAINER_CLOSER));
+ MD_CHECK(md_push_container_bytes(ctx, MD_BLOCK_LI,
+ container.task_mark_off,
+ (container.is_task ? CH(container.task_mark_off) : 0),
+ MD_BLOCK_CONTAINER_OPENER));
+ ctx->containers[n_parents].is_task = container.is_task;
+ ctx->containers[n_parents].task_mark_off = container.task_mark_off;
+ }
+
+ if(n_children > 0)
+ MD_CHECK(md_enter_child_containers(ctx, n_children, line->data));
+
+abort:
+ return ret;
+}
+
+static int
+md_process_line(MD_CTX* ctx, const MD_LINE_ANALYSIS** p_pivot_line, MD_LINE_ANALYSIS* line)
+{
+ const MD_LINE_ANALYSIS* pivot_line = *p_pivot_line;
+ int ret = 0;
+
+ /* Blank line ends current leaf block. */
+ if(line->type == MD_LINE_BLANK) {
+ MD_CHECK(md_end_current_block(ctx));
+ *p_pivot_line = &md_dummy_blank_line;
+ return 0;
+ }
+
+ /* Some line types form block on their own. */
+ if(line->type == MD_LINE_HR || line->type == MD_LINE_ATXHEADER) {
+ MD_CHECK(md_end_current_block(ctx));
+
+ /* Add our single-line block. */
+ MD_CHECK(md_start_new_block(ctx, line));
+ MD_CHECK(md_add_line_into_current_block(ctx, line));
+ MD_CHECK(md_end_current_block(ctx));
+ *p_pivot_line = &md_dummy_blank_line;
+ return 0;
+ }
+
+ /* MD_LINE_SETEXTUNDERLINE changes meaning of the current block and ends it. */
+ if(line->type == MD_LINE_SETEXTUNDERLINE) {
+ MD_ASSERT(ctx->current_block != NULL);
+ ctx->current_block->type = MD_BLOCK_H;
+ ctx->current_block->data = line->data;
+ ctx->current_block->flags |= MD_BLOCK_SETEXT_HEADER;
+ MD_CHECK(md_add_line_into_current_block(ctx, line));
+ MD_CHECK(md_end_current_block(ctx));
+ if(ctx->current_block == NULL) {
+ *p_pivot_line = &md_dummy_blank_line;
+ } else {
+ /* This happens if we have consumed all the body as link ref. defs.
+ * and downgraded the underline into start of a new paragraph block. */
+ line->type = MD_LINE_TEXT;
+ *p_pivot_line = line;
+ }
+ return 0;
+ }
+
+ /* MD_LINE_TABLEUNDERLINE changes meaning of the current block. */
+ if(line->type == MD_LINE_TABLEUNDERLINE) {
+ MD_ASSERT(ctx->current_block != NULL);
+ MD_ASSERT(ctx->current_block->n_lines == 1);
+ ctx->current_block->type = MD_BLOCK_TABLE;
+ ctx->current_block->data = line->data;
+ MD_ASSERT(pivot_line != &md_dummy_blank_line);
+ ((MD_LINE_ANALYSIS*)pivot_line)->type = MD_LINE_TABLE;
+ MD_CHECK(md_add_line_into_current_block(ctx, line));
+ return 0;
+ }
+
+ /* The current block also ends if the line has different type. */
+ if(line->type != pivot_line->type)
+ MD_CHECK(md_end_current_block(ctx));
+
+ /* The current line may start a new block. */
+ if(ctx->current_block == NULL) {
+ MD_CHECK(md_start_new_block(ctx, line));
+ *p_pivot_line = line;
+ }
+
+ /* In all other cases the line is just a continuation of the current block. */
+ MD_CHECK(md_add_line_into_current_block(ctx, line));
+
+abort:
+ return ret;
+}
+
+static int
+md_process_doc(MD_CTX *ctx)
+{
+ const MD_LINE_ANALYSIS* pivot_line = &md_dummy_blank_line;
+ MD_LINE_ANALYSIS line_buf[2];
+ MD_LINE_ANALYSIS* line = &line_buf[0];
+ OFF off = 0;
+ int ret = 0;
+
+ MD_ENTER_BLOCK(MD_BLOCK_DOC, NULL);
+
+ while(off < ctx->size) {
+ if(line == pivot_line)
+ line = (line == &line_buf[0] ? &line_buf[1] : &line_buf[0]);
+
+ MD_CHECK(md_analyze_line(ctx, off, &off, pivot_line, line));
+ MD_CHECK(md_process_line(ctx, &pivot_line, line));
+ }
+
+ md_end_current_block(ctx);
+
+ MD_CHECK(md_build_ref_def_hashtable(ctx));
+
+ /* Process all blocks. */
+ MD_CHECK(md_leave_child_containers(ctx, 0));
+ MD_CHECK(md_process_all_blocks(ctx));
+
+ MD_LEAVE_BLOCK(MD_BLOCK_DOC, NULL);
+
+abort:
+
+#if 0
+ /* Output some memory consumption statistics. */
+ {
+ char buffer[256];
+ sprintf(buffer, "Alloced %u bytes for block buffer.",
+ (unsigned)(ctx->alloc_block_bytes));
+ MD_LOG(buffer);
+
+ sprintf(buffer, "Alloced %u bytes for containers buffer.",
+ (unsigned)(ctx->alloc_containers * sizeof(MD_CONTAINER)));
+ MD_LOG(buffer);
+
+ sprintf(buffer, "Alloced %u bytes for marks buffer.",
+ (unsigned)(ctx->alloc_marks * sizeof(MD_MARK)));
+ MD_LOG(buffer);
+
+ sprintf(buffer, "Alloced %u bytes for aux. buffer.",
+ (unsigned)(ctx->alloc_buffer * sizeof(MD_CHAR)));
+ MD_LOG(buffer);
+ }
+#endif
+
+ return ret;
+}
+
+
+/********************
+ *** Public API ***
+ ********************/
+
+int
+md_parse(const MD_CHAR* text, MD_SIZE size, const MD_PARSER* parser, void* userdata)
+{
+ MD_CTX ctx;
+ int i;
+ int ret;
+
+ if(parser->abi_version != 0) {
+ if(parser->debug_log != NULL)
+ parser->debug_log("Unsupported abi_version.", userdata);
+ return -1;
+ }
+
+ /* Setup context structure. */
+ memset(&ctx, 0, sizeof(MD_CTX));
+ ctx.text = text;
+ ctx.size = size;
+ memcpy(&ctx.parser, parser, sizeof(MD_PARSER));
+ ctx.userdata = userdata;
+ ctx.code_indent_offset = (ctx.parser.flags & MD_FLAG_NOINDENTEDCODEBLOCKS) ? (OFF)(-1) : 4;
+ md_build_mark_char_map(&ctx);
+ ctx.doc_ends_with_newline = (size > 0 && ISNEWLINE_(text[size-1]));
+
+ /* Reset all unresolved opener mark chains. */
+ for(i = 0; i < (int) SIZEOF_ARRAY(ctx.mark_chains); i++) {
+ ctx.mark_chains[i].head = -1;
+ ctx.mark_chains[i].tail = -1;
+ }
+ ctx.unresolved_link_head = -1;
+ ctx.unresolved_link_tail = -1;
+
+ /* All the work. */
+ ret = md_process_doc(&ctx);
+
+ /* Clean-up. */
+ md_free_ref_defs(&ctx);
+ md_free_ref_def_hashtable(&ctx);
+ free(ctx.buffer);
+ free(ctx.marks);
+ free(ctx.block_bytes);
+ free(ctx.containers);
+
+ return ret;
+}
diff --git a/src/3rdparty/md4c/md4c.h b/src/3rdparty/md4c/md4c.h
new file mode 100644
index 0000000000..6d9fce5180
--- /dev/null
+++ b/src/3rdparty/md4c/md4c.h
@@ -0,0 +1,373 @@
+/*
+ * MD4C: Markdown parser for C
+ * (http://github.com/mity/md4c)
+ *
+ * Copyright (c) 2016-2019 Martin Mitas
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef MD4C_MARKDOWN_H
+#define MD4C_MARKDOWN_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#if defined MD4C_USE_UTF16
+ /* Magic to support UTF-16. Not that in order to use it, you have to define
+ * the macro MD4C_USE_UTF16 both when building MD4C as well as when
+ * including this header in your code. */
+ #ifdef _WIN32
+ #include <windows.h>
+ typedef WCHAR MD_CHAR;
+ #else
+ #error MD4C_USE_UTF16 is only supported on Windows.
+ #endif
+#else
+ typedef char MD_CHAR;
+#endif
+
+typedef unsigned MD_SIZE;
+typedef unsigned MD_OFFSET;
+
+
+/* Block represents a part of document hierarchy structure like a paragraph
+ * or list item.
+ */
+typedef enum MD_BLOCKTYPE {
+ /* <body>...</body> */
+ MD_BLOCK_DOC = 0,
+
+ /* <blockquote>...</blockquote> */
+ MD_BLOCK_QUOTE,
+
+ /* <ul>...</ul>
+ * Detail: Structure MD_BLOCK_UL_DETAIL. */
+ MD_BLOCK_UL,
+
+ /* <ol>...</ol>
+ * Detail: Structure MD_BLOCK_OL_DETAIL. */
+ MD_BLOCK_OL,
+
+ /* <li>...</li>
+ * Detail: Structure MD_BLOCK_LI_DETAIL. */
+ MD_BLOCK_LI,
+
+ /* <hr> */
+ MD_BLOCK_HR,
+
+ /* <h1>...</h1> (for levels up to 6)
+ * Detail: Structure MD_BLOCK_H_DETAIL. */
+ MD_BLOCK_H,
+
+ /* <pre><code>...</code></pre>
+ * Note the text lines within code blocks are terminated with '\n'
+ * instead of explicit MD_TEXT_BR. */
+ MD_BLOCK_CODE,
+
+ /* Raw HTML block. This itself does not correspond to any particular HTML
+ * tag. The contents of it _is_ raw HTML source intended to be put
+ * in verbatim form to the HTML output. */
+ MD_BLOCK_HTML,
+
+ /* <p>...</p> */
+ MD_BLOCK_P,
+
+ /* <table>...</table> and its contents.
+ * Detail: Structure MD_BLOCK_TD_DETAIL (used with MD_BLOCK_TH and MD_BLOCK_TD)
+ * Note all of these are used only if extension MD_FLAG_TABLES is enabled. */
+ MD_BLOCK_TABLE,
+ MD_BLOCK_THEAD,
+ MD_BLOCK_TBODY,
+ MD_BLOCK_TR,
+ MD_BLOCK_TH,
+ MD_BLOCK_TD
+} MD_BLOCKTYPE;
+
+/* Span represents an in-line piece of a document which should be rendered with
+ * the same font, color and other attributes. A sequence of spans forms a block
+ * like paragraph or list item. */
+typedef enum MD_SPANTYPE {
+ /* <em>...</em> */
+ MD_SPAN_EM,
+
+ /* <strong>...</strong> */
+ MD_SPAN_STRONG,
+
+ /* <a href="xxx">...</a>
+ * Detail: Structure MD_SPAN_A_DETAIL. */
+ MD_SPAN_A,
+
+ /* <img src="xxx">...</a>
+ * Detail: Structure MD_SPAN_IMG_DETAIL.
+ * Note: Image text can contain nested spans and even nested images.
+ * If rendered into ALT attribute of HTML <IMG> tag, it's responsibility
+ * of the renderer to deal with it.
+ */
+ MD_SPAN_IMG,
+
+ /* <code>...</code> */
+ MD_SPAN_CODE,
+
+ /* <del>...</del>
+ * Note: Recognized only when MD_FLAG_STRIKETHROUGH is enabled.
+ */
+ MD_SPAN_DEL,
+
+ /* For recognizing inline ($) and display ($$) equations
+ * Note: Recognized only when MD_FLAG_LATEXMATHSPANS is enabled.
+ */
+ MD_SPAN_LATEXMATH,
+ MD_SPAN_LATEXMATH_DISPLAY
+} MD_SPANTYPE;
+
+/* Text is the actual textual contents of span. */
+typedef enum MD_TEXTTYPE {
+ /* Normal text. */
+ MD_TEXT_NORMAL = 0,
+
+ /* NULL character. CommonMark requires replacing NULL character with
+ * the replacement char U+FFFD, so this allows caller to do that easily. */
+ MD_TEXT_NULLCHAR,
+
+ /* Line breaks.
+ * Note these are not sent from blocks with verbatim output (MD_BLOCK_CODE
+ * or MD_BLOCK_HTML). In such cases, '\n' is part of the text itself. */
+ MD_TEXT_BR, /* <br> (hard break) */
+ MD_TEXT_SOFTBR, /* '\n' in source text where it is not semantically meaningful (soft break) */
+
+ /* Entity.
+ * (a) Named entity, e.g. &nbsp;
+ * (Note MD4C does not have a list of known entities.
+ * Anything matching the regexp /&[A-Za-z][A-Za-z0-9]{1,47};/ is
+ * treated as a named entity.)
+ * (b) Numerical entity, e.g. &#1234;
+ * (c) Hexadecimal entity, e.g. &#x12AB;
+ *
+ * As MD4C is mostly encoding agnostic, application gets the verbatim
+ * entity text into the MD_RENDERER::text_callback(). */
+ MD_TEXT_ENTITY,
+
+ /* Text in a code block (inside MD_BLOCK_CODE) or inlined code (`code`).
+ * If it is inside MD_BLOCK_CODE, it includes spaces for indentation and
+ * '\n' for new lines. MD_TEXT_BR and MD_TEXT_SOFTBR are not sent for this
+ * kind of text. */
+ MD_TEXT_CODE,
+
+ /* Text is a raw HTML. If it is contents of a raw HTML block (i.e. not
+ * an inline raw HTML), then MD_TEXT_BR and MD_TEXT_SOFTBR are not used.
+ * The text contains verbatim '\n' for the new lines. */
+ MD_TEXT_HTML,
+
+ /* Text is inside an equation. This is processed the same way as inlined code
+ * spans (`code`). */
+ MD_TEXT_LATEXMATH
+} MD_TEXTTYPE;
+
+
+/* Alignment enumeration. */
+typedef enum MD_ALIGN {
+ MD_ALIGN_DEFAULT = 0, /* When unspecified. */
+ MD_ALIGN_LEFT,
+ MD_ALIGN_CENTER,
+ MD_ALIGN_RIGHT
+} MD_ALIGN;
+
+
+/* String attribute.
+ *
+ * This wraps strings which are outside of a normal text flow and which are
+ * propagated within various detailed structures, but which still may contain
+ * string portions of different types like e.g. entities.
+ *
+ * So, for example, lets consider an image has a title attribute string
+ * set to "foo &quot; bar". (Note the string size is 14.)
+ *
+ * Then the attribute MD_SPAN_IMG_DETAIL::title shall provide the following:
+ * -- [0]: "foo " (substr_types[0] == MD_TEXT_NORMAL; substr_offsets[0] == 0)
+ * -- [1]: "&quot;" (substr_types[1] == MD_TEXT_ENTITY; substr_offsets[1] == 4)
+ * -- [2]: " bar" (substr_types[2] == MD_TEXT_NORMAL; substr_offsets[2] == 10)
+ * -- [3]: (n/a) (n/a ; substr_offsets[3] == 14)
+ *
+ * Note that these conditions are guaranteed:
+ * -- substr_offsets[0] == 0
+ * -- substr_offsets[LAST+1] == size
+ * -- Only MD_TEXT_NORMAL, MD_TEXT_ENTITY, MD_TEXT_NULLCHAR substrings can appear.
+ */
+typedef struct MD_ATTRIBUTE {
+ const MD_CHAR* text;
+ MD_SIZE size;
+ const MD_TEXTTYPE* substr_types;
+ const MD_OFFSET* substr_offsets;
+} MD_ATTRIBUTE;
+
+
+/* Detailed info for MD_BLOCK_UL. */
+typedef struct MD_BLOCK_UL_DETAIL {
+ int is_tight; /* Non-zero if tight list, zero if loose. */
+ MD_CHAR mark; /* Item bullet character in MarkDown source of the list, e.g. '-', '+', '*'. */
+} MD_BLOCK_UL_DETAIL;
+
+/* Detailed info for MD_BLOCK_OL. */
+typedef struct MD_BLOCK_OL_DETAIL {
+ unsigned start; /* Start index of the ordered list. */
+ int is_tight; /* Non-zero if tight list, zero if loose. */
+ MD_CHAR mark_delimiter; /* Character delimiting the item marks in MarkDown source, e.g. '.' or ')' */
+} MD_BLOCK_OL_DETAIL;
+
+/* Detailed info for MD_BLOCK_LI. */
+typedef struct MD_BLOCK_LI_DETAIL {
+ int is_task; /* Can be non-zero only with MD_FLAG_TASKLISTS */
+ MD_CHAR task_mark; /* If is_task, then one of 'x', 'X' or ' '. Undefined otherwise. */
+ MD_OFFSET task_mark_offset; /* If is_task, then offset in the input of the char between '[' and ']'. */
+} MD_BLOCK_LI_DETAIL;
+
+/* Detailed info for MD_BLOCK_H. */
+typedef struct MD_BLOCK_H_DETAIL {
+ unsigned level; /* Header level (1 - 6) */
+} MD_BLOCK_H_DETAIL;
+
+/* Detailed info for MD_BLOCK_CODE. */
+typedef struct MD_BLOCK_CODE_DETAIL {
+ MD_ATTRIBUTE info;
+ MD_ATTRIBUTE lang;
+ MD_CHAR fence_char; /* The character used for fenced code block; or zero for indented code block. */
+} MD_BLOCK_CODE_DETAIL;
+
+/* Detailed info for MD_BLOCK_TH and MD_BLOCK_TD. */
+typedef struct MD_BLOCK_TD_DETAIL {
+ MD_ALIGN align;
+} MD_BLOCK_TD_DETAIL;
+
+/* Detailed info for MD_SPAN_A. */
+typedef struct MD_SPAN_A_DETAIL {
+ MD_ATTRIBUTE href;
+ MD_ATTRIBUTE title;
+} MD_SPAN_A_DETAIL;
+
+/* Detailed info for MD_SPAN_IMG. */
+typedef struct MD_SPAN_IMG_DETAIL {
+ MD_ATTRIBUTE src;
+ MD_ATTRIBUTE title;
+} MD_SPAN_IMG_DETAIL;
+
+
+/* Flags specifying extensions/deviations from CommonMark specification.
+ *
+ * By default (when MD_RENDERER::flags == 0), we follow CommonMark specification.
+ * The following flags may allow some extensions or deviations from it.
+ */
+#define MD_FLAG_COLLAPSEWHITESPACE 0x0001 /* In MD_TEXT_NORMAL, collapse non-trivial whitespace into single ' ' */
+#define MD_FLAG_PERMISSIVEATXHEADERS 0x0002 /* Do not require space in ATX headers ( ###header ) */
+#define MD_FLAG_PERMISSIVEURLAUTOLINKS 0x0004 /* Recognize URLs as autolinks even without '<', '>' */
+#define MD_FLAG_PERMISSIVEEMAILAUTOLINKS 0x0008 /* Recognize e-mails as autolinks even without '<', '>' and 'mailto:' */
+#define MD_FLAG_NOINDENTEDCODEBLOCKS 0x0010 /* Disable indented code blocks. (Only fenced code works.) */
+#define MD_FLAG_NOHTMLBLOCKS 0x0020 /* Disable raw HTML blocks. */
+#define MD_FLAG_NOHTMLSPANS 0x0040 /* Disable raw HTML (inline). */
+#define MD_FLAG_TABLES 0x0100 /* Enable tables extension. */
+#define MD_FLAG_STRIKETHROUGH 0x0200 /* Enable strikethrough extension. */
+#define MD_FLAG_PERMISSIVEWWWAUTOLINKS 0x0400 /* Enable WWW autolinks (even without any scheme prefix, if they begin with 'www.') */
+#define MD_FLAG_TASKLISTS 0x0800 /* Enable task list extension. */
+#define MD_FLAG_LATEXMATHSPANS 0x1000 /* Enable $ and $$ containing LaTeX equations. */
+
+#define MD_FLAG_PERMISSIVEAUTOLINKS (MD_FLAG_PERMISSIVEEMAILAUTOLINKS | MD_FLAG_PERMISSIVEURLAUTOLINKS | MD_FLAG_PERMISSIVEWWWAUTOLINKS)
+#define MD_FLAG_NOHTML (MD_FLAG_NOHTMLBLOCKS | MD_FLAG_NOHTMLSPANS)
+
+/* Convenient sets of flags corresponding to well-known Markdown dialects.
+ *
+ * Note we may only support subset of features of the referred dialect.
+ * The constant just enables those extensions which bring us as close as
+ * possible given what features we implement.
+ *
+ * ABI compatibility note: Meaning of these can change in time as new
+ * extensions, bringing the dialect closer to the original, are implemented.
+ */
+#define MD_DIALECT_COMMONMARK 0
+#define MD_DIALECT_GITHUB (MD_FLAG_PERMISSIVEAUTOLINKS | MD_FLAG_TABLES | MD_FLAG_STRIKETHROUGH | MD_FLAG_TASKLISTS)
+
+/* Renderer structure.
+ */
+typedef struct MD_PARSER {
+ /* Reserved. Set to zero.
+ */
+ unsigned abi_version;
+
+ /* Dialect options. Bitmask of MD_FLAG_xxxx values.
+ */
+ unsigned flags;
+
+ /* Caller-provided rendering callbacks.
+ *
+ * For some block/span types, more detailed information is provided in a
+ * type-specific structure pointed by the argument 'detail'.
+ *
+ * The last argument of all callbacks, 'userdata', is just propagated from
+ * md_parse() and is available for any use by the application.
+ *
+ * Note any strings provided to the callbacks as their arguments or as
+ * members of any detail structure are generally not zero-terminated.
+ * Application has take the respective size information into account.
+ *
+ * Callbacks may abort further parsing of the document by returning non-zero.
+ */
+ int (*enter_block)(MD_BLOCKTYPE /*type*/, void* /*detail*/, void* /*userdata*/);
+ int (*leave_block)(MD_BLOCKTYPE /*type*/, void* /*detail*/, void* /*userdata*/);
+
+ int (*enter_span)(MD_SPANTYPE /*type*/, void* /*detail*/, void* /*userdata*/);
+ int (*leave_span)(MD_SPANTYPE /*type*/, void* /*detail*/, void* /*userdata*/);
+
+ int (*text)(MD_TEXTTYPE /*type*/, const MD_CHAR* /*text*/, MD_SIZE /*size*/, void* /*userdata*/);
+
+ /* Debug callback. Optional (may be NULL).
+ *
+ * If provided and something goes wrong, this function gets called.
+ * This is intended for debugging and problem diagnosis for developers;
+ * it is not intended to provide any errors suitable for displaying to an
+ * end user.
+ */
+ void (*debug_log)(const char* /*msg*/, void* /*userdata*/);
+
+ /* Reserved. Set to NULL.
+ */
+ void (*syntax)(void);
+} MD_PARSER;
+
+
+/* For backward compatibility. Do not use in new code. */
+typedef MD_PARSER MD_RENDERER;
+
+
+/* Parse the Markdown document stored in the string 'text' of size 'size'.
+ * The renderer provides callbacks to be called during the parsing so the
+ * caller can render the document on the screen or convert the Markdown
+ * to another format.
+ *
+ * Zero is returned on success. If a runtime error occurs (e.g. a memory
+ * fails), -1 is returned. If the processing is aborted due any callback
+ * returning non-zero, md_parse() the return value of the callback is returned.
+ */
+int md_parse(const MD_CHAR* text, MD_SIZE size, const MD_PARSER* parser, void* userdata);
+
+
+#ifdef __cplusplus
+ } /* extern "C" { */
+#endif
+
+#endif /* MD4C_MARKDOWN_H */
diff --git a/src/3rdparty/md4c/qt_attribution.json b/src/3rdparty/md4c/qt_attribution.json
new file mode 100644
index 0000000000..5fd77269e9
--- /dev/null
+++ b/src/3rdparty/md4c/qt_attribution.json
@@ -0,0 +1,15 @@
+{
+ "Id": "md4c",
+ "Name": "MD4C",
+ "QDocModule": "qtgui",
+ "QtUsage": "Optionally used in QTextDocument if configured with textmarkdownreader.",
+
+ "Description": "A CommonMark-compliant Markdown parser.",
+ "Homepage": "https://github.com/mity/md4c",
+ "License": "MIT License",
+ "LicenseId": "MIT",
+ "LicenseFile": "LICENSE.md",
+ "Version": "0.3.4",
+ "DownloadLocation": "https://github.com/mity/md4c/releases/tag/release-0.3.4",
+ "Copyright": "Copyright © 2016-2019 Martin Mitáš"
+}
diff --git a/src/3rdparty/pcre2/patches/0001-fix-rtems-build-undefine-madvise.patch b/src/3rdparty/pcre2/patches/0001-fix-rtems-build-undefine-madvise.patch
new file mode 100644
index 0000000000..074b39df85
--- /dev/null
+++ b/src/3rdparty/pcre2/patches/0001-fix-rtems-build-undefine-madvise.patch
@@ -0,0 +1,28 @@
+From ac10063196685fe6124055feb1275e13a78f562e Mon Sep 17 00:00:00 2001
+From: Mikhail Svetkin <mikhail.svetkin@qt.io>
+Date: Tue, 20 Mar 2018 14:03:54 +0100
+Subject: [PATCH] rtems: Fix pcre2 build (madvise undefined)
+
+RTEMS does not have madvise. We can use only posix_madvise
+
+Change-Id: Ia18b7cd2d7f9db84331f7e2350d060b9e85b30c8
+---
+ src/3rdparty/pcre2/src/sljit/sljitUtils.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/3rdparty/pcre2/src/sljit/sljitUtils.c b/src/3rdparty/pcre2/src/sljit/sljitUtils.c
+index 5c2a838932..2ead044b1b 100644
+--- a/src/3rdparty/pcre2/src/sljit/sljitUtils.c
++++ b/src/3rdparty/pcre2/src/sljit/sljitUtils.c
+@@ -315,7 +315,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_u8 *SLJIT_FUNC sljit_stack_resize(struct sljit_st
+ aligned_new_start = (sljit_uw)new_start & ~sljit_page_align;
+ aligned_old_start = ((sljit_uw)stack->start) & ~sljit_page_align;
+ /* If madvise is available, we release the unnecessary space. */
+-#if defined(MADV_DONTNEED)
++#if defined(MADV_DONTNEED) && !defined(__rtems__)
+ if (aligned_new_start > aligned_old_start)
+ madvise((void*)aligned_old_start, aligned_new_start - aligned_old_start, MADV_DONTNEED);
+ #elif defined(POSIX_MADV_DONTNEED)
+--
+2.21.0
+
diff --git a/src/3rdparty/pcre2/src/sljit/sljitUtils.c b/src/3rdparty/pcre2/src/sljit/sljitUtils.c
index 5c2a838932..2ead044b1b 100644
--- a/src/3rdparty/pcre2/src/sljit/sljitUtils.c
+++ b/src/3rdparty/pcre2/src/sljit/sljitUtils.c
@@ -315,7 +315,7 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_u8 *SLJIT_FUNC sljit_stack_resize(struct sljit_st
aligned_new_start = (sljit_uw)new_start & ~sljit_page_align;
aligned_old_start = ((sljit_uw)stack->start) & ~sljit_page_align;
/* If madvise is available, we release the unnecessary space. */
-#if defined(MADV_DONTNEED)
+#if defined(MADV_DONTNEED) && !defined(__rtems__)
if (aligned_new_start > aligned_old_start)
madvise((void*)aligned_old_start, aligned_new_start - aligned_old_start, MADV_DONTNEED);
#elif defined(POSIX_MADV_DONTNEED)
diff --git a/src/3rdparty/sha3/brg_endian.h b/src/3rdparty/sha3/brg_endian.h
index 09d2a8b6a9..9bb306e678 100644
--- a/src/3rdparty/sha3/brg_endian.h
+++ b/src/3rdparty/sha3/brg_endian.h
@@ -42,7 +42,7 @@ Changes for ARM 9/9/2010 [Downstream relative to Gladman's GitHub, upstream to Q
#elif defined( __linux__ ) || defined( __GNUC__ ) || defined( __GNU_LIBRARY__ )
# if !defined( __MINGW32__ ) && !defined( _AIX ) && !defined(Q_OS_QNX)
# include <endian.h>
-# if !defined( __BEOS__ )
+# if !defined( __BEOS__ ) && !defined(Q_OS_RTEMS)
# include <byteswap.h>
# endif
# endif
diff --git a/src/3rdparty/sqlite.pri b/src/3rdparty/sqlite.pri
index 068764c726..cc8c12a6da 100644
--- a/src/3rdparty/sqlite.pri
+++ b/src/3rdparty/sqlite.pri
@@ -1,5 +1,6 @@
CONFIG(release, debug|release):DEFINES *= NDEBUG
-DEFINES += SQLITE_ENABLE_COLUMN_METADATA SQLITE_OMIT_LOAD_EXTENSION SQLITE_OMIT_COMPLETE SQLITE_ENABLE_FTS3 SQLITE_ENABLE_FTS3_PARENTHESIS SQLITE_ENABLE_FTS5 SQLITE_ENABLE_RTREE SQLITE_ENABLE_JSON1
+QT_FOR_CONFIG += core-private
+DEFINES += SQLITE_ENABLE_COLUMN_METADATA SQLITE_OMIT_COMPLETE SQLITE_ENABLE_FTS3 SQLITE_ENABLE_FTS3_PARENTHESIS SQLITE_ENABLE_FTS5 SQLITE_ENABLE_RTREE SQLITE_ENABLE_JSON1
!contains(CONFIG, largefile):DEFINES += SQLITE_DISABLE_LFS
qtConfig(posix_fallocate): DEFINES += HAVE_POSIX_FALLOCATE=1
winrt {
@@ -8,6 +9,11 @@ winrt {
}
qnx: DEFINES += _QNX_SOURCE
!win32:!winrt:!winphone: DEFINES += HAVE_USLEEP=1
+qtConfig(dlopen) {
+ QMAKE_USE += libdl
+} else {
+ DEFINES += SQLITE_OMIT_LOAD_EXTENSION
+}
integrity: QMAKE_CFLAGS += -include qplatformdefs.h
INCLUDEPATH += $$PWD/sqlite
SOURCES += $$PWD/sqlite/sqlite3.c
diff --git a/src/3rdparty/sqlite/patches/0001-Fix-CVE-2019-16168-in-SQLite.patch b/src/3rdparty/sqlite/patches/0001-Fix-CVE-2019-16168-in-SQLite.patch
deleted file mode 100644
index e56a6a2411..0000000000
--- a/src/3rdparty/sqlite/patches/0001-Fix-CVE-2019-16168-in-SQLite.patch
+++ /dev/null
@@ -1,42 +0,0 @@
-From 3442a3ce9c2bd366eb0bd1c18d37a6ce732a888d Mon Sep 17 00:00:00 2001
-From: Andy Shaw <andy.shaw@qt.io>
-Date: Wed, 25 Sep 2019 09:17:01 +0200
-Subject: [PATCH] Fix CVE-2019-16168 in SQLite
-
-v3.29.0 is the latest and there is no indication as to when the next
-release is so we will apply this separately for now and it can be
-reverted once it is in a release that we ship with.
-
-This patch is taken from https://www.sqlite.org/src/info/98357d8c1263920b
-
-Change-Id: I82d398b093b67842a4369e3220c01e7eea30763a
----
- src/3rdparty/sqlite/sqlite3.c | 5 ++++-
- 1 file changed, 4 insertions(+), 1 deletion(-)
-
-diff --git a/src/3rdparty/sqlite/sqlite3.c b/src/3rdparty/sqlite/sqlite3.c
-index 61bfdeb766..b3e6ae27b6 100644
---- a/src/3rdparty/sqlite/sqlite3.c
-+++ b/src/3rdparty/sqlite/sqlite3.c
-@@ -105933,7 +105933,9 @@ static void decodeIntArray(
- if( sqlite3_strglob("unordered*", z)==0 ){
- pIndex->bUnordered = 1;
- }else if( sqlite3_strglob("sz=[0-9]*", z)==0 ){
-- pIndex->szIdxRow = sqlite3LogEst(sqlite3Atoi(z+3));
-+ int sz = sqlite3Atoi(z+3);
-+ if( sz<2 ) sz = 2;
-+ pIndex->szIdxRow = sqlite3LogEst(sz);
- }else if( sqlite3_strglob("noskipscan*", z)==0 ){
- pIndex->noSkipScan = 1;
- }
-@@ -143260,6 +143262,7 @@ static int whereLoopAddBtreeIndex(
- ** it to pNew->rRun, which is currently set to the cost of the index
- ** seek only. Then, if this is a non-covering index, add the cost of
- ** visiting the rows in the main table. */
-+ assert( pSrc->pTab->szTabRow>0 );
- rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pTab->szTabRow;
- pNew->rRun = sqlite3LogEstAdd(rLogSize, rCostIdx);
- if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK))==0 ){
---
-2.20.1 (Apple Git-117)
-
diff --git a/src/3rdparty/sqlite/qt_attribution.json b/src/3rdparty/sqlite/qt_attribution.json
index 2ad20f8637..4d0851e9b9 100644
--- a/src/3rdparty/sqlite/qt_attribution.json
+++ b/src/3rdparty/sqlite/qt_attribution.json
@@ -6,8 +6,8 @@
"Description": "SQLite is a small C library that implements a self-contained, embeddable, zero-configuration SQL database engine.",
"Homepage": "https://www.sqlite.org/",
- "Version": "3.29.0",
- "DownloadLocation": "https://www.sqlite.org/2019/sqlite-amalgamation-3290000.zip",
+ "Version": "3.30.1",
+ "DownloadLocation": "https://www.sqlite.org/2019/sqlite-amalgamation-3300100.zip",
"License": "Public Domain",
"Copyright": "The authors disclaim copyright to the source code. However, a license can be obtained if needed."
}
diff --git a/src/3rdparty/sqlite/sqlite3.c b/src/3rdparty/sqlite/sqlite3.c
index b3e6ae27b6..8fd740b300 100644
--- a/src/3rdparty/sqlite/sqlite3.c
+++ b/src/3rdparty/sqlite/sqlite3.c
@@ -1,6 +1,6 @@
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
-** version 3.29.0. By combining all the individual C code files into this
+** version 3.30.1. By combining all the individual C code files into this
** single large file, the entire code can be compiled as a single translation
** unit. This allows many compilers to do optimizations that would not be
** possible if the files were compiled separately. Performance improvements
@@ -331,8 +331,6 @@ static const char * const sqlite3azCompileOpt[] = {
#endif
#if defined(SQLITE_ENABLE_STAT4)
"ENABLE_STAT4",
-#elif defined(SQLITE_ENABLE_STAT3)
- "ENABLE_STAT3",
#endif
#if SQLITE_ENABLE_STMTVTAB
"ENABLE_STMTVTAB",
@@ -1167,9 +1165,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.29.0"
-#define SQLITE_VERSION_NUMBER 3029000
-#define SQLITE_SOURCE_ID "2019-07-10 17:32:03 fc82b73eaac8b36950e527f12c4b5dc1e147e6f4ad2217ae43ad82882a88bfa6"
+#define SQLITE_VERSION "3.30.1"
+#define SQLITE_VERSION_NUMBER 3030001
+#define SQLITE_SOURCE_ID "2019-10-10 20:19:45 18db032d058f1436ce3dea84081f4ee5a0f2259ad97301d43c426bc7f3df1b0b"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -3137,6 +3135,17 @@ struct sqlite3_mem_methods {
** following this call. The second parameter may be a NULL pointer, in
** which case the trigger setting is not reported back. </dd>
**
+** [[SQLITE_DBCONFIG_ENABLE_VIEW]]
+** <dt>SQLITE_DBCONFIG_ENABLE_VIEW</dt>
+** <dd> ^This option is used to enable or disable [CREATE VIEW | views].
+** There should be two additional arguments.
+** The first argument is an integer which is 0 to disable views,
+** positive to enable views or negative to leave the setting unchanged.
+** The second parameter is a pointer to an integer into which
+** is written 0 or 1 to indicate whether views are disabled or enabled
+** following this call. The second parameter may be a NULL pointer, in
+** which case the view setting is not reported back. </dd>
+**
** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
** <dd> ^This option is used to enable or disable the
@@ -3309,7 +3318,8 @@ struct sqlite3_mem_methods {
#define SQLITE_DBCONFIG_LEGACY_ALTER_TABLE 1012 /* int int* */
#define SQLITE_DBCONFIG_DQS_DML 1013 /* int int* */
#define SQLITE_DBCONFIG_DQS_DDL 1014 /* int int* */
-#define SQLITE_DBCONFIG_MAX 1014 /* Largest DBCONFIG */
+#define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */
+#define SQLITE_DBCONFIG_MAX 1015 /* Largest DBCONFIG */
/*
** CAPI3REF: Enable Or Disable Extended Result Codes
@@ -4858,7 +4868,7 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
** ^The specific value of WHERE-clause [parameter] might influence the
** choice of query plan if the parameter is the left-hand side of a [LIKE]
** or [GLOB] operator or if the parameter is compared to an indexed column
-** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled.
+** and the [SQLITE_ENABLE_STAT4] compile-time option is enabled.
** </li>
** </ol>
**
@@ -5893,6 +5903,12 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
** perform additional optimizations on deterministic functions, so use
** of the [SQLITE_DETERMINISTIC] flag is recommended where possible.
**
+** ^The fourth parameter may also optionally include the [SQLITE_DIRECTONLY]
+** flag, which if present prevents the function from being invoked from
+** within VIEWs or TRIGGERs. For security reasons, the [SQLITE_DIRECTONLY]
+** flag is recommended for any application-defined SQL function that has
+** side-effects.
+**
** ^(The fifth parameter is an arbitrary pointer. The implementation of the
** function can gain access to this pointer using [sqlite3_user_data()].)^
**
@@ -6009,8 +6025,30 @@ SQLITE_API int sqlite3_create_window_function(
** [SQLITE_UTF8 | preferred text encoding] as the fourth argument
** to [sqlite3_create_function()], [sqlite3_create_function16()], or
** [sqlite3_create_function_v2()].
+**
+** The SQLITE_DETERMINISTIC flag means that the new function will always
+** maps the same inputs into the same output. The abs() function is
+** deterministic, for example, but randomblob() is not.
+**
+** The SQLITE_DIRECTONLY flag means that the function may only be invoked
+** from top-level SQL, and cannot be used in VIEWs or TRIGGERs. This is
+** a security feature which is recommended for all
+** [application-defined SQL functions] that have side-effects. This flag
+** prevents an attacker from adding triggers and views to a schema then
+** tricking a high-privilege application into causing unintended side-effects
+** while performing ordinary queries.
+**
+** The SQLITE_SUBTYPE flag indicates to SQLite that a function may call
+** [sqlite3_value_subtype()] to inspect the sub-types of its arguments.
+** Specifying this flag makes no difference for scalar or aggregate user
+** functions. However, if it is not specified for a user-defined window
+** function, then any sub-types belonging to arguments passed to the window
+** function may be discarded before the window function is called (i.e.
+** sqlite3_value_subtype() will always return 0).
*/
-#define SQLITE_DETERMINISTIC 0x800
+#define SQLITE_DETERMINISTIC 0x000000800
+#define SQLITE_DIRECTONLY 0x000080000
+#define SQLITE_SUBTYPE 0x000100000
/*
** CAPI3REF: Deprecated Functions
@@ -7656,6 +7694,12 @@ struct sqlite3_index_info {
** ^The sqlite3_create_module()
** interface is equivalent to sqlite3_create_module_v2() with a NULL
** destructor.
+**
+** ^If the third parameter (the pointer to the sqlite3_module object) is
+** NULL then no new module is create and any existing modules with the
+** same name are dropped.
+**
+** See also: [sqlite3_drop_modules()]
*/
SQLITE_API int sqlite3_create_module(
sqlite3 *db, /* SQLite connection to register module with */
@@ -7672,6 +7716,23 @@ SQLITE_API int sqlite3_create_module_v2(
);
/*
+** CAPI3REF: Remove Unnecessary Virtual Table Implementations
+** METHOD: sqlite3
+**
+** ^The sqlite3_drop_modules(D,L) interface removes all virtual
+** table modules from database connection D except those named on list L.
+** The L parameter must be either NULL or a pointer to an array of pointers
+** to strings where the array is terminated by a single NULL pointer.
+** ^If the L parameter is NULL, then all virtual table modules are removed.
+**
+** See also: [sqlite3_create_module()]
+*/
+SQLITE_API int sqlite3_drop_modules(
+ sqlite3 *db, /* Remove modules from this connection */
+ const char **azKeep /* Except, do not remove the ones named here */
+);
+
+/*
** CAPI3REF: Virtual Table Instance Object
** KEYWORDS: sqlite3_vtab
**
@@ -8379,7 +8440,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_FIRST 5
#define SQLITE_TESTCTRL_PRNG_SAVE 5
#define SQLITE_TESTCTRL_PRNG_RESTORE 6
-#define SQLITE_TESTCTRL_PRNG_RESET 7
+#define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */
#define SQLITE_TESTCTRL_BITVEC_TEST 8
#define SQLITE_TESTCTRL_FAULT_INSTALL 9
#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
@@ -8402,7 +8463,9 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_IMPOSTER 25
#define SQLITE_TESTCTRL_PARSER_COVERAGE 26
#define SQLITE_TESTCTRL_RESULT_INTREAL 27
-#define SQLITE_TESTCTRL_LAST 27 /* Largest TESTCTRL */
+#define SQLITE_TESTCTRL_PRNG_SEED 28
+#define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29
+#define SQLITE_TESTCTRL_LAST 29 /* Largest TESTCTRL */
/*
** CAPI3REF: SQL Keyword Checking
@@ -13099,15 +13162,15 @@ struct fts5_api {
** So we have to define the macros in different ways depending on the
** compiler.
*/
-#if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */
+#if defined(HAVE_STDINT_H) /* Use this case if we have ANSI headers */
+# define SQLITE_INT_TO_PTR(X) ((void*)(intptr_t)(X))
+# define SQLITE_PTR_TO_INT(X) ((int)(intptr_t)(X))
+#elif defined(__PTRDIFF_TYPE__) /* This case should work for GCC */
# define SQLITE_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X))
# define SQLITE_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X))
#elif !defined(__GNUC__) /* Works for compilers other than LLVM */
# define SQLITE_INT_TO_PTR(X) ((void*)&((char*)0)[X])
# define SQLITE_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0))
-#elif defined(HAVE_STDINT_H) /* Use this case if we have ANSI headers */
-# define SQLITE_INT_TO_PTR(X) ((void*)(intptr_t)(X))
-# define SQLITE_PTR_TO_INT(X) ((int)(intptr_t)(X))
#else /* Generates a warning - but it always works */
# define SQLITE_INT_TO_PTR(X) ((void*)(X))
# define SQLITE_PTR_TO_INT(X) ((int)(X))
@@ -13597,100 +13660,103 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
#define TK_VIEW 79
#define TK_VIRTUAL 80
#define TK_WITH 81
-#define TK_CURRENT 82
-#define TK_FOLLOWING 83
-#define TK_PARTITION 84
-#define TK_PRECEDING 85
-#define TK_RANGE 86
-#define TK_UNBOUNDED 87
-#define TK_EXCLUDE 88
-#define TK_GROUPS 89
-#define TK_OTHERS 90
-#define TK_TIES 91
-#define TK_REINDEX 92
-#define TK_RENAME 93
-#define TK_CTIME_KW 94
-#define TK_ANY 95
-#define TK_BITAND 96
-#define TK_BITOR 97
-#define TK_LSHIFT 98
-#define TK_RSHIFT 99
-#define TK_PLUS 100
-#define TK_MINUS 101
-#define TK_STAR 102
-#define TK_SLASH 103
-#define TK_REM 104
-#define TK_CONCAT 105
-#define TK_COLLATE 106
-#define TK_BITNOT 107
-#define TK_ON 108
-#define TK_INDEXED 109
-#define TK_STRING 110
-#define TK_JOIN_KW 111
-#define TK_CONSTRAINT 112
-#define TK_DEFAULT 113
-#define TK_NULL 114
-#define TK_PRIMARY 115
-#define TK_UNIQUE 116
-#define TK_CHECK 117
-#define TK_REFERENCES 118
-#define TK_AUTOINCR 119
-#define TK_INSERT 120
-#define TK_DELETE 121
-#define TK_UPDATE 122
-#define TK_SET 123
-#define TK_DEFERRABLE 124
-#define TK_FOREIGN 125
-#define TK_DROP 126
-#define TK_UNION 127
-#define TK_ALL 128
-#define TK_EXCEPT 129
-#define TK_INTERSECT 130
-#define TK_SELECT 131
-#define TK_VALUES 132
-#define TK_DISTINCT 133
-#define TK_DOT 134
-#define TK_FROM 135
-#define TK_JOIN 136
-#define TK_USING 137
-#define TK_ORDER 138
-#define TK_GROUP 139
-#define TK_HAVING 140
-#define TK_LIMIT 141
-#define TK_WHERE 142
-#define TK_INTO 143
-#define TK_NOTHING 144
-#define TK_FLOAT 145
-#define TK_BLOB 146
-#define TK_INTEGER 147
-#define TK_VARIABLE 148
-#define TK_CASE 149
-#define TK_WHEN 150
-#define TK_THEN 151
-#define TK_ELSE 152
-#define TK_INDEX 153
-#define TK_ALTER 154
-#define TK_ADD 155
-#define TK_WINDOW 156
-#define TK_OVER 157
-#define TK_FILTER 158
-#define TK_TRUEFALSE 159
-#define TK_ISNOT 160
-#define TK_FUNCTION 161
+#define TK_NULLS 82
+#define TK_FIRST 83
+#define TK_LAST 84
+#define TK_CURRENT 85
+#define TK_FOLLOWING 86
+#define TK_PARTITION 87
+#define TK_PRECEDING 88
+#define TK_RANGE 89
+#define TK_UNBOUNDED 90
+#define TK_EXCLUDE 91
+#define TK_GROUPS 92
+#define TK_OTHERS 93
+#define TK_TIES 94
+#define TK_REINDEX 95
+#define TK_RENAME 96
+#define TK_CTIME_KW 97
+#define TK_ANY 98
+#define TK_BITAND 99
+#define TK_BITOR 100
+#define TK_LSHIFT 101
+#define TK_RSHIFT 102
+#define TK_PLUS 103
+#define TK_MINUS 104
+#define TK_STAR 105
+#define TK_SLASH 106
+#define TK_REM 107
+#define TK_CONCAT 108
+#define TK_COLLATE 109
+#define TK_BITNOT 110
+#define TK_ON 111
+#define TK_INDEXED 112
+#define TK_STRING 113
+#define TK_JOIN_KW 114
+#define TK_CONSTRAINT 115
+#define TK_DEFAULT 116
+#define TK_NULL 117
+#define TK_PRIMARY 118
+#define TK_UNIQUE 119
+#define TK_CHECK 120
+#define TK_REFERENCES 121
+#define TK_AUTOINCR 122
+#define TK_INSERT 123
+#define TK_DELETE 124
+#define TK_UPDATE 125
+#define TK_SET 126
+#define TK_DEFERRABLE 127
+#define TK_FOREIGN 128
+#define TK_DROP 129
+#define TK_UNION 130
+#define TK_ALL 131
+#define TK_EXCEPT 132
+#define TK_INTERSECT 133
+#define TK_SELECT 134
+#define TK_VALUES 135
+#define TK_DISTINCT 136
+#define TK_DOT 137
+#define TK_FROM 138
+#define TK_JOIN 139
+#define TK_USING 140
+#define TK_ORDER 141
+#define TK_GROUP 142
+#define TK_HAVING 143
+#define TK_LIMIT 144
+#define TK_WHERE 145
+#define TK_INTO 146
+#define TK_NOTHING 147
+#define TK_FLOAT 148
+#define TK_BLOB 149
+#define TK_INTEGER 150
+#define TK_VARIABLE 151
+#define TK_CASE 152
+#define TK_WHEN 153
+#define TK_THEN 154
+#define TK_ELSE 155
+#define TK_INDEX 156
+#define TK_ALTER 157
+#define TK_ADD 158
+#define TK_WINDOW 159
+#define TK_OVER 160
+#define TK_FILTER 161
#define TK_COLUMN 162
#define TK_AGG_FUNCTION 163
#define TK_AGG_COLUMN 164
-#define TK_UMINUS 165
-#define TK_UPLUS 166
-#define TK_TRUTH 167
-#define TK_REGISTER 168
-#define TK_VECTOR 169
-#define TK_SELECT_COLUMN 170
-#define TK_IF_NULL_ROW 171
-#define TK_ASTERISK 172
-#define TK_SPAN 173
-#define TK_SPACE 174
-#define TK_ILLEGAL 175
+#define TK_TRUEFALSE 165
+#define TK_ISNOT 166
+#define TK_FUNCTION 167
+#define TK_UMINUS 168
+#define TK_UPLUS 169
+#define TK_TRUTH 170
+#define TK_REGISTER 171
+#define TK_VECTOR 172
+#define TK_SELECT_COLUMN 173
+#define TK_IF_NULL_ROW 174
+#define TK_ASTERISK 175
+#define TK_SPAN 176
+#define TK_SPACE 177
+#define TK_ILLEGAL 178
/************** End of parse.h ***********************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
@@ -14102,20 +14168,6 @@ typedef INT16_TYPE LogEst;
#endif
/*
-** Only one of SQLITE_ENABLE_STAT3 or SQLITE_ENABLE_STAT4 can be defined.
-** Priority is given to SQLITE_ENABLE_STAT4. If either are defined, also
-** define SQLITE_ENABLE_STAT3_OR_STAT4
-*/
-#ifdef SQLITE_ENABLE_STAT4
-# undef SQLITE_ENABLE_STAT3
-# define SQLITE_ENABLE_STAT3_OR_STAT4 1
-#elif SQLITE_ENABLE_STAT3
-# define SQLITE_ENABLE_STAT3_OR_STAT4 1
-#elif SQLITE_ENABLE_STAT3_OR_STAT4
-# undef SQLITE_ENABLE_STAT3_OR_STAT4
-#endif
-
-/*
** SELECTTRACE_ENABLED will be either 1 or 0 depending on whether or not
** the Select query generator tracing logic is turned on.
*/
@@ -14984,24 +15036,24 @@ typedef struct VdbeOpList VdbeOpList;
#define OP_Count 93 /* synopsis: r[P2]=count() */
#define OP_ReadCookie 94
#define OP_SetCookie 95
-#define OP_BitAnd 96 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */
-#define OP_BitOr 97 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */
-#define OP_ShiftLeft 98 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<<r[P1] */
-#define OP_ShiftRight 99 /* same as TK_RSHIFT, synopsis: r[P3]=r[P2]>>r[P1] */
-#define OP_Add 100 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */
-#define OP_Subtract 101 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */
-#define OP_Multiply 102 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */
-#define OP_Divide 103 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */
-#define OP_Remainder 104 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */
-#define OP_Concat 105 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */
-#define OP_ReopenIdx 106 /* synopsis: root=P2 iDb=P3 */
-#define OP_BitNot 107 /* same as TK_BITNOT, synopsis: r[P2]= ~r[P1] */
-#define OP_OpenRead 108 /* synopsis: root=P2 iDb=P3 */
-#define OP_OpenWrite 109 /* synopsis: root=P2 iDb=P3 */
-#define OP_String8 110 /* same as TK_STRING, synopsis: r[P2]='P4' */
-#define OP_OpenDup 111
-#define OP_OpenAutoindex 112 /* synopsis: nColumn=P2 */
-#define OP_OpenEphemeral 113 /* synopsis: nColumn=P2 */
+#define OP_ReopenIdx 96 /* synopsis: root=P2 iDb=P3 */
+#define OP_OpenRead 97 /* synopsis: root=P2 iDb=P3 */
+#define OP_OpenWrite 98 /* synopsis: root=P2 iDb=P3 */
+#define OP_BitAnd 99 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */
+#define OP_BitOr 100 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */
+#define OP_ShiftLeft 101 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<<r[P1] */
+#define OP_ShiftRight 102 /* same as TK_RSHIFT, synopsis: r[P3]=r[P2]>>r[P1] */
+#define OP_Add 103 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */
+#define OP_Subtract 104 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */
+#define OP_Multiply 105 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */
+#define OP_Divide 106 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */
+#define OP_Remainder 107 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */
+#define OP_Concat 108 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */
+#define OP_OpenDup 109
+#define OP_BitNot 110 /* same as TK_BITNOT, synopsis: r[P2]= ~r[P1] */
+#define OP_OpenAutoindex 111 /* synopsis: nColumn=P2 */
+#define OP_OpenEphemeral 112 /* synopsis: nColumn=P2 */
+#define OP_String8 113 /* same as TK_STRING, synopsis: r[P2]='P4' */
#define OP_SorterOpen 114
#define OP_SequenceTest 115 /* synopsis: if( cursor[P1].ctr++ ) pc = P2 */
#define OP_OpenPseudo 116 /* synopsis: P3 columns in r[P2] */
@@ -15033,10 +15085,10 @@ typedef struct VdbeOpList VdbeOpList;
#define OP_LoadAnalysis 142
#define OP_DropTable 143
#define OP_DropIndex 144
-#define OP_Real 145 /* same as TK_FLOAT, synopsis: r[P2]=P4 */
-#define OP_DropTrigger 146
-#define OP_IntegrityCk 147
-#define OP_RowSetAdd 148 /* synopsis: rowset(P1)=r[P2] */
+#define OP_DropTrigger 145
+#define OP_IntegrityCk 146
+#define OP_RowSetAdd 147 /* synopsis: rowset(P1)=r[P2] */
+#define OP_Real 148 /* same as TK_FLOAT, synopsis: r[P2]=P4 */
#define OP_Param 149
#define OP_FkCounter 150 /* synopsis: fkctr[P1]+=P2 */
#define OP_MemMax 151 /* synopsis: r[P1]=max(r[P1],r[P2]) */
@@ -15085,13 +15137,13 @@ typedef struct VdbeOpList VdbeOpList;
/* 72 */ 0x10, 0x10, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10,\
/* 80 */ 0x10, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00,\
/* 88 */ 0x12, 0x20, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00,\
-/* 96 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26,\
-/* 104 */ 0x26, 0x26, 0x00, 0x12, 0x00, 0x00, 0x10, 0x00,\
-/* 112 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+/* 96 */ 0x00, 0x00, 0x00, 0x26, 0x26, 0x26, 0x26, 0x26,\
+/* 104 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x00, 0x12, 0x00,\
+/* 112 */ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
/* 120 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
/* 128 */ 0x10, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x10,\
/* 136 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\
-/* 144 */ 0x00, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00, 0x04,\
+/* 144 */ 0x00, 0x00, 0x00, 0x06, 0x10, 0x10, 0x00, 0x04,\
/* 152 */ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
/* 160 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10,\
/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00,}
@@ -15161,10 +15213,10 @@ SQLITE_PRIVATE void sqlite3ExplainBreakpoint(const char*,const char*);
# define sqlite3ExplainBreakpoint(A,B) /*no-op*/
#endif
SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*);
-SQLITE_PRIVATE void sqlite3VdbeChangeOpcode(Vdbe*, u32 addr, u8);
-SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, u32 addr, int P1);
-SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, u32 addr, int P2);
-SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, u32 addr, int P3);
+SQLITE_PRIVATE void sqlite3VdbeChangeOpcode(Vdbe*, int addr, u8);
+SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, int addr, int P1);
+SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2);
+SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, int addr, int P3);
SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe*, u16 P5);
SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe*, int addr);
SQLITE_PRIVATE int sqlite3VdbeChangeToNoop(Vdbe*, int addr);
@@ -16121,6 +16173,7 @@ SQLITE_PRIVATE void sqlite3OsCloseFree(sqlite3_file *);
#define MUTEX_LOGIC(X)
#else
#define MUTEX_LOGIC(X) X
+SQLITE_API int sqlite3_mutex_held(sqlite3_mutex*);
#endif /* defined(SQLITE_MUTEX_OMIT) */
/************** End of mutex.h ***********************************************/
@@ -16379,6 +16432,7 @@ struct sqlite3 {
unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */
unsigned imposterTable : 1; /* Building an imposter table */
unsigned reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */
+ char **azInit; /* "type", "name", and "tbl_name" columns */
} init;
int nVdbeActive; /* Number of VDBEs currently running */
int nVdbeRead; /* Number of active VDBEs that read or write */
@@ -16517,16 +16571,17 @@ struct sqlite3 {
#define SQLITE_Defensive 0x10000000 /* Input SQL is likely hostile */
#define SQLITE_DqsDDL 0x20000000 /* dbl-quoted strings allowed in DDL*/
#define SQLITE_DqsDML 0x40000000 /* dbl-quoted strings allowed in DML*/
+#define SQLITE_EnableView 0x80000000 /* Enable the use of views */
/* Flags used only if debugging */
#define HI(X) ((u64)(X)<<32)
#ifdef SQLITE_DEBUG
-#define SQLITE_SqlTrace HI(0x0001) /* Debug print SQL as it executes */
-#define SQLITE_VdbeListing HI(0x0002) /* Debug listings of VDBE progs */
-#define SQLITE_VdbeTrace HI(0x0004) /* True to trace VDBE execution */
-#define SQLITE_VdbeAddopTrace HI(0x0008) /* Trace sqlite3VdbeAddOp() calls */
-#define SQLITE_VdbeEQP HI(0x0010) /* Debug EXPLAIN QUERY PLAN */
-#define SQLITE_ParserTrace HI(0x0020) /* PRAGMA parser_trace=ON */
+#define SQLITE_SqlTrace HI(0x0100000) /* Debug print SQL as it executes */
+#define SQLITE_VdbeListing HI(0x0200000) /* Debug listings of VDBE progs */
+#define SQLITE_VdbeTrace HI(0x0400000) /* True to trace VDBE execution */
+#define SQLITE_VdbeAddopTrace HI(0x0800000) /* Trace sqlite3VdbeAddOp() calls */
+#define SQLITE_VdbeEQP HI(0x1000000) /* Debug EXPLAIN QUERY PLAN */
+#define SQLITE_ParserTrace HI(0x2000000) /* PRAGMA parser_trace=ON */
#endif
/*
@@ -16554,8 +16609,8 @@ struct sqlite3 {
#define SQLITE_OmitNoopJoin 0x0100 /* Omit unused tables in joins */
#define SQLITE_CountOfView 0x0200 /* The count-of-view optimization */
#define SQLITE_CursorHints 0x0400 /* Add OP_CursorHint opcodes */
-#define SQLITE_Stat34 0x0800 /* Use STAT3 or STAT4 data */
- /* TH3 expects the Stat34 ^^^^^^ value to be 0x0800. Don't change it */
+#define SQLITE_Stat4 0x0800 /* Use STAT4 data */
+ /* TH3 expects the Stat4 ^^^^^^ value to be 0x0800. Don't change it */
#define SQLITE_PushDown 0x1000 /* The push-down optimization */
#define SQLITE_SimplifyJoin 0x2000 /* Convert LEFT JOIN to JOIN */
#define SQLITE_SkipScan 0x4000 /* Skip-scans */
@@ -16643,6 +16698,7 @@ struct FuncDestructor {
** SQLITE_FUNC_LENGTH == OPFLAG_LENGTHARG
** SQLITE_FUNC_TYPEOF == OPFLAG_TYPEOFARG
** SQLITE_FUNC_CONSTANT == SQLITE_DETERMINISTIC from the API
+** SQLITE_FUNC_DIRECT == SQLITE_DIRECTONLY from the API
** SQLITE_FUNC_ENCMASK depends on SQLITE_UTF* macros in the API
*/
#define SQLITE_FUNC_ENCMASK 0x0003 /* SQLITE_UTF8, SQLITE_UTF16BE or UTF16LE */
@@ -16663,6 +16719,8 @@ struct FuncDestructor {
#define SQLITE_FUNC_OFFSET 0x8000 /* Built-in sqlite_offset() function */
#define SQLITE_FUNC_WINDOW 0x00010000 /* Built-in window-only function */
#define SQLITE_FUNC_INTERNAL 0x00040000 /* For use by NestedParse() only */
+#define SQLITE_FUNC_DIRECT 0x00080000 /* Not for use in TRIGGERs or VIEWs */
+#define SQLITE_FUNC_SUBTYPE 0x00100000 /* Result likely to have sub-type */
/*
** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are
@@ -16776,6 +16834,7 @@ struct Savepoint {
struct Module {
const sqlite3_module *pModule; /* Callback pointers */
const char *zName; /* Name passed to create_module() */
+ int nRefModule; /* Number of pointers to this object */
void *pAux; /* pAux passed to create_module() */
void (*xDestroy)(void *); /* Module destructor function */
Table *pEpoTab; /* Eponymous table for this module */
@@ -16841,11 +16900,12 @@ struct CollSeq {
** Note also that the numeric types are grouped together so that testing
** for a numeric type is a single comparison. And the BLOB type is first.
*/
-#define SQLITE_AFF_BLOB 'A'
-#define SQLITE_AFF_TEXT 'B'
-#define SQLITE_AFF_NUMERIC 'C'
-#define SQLITE_AFF_INTEGER 'D'
-#define SQLITE_AFF_REAL 'E'
+#define SQLITE_AFF_NONE 0x40 /* '@' */
+#define SQLITE_AFF_BLOB 0x41 /* 'A' */
+#define SQLITE_AFF_TEXT 0x42 /* 'B' */
+#define SQLITE_AFF_NUMERIC 0x43 /* 'C' */
+#define SQLITE_AFF_INTEGER 0x44 /* 'D' */
+#define SQLITE_AFF_REAL 0x45 /* 'E' */
#define sqlite3IsNumericAffinity(X) ((X)>=SQLITE_AFF_NUMERIC)
@@ -17113,11 +17173,17 @@ struct KeyInfo {
u16 nKeyField; /* Number of key columns in the index */
u16 nAllField; /* Total columns, including key plus others */
sqlite3 *db; /* The database connection */
- u8 *aSortOrder; /* Sort order for each column. */
+ u8 *aSortFlags; /* Sort order for each column. */
CollSeq *aColl[1]; /* Collating sequence for each term of the key */
};
/*
+** Allowed bit values for entries in the KeyInfo.aSortFlags[] array.
+*/
+#define KEYINFO_ORDER_DESC 0x01 /* DESC sort order */
+#define KEYINFO_ORDER_BIGNULL 0x02 /* NULL is larger than any other value */
+
+/*
** This object holds a record which has been parsed out into individual
** fields, for the purposes of doing a comparison.
**
@@ -17224,7 +17290,7 @@ struct Index {
unsigned hasStat1:1; /* aiRowLogEst values come from sqlite_stat1 */
unsigned bNoQuery:1; /* Do not use this index to optimize queries */
unsigned bAscKeyBug:1; /* True if the bba7b69f9849b5bf bug applies */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
int nSample; /* Number of elements in aSample[] */
int nSampleCol; /* Size of IndexSample.anEq[] and so on */
tRowcnt *aAvgEq; /* Average nEq values for keys not in aSample */
@@ -17256,7 +17322,7 @@ struct Index {
#define XN_EXPR (-2) /* Indexed column is an expression */
/*
-** Each sample stored in the sqlite_stat3 table is represented in memory
+** Each sample stored in the sqlite_stat4 table is represented in memory
** using a structure of this type. See documentation at the top of the
** analyze.c source file for additional information.
*/
@@ -17414,7 +17480,7 @@ typedef int ynVar;
*/
struct Expr {
u8 op; /* Operation performed by this node */
- char affinity; /* The affinity of the column or 0 if not a column */
+ char affExpr; /* affinity, or RAISE type */
u32 flags; /* Various flags. EP_* See below */
union {
char *zToken; /* Token value. Zero terminated and dequoted */
@@ -17445,6 +17511,8 @@ struct Expr {
** TK_REGISTER: register number
** TK_TRIGGER: 1 -> new, 0 -> old
** EP_Unlikely: 134217728 times likelihood
+ ** TK_IN: ephemerial table holding RHS
+ ** TK_SELECT_COLUMN: Number of columns on the LHS
** TK_SELECT: 1st register of result vector */
ynVar iColumn; /* TK_COLUMN: column index. -1 for rowid.
** TK_VARIABLE: variable number (always >= 1).
@@ -17458,7 +17526,7 @@ struct Expr {
union {
Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL
** for a column of an index on an expression */
- Window *pWin; /* TK_FUNCTION: Window definition for the func */
+ Window *pWin; /* EP_WinFunc: Window/Filter defn for a function */
struct { /* TK_IN, TK_SELECT, and TK_EXISTS */
int iAddr; /* Subroutine entry address */
int regReturn; /* Register used to hold return address */
@@ -17473,36 +17541,37 @@ struct Expr {
** EP_Agg == NC_HasAgg == SF_HasAgg
** EP_Win == NC_HasWin
*/
-#define EP_FromJoin 0x000001 /* Originates in ON/USING clause of outer join */
-#define EP_Distinct 0x000002 /* Aggregate function with DISTINCT keyword */
-#define EP_HasFunc 0x000004 /* Contains one or more functions of any kind */
-#define EP_FixedCol 0x000008 /* TK_Column with a known fixed value */
-#define EP_Agg 0x000010 /* Contains one or more aggregate functions */
-#define EP_VarSelect 0x000020 /* pSelect is correlated, not constant */
-#define EP_DblQuoted 0x000040 /* token.z was originally in "..." */
-#define EP_InfixFunc 0x000080 /* True for an infix function: LIKE, GLOB, etc */
-#define EP_Collate 0x000100 /* Tree contains a TK_COLLATE operator */
-#define EP_Generic 0x000200 /* Ignore COLLATE or affinity on this tree */
-#define EP_IntValue 0x000400 /* Integer value contained in u.iValue */
-#define EP_xIsSelect 0x000800 /* x.pSelect is valid (otherwise x.pList is) */
-#define EP_Skip 0x001000 /* Operator does not contribute to affinity */
-#define EP_Reduced 0x002000 /* Expr struct EXPR_REDUCEDSIZE bytes only */
-#define EP_TokenOnly 0x004000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */
-#define EP_Win 0x008000 /* Contains window functions */
-#define EP_MemToken 0x010000 /* Need to sqlite3DbFree() Expr.zToken */
-#define EP_NoReduce 0x020000 /* Cannot EXPRDUP_REDUCE this Expr */
-#define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */
-#define EP_ConstFunc 0x080000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */
-#define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */
-#define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */
-#define EP_Alias 0x400000 /* Is an alias for a result set column */
-#define EP_Leaf 0x800000 /* Expr.pLeft, .pRight, .u.pSelect all NULL */
-#define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */
-#define EP_Subrtn 0x2000000 /* Uses Expr.y.sub. TK_IN, _SELECT, or _EXISTS */
-#define EP_Quoted 0x4000000 /* TK_ID was originally quoted */
-#define EP_Static 0x8000000 /* Held in memory not obtained from malloc() */
-#define EP_IsTrue 0x10000000 /* Always has boolean value of TRUE */
-#define EP_IsFalse 0x20000000 /* Always has boolean value of FALSE */
+#define EP_FromJoin 0x000001 /* Originates in ON/USING clause of outer join */
+#define EP_Distinct 0x000002 /* Aggregate function with DISTINCT keyword */
+#define EP_HasFunc 0x000004 /* Contains one or more functions of any kind */
+#define EP_FixedCol 0x000008 /* TK_Column with a known fixed value */
+#define EP_Agg 0x000010 /* Contains one or more aggregate functions */
+#define EP_VarSelect 0x000020 /* pSelect is correlated, not constant */
+#define EP_DblQuoted 0x000040 /* token.z was originally in "..." */
+#define EP_InfixFunc 0x000080 /* True for an infix function: LIKE, GLOB, etc */
+#define EP_Collate 0x000100 /* Tree contains a TK_COLLATE operator */
+ /* 0x000200 Available for reuse */
+#define EP_IntValue 0x000400 /* Integer value contained in u.iValue */
+#define EP_xIsSelect 0x000800 /* x.pSelect is valid (otherwise x.pList is) */
+#define EP_Skip 0x001000 /* Operator does not contribute to affinity */
+#define EP_Reduced 0x002000 /* Expr struct EXPR_REDUCEDSIZE bytes only */
+#define EP_TokenOnly 0x004000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */
+#define EP_Win 0x008000 /* Contains window functions */
+#define EP_MemToken 0x010000 /* Need to sqlite3DbFree() Expr.zToken */
+#define EP_NoReduce 0x020000 /* Cannot EXPRDUP_REDUCE this Expr */
+#define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */
+#define EP_ConstFunc 0x080000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */
+#define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */
+#define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */
+#define EP_Alias 0x400000 /* Is an alias for a result set column */
+#define EP_Leaf 0x800000 /* Expr.pLeft, .pRight, .u.pSelect all NULL */
+#define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */
+#define EP_Subrtn 0x2000000 /* Uses Expr.y.sub. TK_IN, _SELECT, or _EXISTS */
+#define EP_Quoted 0x4000000 /* TK_ID was originally quoted */
+#define EP_Static 0x8000000 /* Held in memory not obtained from malloc() */
+#define EP_IsTrue 0x10000000 /* Always has boolean value of TRUE */
+#define EP_IsFalse 0x20000000 /* Always has boolean value of FALSE */
+#define EP_Indirect 0x40000000 /* Contained within a TRIGGER or a VIEW */
/*
** The EP_Propagate mask is a set of properties that automatically propagate
@@ -17547,6 +17616,18 @@ struct Expr {
#define EXPRDUP_REDUCE 0x0001 /* Used reduced-size Expr nodes */
/*
+** True if the expression passed as an argument was a function with
+** an OVER() clause (a window function).
+*/
+#ifdef SQLITE_OMIT_WINDOWFUNC
+# define IsWindowFunc(p) 0
+#else
+# define IsWindowFunc(p) ( \
+ ExprHasProperty((p), EP_WinFunc) && p->y.pWin->eFrmType!=TK_FILTER \
+ )
+#endif
+
+/*
** A list of expressions. Each expression may optionally have a
** name. An expr/name combination can be used in several ways, such
** as the list of "expr AS ID" fields following a "SELECT" or in the
@@ -17568,11 +17649,12 @@ struct ExprList {
Expr *pExpr; /* The parse tree for this expression */
char *zName; /* Token associated with this expression */
char *zSpan; /* Original text of the expression */
- u8 sortOrder; /* 1 for DESC or 0 for ASC */
+ u8 sortFlags; /* Mask of KEYINFO_ORDER_* flags */
unsigned done :1; /* A flag to indicate when processing is finished */
unsigned bSpanIsTab :1; /* zSpan holds DB.TABLE.COLUMN */
unsigned reusable :1; /* Constant expression is reusable */
unsigned bSorterRef :1; /* Defer evaluation until after sorting */
+ unsigned bNulls: 1; /* True if explicit "NULLS FIRST/LAST" */
union {
struct {
u16 iOrderByCol; /* For ORDER BY, column number in result set */
@@ -17863,6 +17945,7 @@ struct Select {
#define SF_Converted 0x10000 /* By convertCompoundSelectToSubquery() */
#define SF_IncludeHidden 0x20000 /* Include hidden columns in output */
#define SF_ComplexResult 0x40000 /* Result contains subquery or function */
+#define SF_WhereBegin 0x80000 /* Really a WhereBegin() call. Debug Only */
/*
** The results of a SELECT can be distributed in several ways, as defined
@@ -18367,11 +18450,12 @@ typedef struct {
*/
struct Sqlite3Config {
int bMemstat; /* True to enable memory status */
- int bCoreMutex; /* True to enable core mutexing */
- int bFullMutex; /* True to enable full mutexing */
- int bOpenUri; /* True to interpret filenames as URIs */
- int bUseCis; /* Use covering indices for full-scans */
- int bSmallMalloc; /* Avoid large memory allocations if true */
+ u8 bCoreMutex; /* True to enable core mutexing */
+ u8 bFullMutex; /* True to enable full mutexing */
+ u8 bOpenUri; /* True to interpret filenames as URIs */
+ u8 bUseCis; /* Use covering indices for full-scans */
+ u8 bSmallMalloc; /* Avoid large memory allocations if true */
+ u8 bExtraSchemaChecks; /* Verify type,name,tbl_name in schema */
int mxStrlen; /* Maximum string length */
int neverCorrupt; /* Database is always well-formed */
int szLookaside; /* Default lookaside buffer size */
@@ -18423,6 +18507,7 @@ struct Sqlite3Config {
int bInternalFunctions; /* Internal SQL functions are visible */
int iOnceResetThreshold; /* When to reset OP_Once counters */
u32 szSorterRef; /* Min size in bytes to use sorter-refs */
+ unsigned int iPrngSeed; /* Alternative fixed seed for the PRNG */
};
/*
@@ -18519,10 +18604,11 @@ struct TreeView {
#endif /* SQLITE_DEBUG */
/*
-** This object is used in various ways, all related to window functions
+** This object is used in various ways, most (but not all) related to window
+** functions.
**
** (1) A single instance of this structure is attached to the
-** the Expr.pWin field for each window function in an expression tree.
+** the Expr.y.pWin field for each window function in an expression tree.
** This object holds the information contained in the OVER clause,
** plus additional fields used during code generation.
**
@@ -18533,6 +18619,10 @@ struct TreeView {
** (3) The terms of the WINDOW clause of a SELECT are instances of this
** object on a linked list attached to Select.pWinDefn.
**
+** (4) For an aggregate function with a FILTER clause, an instance
+** of this object is stored in Expr.y.pWin with eFrmType set to
+** TK_FILTER. In this case the only field used is Window.pFilter.
+**
** The uses (1) and (2) are really the same Window object that just happens
** to be accessible in two different ways. Use case (3) are separate objects.
*/
@@ -18548,12 +18638,13 @@ struct Window {
u8 eExclude; /* TK_NO, TK_CURRENT, TK_TIES, TK_GROUP, or 0 */
Expr *pStart; /* Expression for "<expr> PRECEDING" */
Expr *pEnd; /* Expression for "<expr> FOLLOWING" */
+ Window **ppThis; /* Pointer to this object in Select.pWin list */
Window *pNextWin; /* Next window function belonging to this SELECT */
Expr *pFilter; /* The FILTER expression */
FuncDef *pFunc; /* The function */
int iEphCsr; /* Partition buffer or Peer buffer */
- int regAccum;
- int regResult;
+ int regAccum; /* Accumulator */
+ int regResult; /* Interim result */
int csrApp; /* Function cursor (used by min/max) */
int regApp; /* Function register (also used by min/max) */
int regPart; /* Array of registers for PARTITION BY values */
@@ -18563,14 +18654,18 @@ struct Window {
int regOne; /* Register containing constant value 1 */
int regStartRowid;
int regEndRowid;
+ u8 bExprArgs; /* Defer evaluation of window function arguments
+ ** due to the SQLITE_SUBTYPE flag */
};
#ifndef SQLITE_OMIT_WINDOWFUNC
SQLITE_PRIVATE void sqlite3WindowDelete(sqlite3*, Window*);
+SQLITE_PRIVATE void sqlite3WindowUnlinkFromSelect(Window*);
SQLITE_PRIVATE void sqlite3WindowListDelete(sqlite3 *db, Window *p);
SQLITE_PRIVATE Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*, u8);
SQLITE_PRIVATE void sqlite3WindowAttach(Parse*, Expr*, Window*);
-SQLITE_PRIVATE int sqlite3WindowCompare(Parse*, Window*, Window*);
+SQLITE_PRIVATE void sqlite3WindowLink(Select *pSel, Window *pWin);
+SQLITE_PRIVATE int sqlite3WindowCompare(Parse*, Window*, Window*, int);
SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse*, Window*);
SQLITE_PRIVATE void sqlite3WindowCodeStep(Parse*, Select*, WhereInfo*, int, int);
SQLITE_PRIVATE int sqlite3WindowRewrite(Parse*, Select*);
@@ -18842,7 +18937,7 @@ SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3*, Expr*);
SQLITE_PRIVATE void sqlite3ExprUnmapAndDelete(Parse*, Expr*);
SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*);
SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*);
-SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList*,int);
+SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList*,int,int);
SQLITE_PRIVATE void sqlite3ExprListSetName(Parse*,ExprList*,Token*,int);
SQLITE_PRIVATE void sqlite3ExprListSetSpan(Parse*,ExprList*,const char*,const char*);
SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3*, ExprList*);
@@ -18861,8 +18956,8 @@ SQLITE_PRIVATE void sqlite3CollapseDatabaseArray(sqlite3*);
SQLITE_PRIVATE void sqlite3CommitInternalChanges(sqlite3*);
SQLITE_PRIVATE void sqlite3DeleteColumnNames(sqlite3*,Table*);
SQLITE_PRIVATE int sqlite3ColumnsFromExprList(Parse*,ExprList*,i16*,Column**);
-SQLITE_PRIVATE void sqlite3SelectAddColumnTypeAndCollation(Parse*,Table*,Select*);
-SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*);
+SQLITE_PRIVATE void sqlite3SelectAddColumnTypeAndCollation(Parse*,Table*,Select*,char);
+SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*,char);
SQLITE_PRIVATE void sqlite3OpenMasterTable(Parse *, int);
SQLITE_PRIVATE Index *sqlite3PrimaryKeyIndex(Table*);
SQLITE_PRIVATE i16 sqlite3ColumnOfIndex(Index*, i16);
@@ -19163,7 +19258,7 @@ SQLITE_PRIVATE LogEst sqlite3LogEstAdd(LogEst,LogEst);
SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double);
#endif
#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \
- defined(SQLITE_ENABLE_STAT3_OR_STAT4) || \
+ defined(SQLITE_ENABLE_STAT4) || \
defined(SQLITE_EXPLAIN_ESTIMATED_ROWS)
SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst);
#endif
@@ -19229,9 +19324,10 @@ SQLITE_PRIVATE int sqlite3ExprCollSeqMatch(Parse*,Expr*,Expr*);
SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr*, const Token*, int);
SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse*,Expr*,const char*);
SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr*);
+SQLITE_PRIVATE Expr *sqlite3ExprSkipCollateAndLikely(Expr*);
SQLITE_PRIVATE int sqlite3CheckCollSeq(Parse *, CollSeq *);
SQLITE_PRIVATE int sqlite3WritableSchema(sqlite3*);
-SQLITE_PRIVATE int sqlite3CheckObjectName(Parse *, const char *);
+SQLITE_PRIVATE int sqlite3CheckObjectName(Parse*, const char*,const char*,const char*);
SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *, int);
SQLITE_PRIVATE int sqlite3AddInt64(i64*,i64);
SQLITE_PRIVATE int sqlite3SubInt64(i64*,i64);
@@ -19264,7 +19360,6 @@ SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[];
SQLITE_PRIVATE const char sqlite3StrBINARY[];
SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[];
SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[];
-SQLITE_PRIVATE const Token sqlite3IntTokens[];
SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config;
SQLITE_PRIVATE FuncDefHash sqlite3BuiltinFunctions;
#ifndef SQLITE_OMIT_WSD
@@ -19318,6 +19413,7 @@ SQLITE_PRIVATE void sqlite3KeyInfoUnref(KeyInfo*);
SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoRef(KeyInfo*);
SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*);
SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoFromExprList(Parse*, ExprList*, int, int);
+SQLITE_PRIVATE int sqlite3HasExplicitNulls(Parse*, ExprList*);
#ifdef SQLITE_DEBUG
SQLITE_PRIVATE int sqlite3KeyInfoIsWriteable(KeyInfo*);
@@ -19350,8 +19446,7 @@ SQLITE_PRIVATE int sqlite3ExprCheckIN(Parse*, Expr*);
# define sqlite3ExprCheckIN(x,y) SQLITE_OK
#endif
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
-SQLITE_PRIVATE void sqlite3AnalyzeFunctions(void);
+#ifdef SQLITE_ENABLE_STAT4
SQLITE_PRIVATE int sqlite3Stat4ProbeSetValue(
Parse*,Index*,UnpackedRecord**,Expr*,int,int,int*);
SQLITE_PRIVATE int sqlite3Stat4ValueFromExpr(Parse*, Expr*, u8, sqlite3_value**);
@@ -19398,6 +19493,7 @@ SQLITE_PRIVATE int sqlite3Utf8To8(unsigned char*);
# define sqlite3VtabInSync(db) 0
# define sqlite3VtabLock(X)
# define sqlite3VtabUnlock(X)
+# define sqlite3VtabModuleUnref(D,X)
# define sqlite3VtabUnlockList(X)
# define sqlite3VtabSavepoint(X, Y, Z) SQLITE_OK
# define sqlite3GetVTable(X,Y) ((VTable*)0)
@@ -19409,6 +19505,7 @@ SQLITE_PRIVATE int sqlite3VtabRollback(sqlite3 *db);
SQLITE_PRIVATE int sqlite3VtabCommit(sqlite3 *db);
SQLITE_PRIVATE void sqlite3VtabLock(VTable *);
SQLITE_PRIVATE void sqlite3VtabUnlock(VTable *);
+SQLITE_PRIVATE void sqlite3VtabModuleUnref(sqlite3*,Module*);
SQLITE_PRIVATE void sqlite3VtabUnlockList(sqlite3*);
SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *, int, int);
SQLITE_PRIVATE void sqlite3VtabImportErrmsg(Vdbe*, sqlite3_vtab*);
@@ -19876,6 +19973,7 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = {
SQLITE_USE_URI, /* bOpenUri */
SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */
0, /* bSmallMalloc */
+ 1, /* bExtraSchemaChecks */
0x7ffffffe, /* mxStrlen */
0, /* neverCorrupt */
SQLITE_DEFAULT_LOOKASIDE, /* szLookaside, nLookaside */
@@ -19922,6 +20020,7 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = {
0, /* bInternalFunctions */
0x7ffffffe, /* iOnceResetThreshold */
SQLITE_DEFAULT_SORTERREF_SIZE, /* szSorterRef */
+ 0, /* iPrngSeed */
};
/*
@@ -19931,14 +20030,6 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = {
*/
SQLITE_PRIVATE FuncDefHash sqlite3BuiltinFunctions;
-/*
-** Constant tokens for values 0 and 1.
-*/
-SQLITE_PRIVATE const Token sqlite3IntTokens[] = {
- { "0", 1 },
- { "1", 1 }
-};
-
#ifdef VDBE_PROFILE
/*
** The following performance counter can be used in place of
@@ -20491,7 +20582,6 @@ SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor**, int*);
SQLITE_PRIVATE int sqlite3VdbeCursorRestore(VdbeCursor*);
SQLITE_PRIVATE u32 sqlite3VdbeSerialTypeLen(u32);
SQLITE_PRIVATE u8 sqlite3VdbeOneByteSerialTypeLen(u8);
-SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem*, int, u32*);
SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(unsigned char*, Mem*, u32);
SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*);
SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(sqlite3*, AuxData**, int, int);
@@ -22497,7 +22587,15 @@ SQLITE_PRIVATE void sqlite3OsDlClose(sqlite3_vfs *pVfs, void *pHandle){
}
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
SQLITE_PRIVATE int sqlite3OsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
- return pVfs->xRandomness(pVfs, nByte, zBufOut);
+ if( sqlite3Config.iPrngSeed ){
+ memset(zBufOut, 0, nByte);
+ if( ALWAYS(nByte>(signed)sizeof(unsigned)) ) nByte = sizeof(unsigned int);
+ memcpy(zBufOut, &sqlite3Config.iPrngSeed, nByte);
+ return SQLITE_OK;
+ }else{
+ return pVfs->xRandomness(pVfs, nByte, zBufOut);
+ }
+
}
SQLITE_PRIVATE int sqlite3OsSleep(sqlite3_vfs *pVfs, int nMicro){
return pVfs->xSleep(pVfs, nMicro);
@@ -28778,13 +28876,17 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m
sqlite3TreeViewPush(pView, 1);
}
do{
- sqlite3TreeViewLine(pView,
- "SELECT%s%s (%u/%p) selFlags=0x%x nSelectRow=%d",
- ((p->selFlags & SF_Distinct) ? " DISTINCT" : ""),
- ((p->selFlags & SF_Aggregate) ? " agg_flag" : ""),
- p->selId, p, p->selFlags,
- (int)p->nSelectRow
- );
+ if( p->selFlags & SF_WhereBegin ){
+ sqlite3TreeViewLine(pView, "sqlite3WhereBegin()");
+ }else{
+ sqlite3TreeViewLine(pView,
+ "SELECT%s%s (%u/%p) selFlags=0x%x nSelectRow=%d",
+ ((p->selFlags & SF_Distinct) ? " DISTINCT" : ""),
+ ((p->selFlags & SF_Aggregate) ? " agg_flag" : ""),
+ p->selId, p, p->selFlags,
+ (int)p->nSelectRow
+ );
+ }
if( cnt++ ) sqlite3TreeViewPop(pView);
if( p->pPrior ){
n = 1000;
@@ -28801,7 +28903,10 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m
if( p->pWinDefn ) n++;
#endif
}
- sqlite3TreeViewExprList(pView, p->pEList, (n--)>0, "result-set");
+ if( p->pEList ){
+ sqlite3TreeViewExprList(pView, p->pEList, n>0, "result-set");
+ }
+ n--;
#ifndef SQLITE_OMIT_WINDOWFUNC
if( p->pWin ){
Window *pX;
@@ -28997,12 +29102,14 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
sqlite3TreeViewPop(pView);
return;
}
- if( pExpr->flags ){
+ if( pExpr->flags || pExpr->affExpr ){
if( ExprHasProperty(pExpr, EP_FromJoin) ){
- sqlite3_snprintf(sizeof(zFlgs),zFlgs," flags=0x%x iRJT=%d",
- pExpr->flags, pExpr->iRightJoinTable);
+ sqlite3_snprintf(sizeof(zFlgs),zFlgs," fg.af=%x.%c iRJT=%d",
+ pExpr->flags, pExpr->affExpr ? pExpr->affExpr : 'n',
+ pExpr->iRightJoinTable);
}else{
- sqlite3_snprintf(sizeof(zFlgs),zFlgs," flags=0x%x",pExpr->flags);
+ sqlite3_snprintf(sizeof(zFlgs),zFlgs," fg.af=%x.%c",
+ pExpr->flags, pExpr->affExpr ? pExpr->affExpr : 'n');
}
}else{
zFlgs[0] = 0;
@@ -29129,7 +29236,14 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
}
case TK_COLLATE: {
- sqlite3TreeViewLine(pView, "COLLATE %Q", pExpr->u.zToken);
+ /* COLLATE operators without the EP_Collate flag are intended to
+ ** emulate collation associated with a table column. These show
+ ** up in the treeview output as "SOFT-COLLATE". Explicit COLLATE
+ ** operators that appear in the original SQL always have the
+ ** EP_Collate bit set and appear in treeview output as just "COLLATE" */
+ sqlite3TreeViewLine(pView, "%sCOLLATE %Q%s",
+ !ExprHasProperty(pExpr, EP_Collate) ? "SOFT-" : "",
+ pExpr->u.zToken, zFlgs);
sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
break;
}
@@ -29150,10 +29264,10 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
#endif
}
if( pExpr->op==TK_AGG_FUNCTION ){
- sqlite3TreeViewLine(pView, "AGG_FUNCTION%d %Q",
- pExpr->op2, pExpr->u.zToken);
+ sqlite3TreeViewLine(pView, "AGG_FUNCTION%d %Q%s",
+ pExpr->op2, pExpr->u.zToken, zFlgs);
}else{
- sqlite3TreeViewLine(pView, "FUNCTION %Q", pExpr->u.zToken);
+ sqlite3TreeViewLine(pView, "FUNCTION %Q%s", pExpr->u.zToken, zFlgs);
}
if( pFarg ){
sqlite3TreeViewExprList(pView, pFarg, pWin!=0, 0);
@@ -29230,7 +29344,7 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
#ifndef SQLITE_OMIT_TRIGGER
case TK_RAISE: {
const char *zType = "unk";
- switch( pExpr->affinity ){
+ switch( pExpr->affExpr ){
case OE_Rollback: zType = "rollback"; break;
case OE_Abort: zType = "abort"; break;
case OE_Fail: zType = "fail"; break;
@@ -29271,7 +29385,7 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
sqlite3TreeViewExpr(pView, pExpr->pRight, 0);
}else if( zUniOp ){
sqlite3TreeViewLine(pView, "%s%s", zUniOp, zFlgs);
- sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
}
sqlite3TreeViewPop(pView);
}
@@ -31779,7 +31893,7 @@ SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double x){
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \
- defined(SQLITE_ENABLE_STAT3_OR_STAT4) || \
+ defined(SQLITE_ENABLE_STAT4) || \
defined(SQLITE_EXPLAIN_ESTIMATED_ROWS)
/*
** Convert a LogEst into an integer.
@@ -31797,7 +31911,7 @@ SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst x){
defined(SQLITE_EXPLAIN_ESTIMATED_ROWS)
if( x>60 ) return (u64)LARGEST_INT64;
#else
- /* If only SQLITE_ENABLE_STAT3_OR_STAT4 is on, then the largest input
+ /* If only SQLITE_ENABLE_STAT4 is on, then the largest input
** possible to this routine is 310, resulting in a maximum x of 31 */
assert( x<=60 );
#endif
@@ -32290,24 +32404,24 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
/* 93 */ "Count" OpHelp("r[P2]=count()"),
/* 94 */ "ReadCookie" OpHelp(""),
/* 95 */ "SetCookie" OpHelp(""),
- /* 96 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"),
- /* 97 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"),
- /* 98 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<<r[P1]"),
- /* 99 */ "ShiftRight" OpHelp("r[P3]=r[P2]>>r[P1]"),
- /* 100 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"),
- /* 101 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"),
- /* 102 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"),
- /* 103 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"),
- /* 104 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"),
- /* 105 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"),
- /* 106 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"),
- /* 107 */ "BitNot" OpHelp("r[P2]= ~r[P1]"),
- /* 108 */ "OpenRead" OpHelp("root=P2 iDb=P3"),
- /* 109 */ "OpenWrite" OpHelp("root=P2 iDb=P3"),
- /* 110 */ "String8" OpHelp("r[P2]='P4'"),
- /* 111 */ "OpenDup" OpHelp(""),
- /* 112 */ "OpenAutoindex" OpHelp("nColumn=P2"),
- /* 113 */ "OpenEphemeral" OpHelp("nColumn=P2"),
+ /* 96 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"),
+ /* 97 */ "OpenRead" OpHelp("root=P2 iDb=P3"),
+ /* 98 */ "OpenWrite" OpHelp("root=P2 iDb=P3"),
+ /* 99 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"),
+ /* 100 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"),
+ /* 101 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<<r[P1]"),
+ /* 102 */ "ShiftRight" OpHelp("r[P3]=r[P2]>>r[P1]"),
+ /* 103 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"),
+ /* 104 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"),
+ /* 105 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"),
+ /* 106 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"),
+ /* 107 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"),
+ /* 108 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"),
+ /* 109 */ "OpenDup" OpHelp(""),
+ /* 110 */ "BitNot" OpHelp("r[P2]= ~r[P1]"),
+ /* 111 */ "OpenAutoindex" OpHelp("nColumn=P2"),
+ /* 112 */ "OpenEphemeral" OpHelp("nColumn=P2"),
+ /* 113 */ "String8" OpHelp("r[P2]='P4'"),
/* 114 */ "SorterOpen" OpHelp(""),
/* 115 */ "SequenceTest" OpHelp("if( cursor[P1].ctr++ ) pc = P2"),
/* 116 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"),
@@ -32339,10 +32453,10 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
/* 142 */ "LoadAnalysis" OpHelp(""),
/* 143 */ "DropTable" OpHelp(""),
/* 144 */ "DropIndex" OpHelp(""),
- /* 145 */ "Real" OpHelp("r[P2]=P4"),
- /* 146 */ "DropTrigger" OpHelp(""),
- /* 147 */ "IntegrityCk" OpHelp(""),
- /* 148 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"),
+ /* 145 */ "DropTrigger" OpHelp(""),
+ /* 146 */ "IntegrityCk" OpHelp(""),
+ /* 147 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"),
+ /* 148 */ "Real" OpHelp("r[P2]=P4"),
/* 149 */ "Param" OpHelp(""),
/* 150 */ "FkCounter" OpHelp("fkctr[P1]+=P2"),
/* 151 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"),
@@ -32481,13 +32595,29 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
# include <sys/param.h>
#endif /* SQLITE_ENABLE_LOCKING_STYLE */
-#if defined(__APPLE__) && ((__MAC_OS_X_VERSION_MIN_REQUIRED > 1050) || \
- (__IPHONE_OS_VERSION_MIN_REQUIRED > 2000))
-# if (!defined(TARGET_OS_EMBEDDED) || (TARGET_OS_EMBEDDED==0)) \
- && (!defined(TARGET_IPHONE_SIMULATOR) || (TARGET_IPHONE_SIMULATOR==0))
-# define HAVE_GETHOSTUUID 1
-# else
-# warning "gethostuuid() is disabled."
+/*
+** Try to determine if gethostuuid() is available based on standard
+** macros. This might sometimes compute the wrong value for some
+** obscure platforms. For those cases, simply compile with one of
+** the following:
+**
+** -DHAVE_GETHOSTUUID=0
+** -DHAVE_GETHOSTUUID=1
+**
+** None if this matters except when building on Apple products with
+** -DSQLITE_ENABLE_LOCKING_STYLE.
+*/
+#ifndef HAVE_GETHOSTUUID
+# define HAVE_GETHOSTUUID 0
+# if defined(__APPLE__) && ((__MAC_OS_X_VERSION_MIN_REQUIRED > 1050) || \
+ (__IPHONE_OS_VERSION_MIN_REQUIRED > 2000))
+# if (!defined(TARGET_OS_EMBEDDED) || (TARGET_OS_EMBEDDED==0)) \
+ && (!defined(TARGET_IPHONE_SIMULATOR) || (TARGET_IPHONE_SIMULATOR==0))
+# undef HAVE_GETHOSTUUID
+# define HAVE_GETHOSTUUID 1
+# else
+# warning "gethostuuid() is disabled."
+# endif
# endif
#endif
@@ -33095,13 +33225,14 @@ static struct unix_syscall {
#if defined(__linux__) && defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
# ifdef __ANDROID__
{ "ioctl", (sqlite3_syscall_ptr)(int(*)(int, int, ...))ioctl, 0 },
+#define osIoctl ((int(*)(int,int,...))aSyscall[28].pCurrent)
# else
{ "ioctl", (sqlite3_syscall_ptr)ioctl, 0 },
+#define osIoctl ((int(*)(int,unsigned long,...))aSyscall[28].pCurrent)
# endif
#else
{ "ioctl", (sqlite3_syscall_ptr)0, 0 },
#endif
-#define osIoctl ((int(*)(int,int,...))aSyscall[28].pCurrent)
}; /* End of the overrideable system calls */
@@ -38343,6 +38474,7 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
UnixUnusedFd **pp;
assert( sqlite3_mutex_notheld(pInode->pLockMutex) );
sqlite3_mutex_enter(pInode->pLockMutex);
+ flags &= (SQLITE_OPEN_READONLY|SQLITE_OPEN_READWRITE);
for(pp=&pInode->pUnused; *pp && (*pp)->flags!=flags; pp=&((*pp)->pNext));
pUnused = *pp;
if( pUnused ){
@@ -38396,7 +38528,7 @@ static int getFileMode(
** If the SQLITE_ENABLE_8_3_NAMES option is enabled, then the
** original filename is unavailable. But 8_3_NAMES is only used for
** FAT filesystems and permissions do not matter there, so just use
-** the default permissions.
+** the default permissions. In 8_3_NAMES mode, leave *pMode set to zero.
*/
static int findCreateFileMode(
const char *zPath, /* Path of file (possibly) being created */
@@ -38631,11 +38763,19 @@ static int unixOpen(
goto open_finished;
}
- /* If this process is running as root and if creating a new rollback
- ** journal or WAL file, set the ownership of the journal or WAL to be
- ** the same as the original database.
+ /* The owner of the rollback journal or WAL file should always be the
+ ** same as the owner of the database file. Try to ensure that this is
+ ** the case. The chown() system call will be a no-op if the current
+ ** process lacks root privileges, be we should at least try. Without
+ ** this step, if a root process opens a database file, it can leave
+ ** behinds a journal/WAL that is owned by root and hence make the
+ ** database inaccessible to unprivileged processes.
+ **
+ ** If openMode==0, then that means uid and gid are not set correctly
+ ** (probably because SQLite is configured to use 8+3 filename mode) and
+ ** in that case we do not want to attempt the chown().
*/
- if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){
+ if( openMode && (flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL))!=0 ){
robustFchown(fd, uid, gid);
}
}
@@ -38646,7 +38786,8 @@ static int unixOpen(
if( p->pPreallocatedUnused ){
p->pPreallocatedUnused->fd = fd;
- p->pPreallocatedUnused->flags = flags;
+ p->pPreallocatedUnused->flags =
+ flags & (SQLITE_OPEN_READONLY|SQLITE_OPEN_READWRITE);
}
if( isDelete ){
@@ -39492,7 +39633,7 @@ SQLITE_API int sqlite3_hostid_num = 0;
#define PROXY_HOSTIDLEN 16 /* conch file host id length */
-#ifdef HAVE_GETHOSTUUID
+#if HAVE_GETHOSTUUID
/* Not always defined in the headers as it ought to be */
extern int gethostuuid(uuid_t id, const struct timespec *wait);
#endif
@@ -39503,7 +39644,7 @@ extern int gethostuuid(uuid_t id, const struct timespec *wait);
static int proxyGetHostID(unsigned char *pHostID, int *pError){
assert(PROXY_HOSTIDLEN == sizeof(uuid_t));
memset(pHostID, 0, PROXY_HOSTIDLEN);
-#ifdef HAVE_GETHOSTUUID
+#if HAVE_GETHOSTUUID
{
struct timespec timeout = {1, 0}; /* 1 sec timeout */
if( gethostuuid(pHostID, &timeout) ){
@@ -40177,7 +40318,7 @@ static int proxyFileControl(sqlite3_file *id, int op, void *pArg){
assert( 0 ); /* The call assures that only valid opcodes are sent */
}
}
- /*NOTREACHED*/
+ /*NOTREACHED*/ assert(0);
return SQLITE_ERROR;
}
@@ -44862,6 +45003,7 @@ static int winShmMap(
rc = winOpenSharedMemory(pDbFd);
if( rc!=SQLITE_OK ) return rc;
pShm = pDbFd->pShm;
+ assert( pShm!=0 );
}
pShmNode = pShm->pShmNode;
@@ -45164,6 +45306,7 @@ static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
}
}
if( pFd->mmapSize >= iOff+nAmt ){
+ assert( pFd->pMapRegion!=0 );
*pp = &((u8 *)pFd->pMapRegion)[iOff];
pFd->nFetchOut++;
}
@@ -48085,6 +48228,7 @@ SQLITE_PRIVATE int sqlite3PcacheInitialize(void){
** built-in default page cache is used instead of the application defined
** page cache. */
sqlite3PCacheSetDefault();
+ assert( sqlite3GlobalConfig.pcache2.xInit!=0 );
}
return sqlite3GlobalConfig.pcache2.xInit(sqlite3GlobalConfig.pcache2.pArg);
}
@@ -49131,6 +49275,7 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){
assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
if( pCache->pFree || (pCache->nPage==0 && pcache1InitBulk(pCache)) ){
+ assert( pCache->pFree!=0 );
p = pCache->pFree;
pCache->pFree = p->pNext;
p->pNext = 0;
@@ -61857,6 +62002,7 @@ SQLITE_PRIVATE int sqlite3WalFrames(
if( rc ) return rc;
iOffset += szFrame;
nExtra++;
+ assert( pLast!=0 );
}
}
if( bSync ){
@@ -61889,6 +62035,7 @@ SQLITE_PRIVATE int sqlite3WalFrames(
iFrame++;
rc = walIndexAppend(pWal, iFrame, p->pgno);
}
+ assert( pLast!=0 || nExtra==0 );
while( rc==SQLITE_OK && nExtra>0 ){
iFrame++;
nExtra--;
@@ -64900,9 +65047,12 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
if( (data[hdr+2] || data[hdr+1]) && gap+2<=top ){
u8 *pSpace = pageFindSlot(pPage, nByte, &rc);
if( pSpace ){
- assert( pSpace>=data && (pSpace - data)<65536 );
- *pIdx = (int)(pSpace - data);
- return SQLITE_OK;
+ assert( pSpace+nByte<=data+pPage->pBt->usableSize );
+ if( (*pIdx = (int)(pSpace-data))<=gap ){
+ return SQLITE_CORRUPT_PAGE(pPage);
+ }else{
+ return SQLITE_OK;
+ }
}else if( rc ){
return rc;
}
@@ -68129,6 +68279,7 @@ static int accessPayload(
assert( aWrite>=pBufStart ); /* due to (6) */
memcpy(aSave, aWrite, 4);
rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1));
+ if( rc && nextPage>pBt->nPage ) rc = SQLITE_CORRUPT_BKPT;
nextPage = get4byte(aWrite);
memcpy(aWrite, aSave, 4);
}else
@@ -69917,12 +70068,7 @@ static void insertCell(
assert( pPage->nOverflow<=ArraySize(pPage->apOvfl) );
assert( ArraySize(pPage->apOvfl)==ArraySize(pPage->aiOvfl) );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
- /* The cell should normally be sized correctly. However, when moving a
- ** malformed cell from a leaf page to an interior page, if the cell size
- ** wanted to be less than 4 but got rounded up to 4 on the leaf, then size
- ** might be less than 8 (leaf-size + pointer) on the interior node. Hence
- ** the term after the || in the following assert(). */
- assert( sz==pPage->xCellSize(pPage, pCell) || (sz==8 && iChild>0) );
+ assert( sz==pPage->xCellSize(pPage, pCell) || CORRUPT_DB );
assert( pPage->nFree>=0 );
if( pPage->nOverflow || sz+2>pPage->nFree ){
if( pTemp ){
@@ -70154,7 +70300,7 @@ static int rebuildPage(
assert( i<iEnd );
j = get2byte(&aData[hdr+5]);
- if( NEVER(j>(u32)usableSize) ){ j = 0; }
+ if( j>(u32)usableSize ){ j = 0; }
memcpy(&pTmp[j], &aData[j], usableSize - j);
for(k=0; pCArray->ixNx[k]<=i && ALWAYS(k<NB*2); k++){}
@@ -70246,7 +70392,8 @@ static int pageInsertArray(
while( 1 /*Exit by break*/ ){
int sz, rc;
u8 *pSlot;
- sz = cachedCellSize(pCArray, i);
+ assert( pCArray->szCell[i]!=0 );
+ sz = pCArray->szCell[i];
if( (aData[1]==0 && aData[2]==0) || (pSlot = pageFindSlot(pPg,sz,&rc))==0 ){
if( (pData - pBegin)<sz ) return 1;
pData -= sz;
@@ -70407,6 +70554,7 @@ static int editPage(
memmove(&pCellptr[2], pCellptr, (nCell - iCell) * 2);
}
nCell++;
+ cachedCellSize(pCArray, iCell+iNew);
if( pageInsertArray(
pPg, pBegin, &pData, pCellptr,
iCell+iNew, 1, pCArray
@@ -70929,7 +71077,7 @@ static int balance_nonroot(
*/
memset(&b.szCell[b.nCell], 0, sizeof(b.szCell[0])*(limit+pOld->nOverflow));
if( pOld->nOverflow>0 ){
- if( limit<pOld->aiOvfl[0] ){
+ if( NEVER(limit<pOld->aiOvfl[0]) ){
rc = SQLITE_CORRUPT_BKPT;
goto balance_cleanup;
}
@@ -71215,6 +71363,8 @@ static int balance_nonroot(
));
assert( sqlite3PagerIswriteable(pParent->pDbPage) );
+ assert( nNew>=1 && nNew<=ArraySize(apNew) );
+ assert( apNew[nNew-1]!=0 );
put4byte(pRight, apNew[nNew-1]->pgno);
/* If the sibling pages are not leaves, ensure that the right-child pointer
@@ -71560,11 +71710,13 @@ static int balance(BtCursor *pCur){
VVA_ONLY( int balance_deeper_called = 0 );
do {
- int iPage = pCur->iPage;
+ int iPage;
MemPage *pPage = pCur->pPage;
if( NEVER(pPage->nFree<0) && btreeComputeFreeSpace(pPage) ) break;
- if( iPage==0 ){
+ if( pPage->nOverflow==0 && pPage->nFree<=nMin ){
+ break;
+ }else if( (iPage = pCur->iPage)==0 ){
if( pPage->nOverflow ){
/* The root page of the b-tree is overfull. In this case call the
** balance_deeper() function to create a new child for the root-page
@@ -71585,8 +71737,6 @@ static int balance(BtCursor *pCur){
}else{
break;
}
- }else if( pPage->nOverflow==0 && pPage->nFree<=nMin ){
- break;
}else{
MemPage * const pParent = pCur->apPage[iPage-1];
int const iIdx = pCur->aiIdx[iPage-1];
@@ -71728,7 +71878,9 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){
Pgno ovflPgno; /* Next overflow page to write */
u32 ovflPageSize; /* Size to write on overflow page */
- if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd ){
+ if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd
+ || pCur->info.pPayload < pPage->aData + pPage->cellOffset
+ ){
return SQLITE_CORRUPT_BKPT;
}
/* Overwrite the local portion first */
@@ -71969,6 +72121,8 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
memcpy(newCell, oldCell, 4);
}
rc = clearCell(pPage, oldCell, &info);
+ testcase( pCur->curFlags & BTCF_ValidOvfl );
+ invalidateOverflowCache(pCur);
if( info.nSize==szNew && info.nLocal==info.nPayload
&& (!ISAUTOVACUUM || szNew<pPage->minLocal)
){
@@ -71982,7 +72136,12 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
** new entry uses overflow pages, as the insertCell() call below is
** necessary to add the PTRMAP_OVERFLOW1 pointer-map entry. */
assert( rc==SQLITE_OK ); /* clearCell never fails when nLocal==nPayload */
- if( oldCell+szNew > pPage->aDataEnd ) return SQLITE_CORRUPT_BKPT;
+ if( oldCell < pPage->aData+pPage->hdrOffset+10 ){
+ return SQLITE_CORRUPT_BKPT;
+ }
+ if( oldCell+szNew > pPage->aDataEnd ){
+ return SQLITE_CORRUPT_BKPT;
+ }
memcpy(oldCell, newCell, szNew);
return SQLITE_OK;
}
@@ -74319,8 +74478,10 @@ SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p){
}
if( p->isAttached ){
pp = sqlite3PagerBackupPtr(sqlite3BtreePager(p->pSrc));
+ assert( pp!=0 );
while( *pp!=p ){
pp = &(*pp)->pNext;
+ assert( pp!=0 );
}
*pp = p->pNext;
}
@@ -74735,7 +74896,13 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPre
assert( pMem->szMalloc==0
|| pMem->szMalloc==sqlite3DbMallocSize(pMem->db, pMem->zMalloc) );
if( pMem->szMalloc>0 && bPreserve && pMem->z==pMem->zMalloc ){
- pMem->z = pMem->zMalloc = sqlite3DbReallocOrFree(pMem->db, pMem->z, n);
+ if( pMem->db ){
+ pMem->z = pMem->zMalloc = sqlite3DbReallocOrFree(pMem->db, pMem->z, n);
+ }else{
+ pMem->zMalloc = sqlite3Realloc(pMem->z, n);
+ if( pMem->zMalloc==0 ) sqlite3_free(pMem->z);
+ pMem->z = pMem->zMalloc;
+ }
bPreserve = 0;
}else{
if( pMem->szMalloc>0 ) sqlite3DbFreeNN(pMem->db, pMem->zMalloc);
@@ -75806,7 +75973,7 @@ struct ValueNewStat4Ctx {
** an sqlite3_value within the UnpackedRecord.a[] array.
*/
static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
if( p ){
UnpackedRecord *pRec = p->ppRec[0];
@@ -75842,7 +76009,7 @@ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){
}
#else
UNUSED_PARAMETER(p);
-#endif /* defined(SQLITE_ENABLE_STAT3_OR_STAT4) */
+#endif /* defined(SQLITE_ENABLE_STAT4) */
return sqlite3ValueNew(db);
}
@@ -75866,7 +76033,7 @@ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){
** and sets (*ppVal) to NULL. Or, if an error occurs, (*ppVal) is set to
** NULL and an SQLite error code returned.
*/
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
static int valueFromFunction(
sqlite3 *db, /* The database connection */
Expr *p, /* The expression to evaluate */
@@ -75949,7 +76116,7 @@ static int valueFromFunction(
}
#else
# define valueFromFunction(a,b,c,d,e,f) SQLITE_OK
-#endif /* defined(SQLITE_ENABLE_STAT3_OR_STAT4) */
+#endif /* defined(SQLITE_ENABLE_STAT4) */
/*
** Extract a value from the supplied expression in the manner described
@@ -75978,7 +76145,7 @@ static int valueFromExpr(
assert( pExpr!=0 );
while( (op = pExpr->op)==TK_UPLUS || op==TK_SPAN ) pExpr = pExpr->pLeft;
-#if defined(SQLITE_ENABLE_STAT3_OR_STAT4)
+#if defined(SQLITE_ENABLE_STAT4)
if( op==TK_REGISTER ) op = pExpr->op2;
#else
if( NEVER(op==TK_REGISTER) ) op = pExpr->op2;
@@ -76071,7 +76238,7 @@ static int valueFromExpr(
0, SQLITE_DYNAMIC);
}
#endif
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
else if( op==TK_FUNCTION && pCtx!=0 ){
rc = valueFromFunction(db, pExpr, enc, affinity, &pVal, pCtx);
}
@@ -76088,13 +76255,13 @@ static int valueFromExpr(
return rc;
no_mem:
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
if( pCtx==0 || pCtx->pParse->nErr==0 )
#endif
sqlite3OomFault(db);
sqlite3DbFree(db, zVal);
assert( *ppVal==0 );
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
if( pCtx==0 ) sqlite3ValueFree(pVal);
#else
assert( pCtx==0 ); sqlite3ValueFree(pVal);
@@ -76122,56 +76289,7 @@ SQLITE_PRIVATE int sqlite3ValueFromExpr(
return pExpr ? valueFromExpr(db, pExpr, enc, affinity, ppVal, 0) : 0;
}
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
-/*
-** The implementation of the sqlite_record() function. This function accepts
-** a single argument of any type. The return value is a formatted database
-** record (a blob) containing the argument value.
-**
-** This is used to convert the value stored in the 'sample' column of the
-** sqlite_stat3 table to the record format SQLite uses internally.
-*/
-static void recordFunc(
- sqlite3_context *context,
- int argc,
- sqlite3_value **argv
-){
- const int file_format = 1;
- u32 iSerial; /* Serial type */
- int nSerial; /* Bytes of space for iSerial as varint */
- u32 nVal; /* Bytes of space required for argv[0] */
- int nRet;
- sqlite3 *db;
- u8 *aRet;
-
- UNUSED_PARAMETER( argc );
- iSerial = sqlite3VdbeSerialType(argv[0], file_format, &nVal);
- nSerial = sqlite3VarintLen(iSerial);
- db = sqlite3_context_db_handle(context);
-
- nRet = 1 + nSerial + nVal;
- aRet = sqlite3DbMallocRawNN(db, nRet);
- if( aRet==0 ){
- sqlite3_result_error_nomem(context);
- }else{
- aRet[0] = nSerial+1;
- putVarint32(&aRet[1], iSerial);
- sqlite3VdbeSerialPut(&aRet[1+nSerial], argv[0], iSerial);
- sqlite3_result_blob(context, aRet, nRet, SQLITE_TRANSIENT);
- sqlite3DbFreeNN(db, aRet);
- }
-}
-
-/*
-** Register built-in functions used to help read ANALYZE data.
-*/
-SQLITE_PRIVATE void sqlite3AnalyzeFunctions(void){
- static FuncDef aAnalyzeTableFuncs[] = {
- FUNCTION(sqlite_record, 1, 0, 0, recordFunc),
- };
- sqlite3InsertBuiltinFuncs(aAnalyzeTableFuncs, ArraySize(aAnalyzeTableFuncs));
-}
-
+#ifdef SQLITE_ENABLE_STAT4
/*
** Attempt to extract a value from pExpr and use it to construct *ppVal.
**
@@ -77078,7 +77196,7 @@ SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){
int opcode = pOp->opcode;
if( opcode==OP_Destroy || opcode==OP_VUpdate || opcode==OP_VRename
|| opcode==OP_VDestroy
- || (opcode==OP_Function0 && pOp->p4.pFunc->funcFlags&SQLITE_FUNC_INTERNAL)
+ || (opcode==OP_ParseSchema && pOp->p4.z==0)
|| ((opcode==OP_Halt || opcode==OP_HaltIfNull)
&& ((pOp->p1)!=SQLITE_OK && pOp->p2==OE_Abort))
){
@@ -77415,16 +77533,16 @@ SQLITE_PRIVATE void sqlite3VdbeScanStatus(
** Change the value of the opcode, or P1, P2, P3, or P5 operands
** for a specific instruction.
*/
-SQLITE_PRIVATE void sqlite3VdbeChangeOpcode(Vdbe *p, u32 addr, u8 iNewOpcode){
+SQLITE_PRIVATE void sqlite3VdbeChangeOpcode(Vdbe *p, int addr, u8 iNewOpcode){
sqlite3VdbeGetOp(p,addr)->opcode = iNewOpcode;
}
-SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe *p, u32 addr, int val){
+SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe *p, int addr, int val){
sqlite3VdbeGetOp(p,addr)->p1 = val;
}
-SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe *p, u32 addr, int val){
+SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe *p, int addr, int val){
sqlite3VdbeGetOp(p,addr)->p2 = val;
}
-SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe *p, u32 addr, int val){
+SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe *p, int addr, int val){
sqlite3VdbeGetOp(p,addr)->p3 = val;
}
SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe *p, u16 p5){
@@ -77931,14 +78049,16 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
case P4_KEYINFO: {
int j;
KeyInfo *pKeyInfo = pOp->p4.pKeyInfo;
- assert( pKeyInfo->aSortOrder!=0 );
+ assert( pKeyInfo->aSortFlags!=0 );
sqlite3_str_appendf(&x, "k(%d", pKeyInfo->nKeyField);
for(j=0; j<pKeyInfo->nKeyField; j++){
CollSeq *pColl = pKeyInfo->aColl[j];
const char *zColl = pColl ? pColl->zName : "";
if( strcmp(zColl, "BINARY")==0 ) zColl = "B";
- sqlite3_str_appendf(&x, ",%s%s",
- pKeyInfo->aSortOrder[j] ? "-" : "", zColl);
+ sqlite3_str_appendf(&x, ",%s%s%s",
+ (pKeyInfo->aSortFlags[j] & KEYINFO_ORDER_DESC) ? "-" : "",
+ (pKeyInfo->aSortFlags[j] & KEYINFO_ORDER_BIGNULL)? "N." : "",
+ zColl);
}
sqlite3_str_append(&x, ")", 1);
break;
@@ -78345,8 +78465,11 @@ SQLITE_PRIVATE int sqlite3VdbeList(
** pick up the appropriate opcode. */
int j;
i -= p->nOp;
+ assert( apSub!=0 );
+ assert( nSub>0 );
for(j=0; i>=apSub[j]->nOp; j++){
i -= apSub[j]->nOp;
+ assert( i<apSub[j]->nOp || j+1<nSub );
}
pOp = &apSub[j]->aOp[i];
}
@@ -79868,10 +79991,17 @@ SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor **pp, int *piCol){
** of SQLite will not understand those serial types.
*/
+#if 0 /* Inlined into the OP_MakeRecord opcode */
/*
** Return the serial-type for the value stored in pMem.
**
** This routine might convert a large MEM_IntReal value into MEM_Real.
+**
+** 2019-07-11: The primary user of this subroutine was the OP_MakeRecord
+** opcode in the byte-code engine. But by moving this routine in-line, we
+** can omit some redundant tests and make that opcode a lot faster. So
+** this routine is now only used by the STAT3 logic and STAT3 support has
+** ended. The code is kept here for historical reference only.
*/
SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem *pMem, int file_format, u32 *pLen){
int flags = pMem->flags;
@@ -79932,6 +80062,7 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem *pMem, int file_format, u32 *pLen){
*pLen = n;
return ((n*2) + 12 + ((flags&MEM_Str)!=0));
}
+#endif /* inlined into OP_MakeRecord */
/*
** The sizes for serial types less than 128
@@ -80240,7 +80371,7 @@ SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(
p = (UnpackedRecord *)sqlite3DbMallocRaw(pKeyInfo->db, nByte);
if( !p ) return 0;
p->aMem = (Mem*)&((char*)p)[ROUND8(sizeof(UnpackedRecord))];
- assert( pKeyInfo->aSortOrder!=0 );
+ assert( pKeyInfo->aSortFlags!=0 );
p->pKeyInfo = pKeyInfo;
p->nField = pKeyInfo->nKeyField + 1;
return p;
@@ -80339,7 +80470,7 @@ static int vdbeRecordCompareDebug(
if( szHdr1>98307 ) return SQLITE_CORRUPT;
d1 = szHdr1;
assert( pKeyInfo->nAllField>=pPKey2->nField || CORRUPT_DB );
- assert( pKeyInfo->aSortOrder!=0 );
+ assert( pKeyInfo->aSortFlags!=0 );
assert( pKeyInfo->nKeyField>0 );
assert( idx1<=szHdr1 || CORRUPT_DB );
do{
@@ -80370,7 +80501,12 @@ static int vdbeRecordCompareDebug(
pKeyInfo->nAllField>i ? pKeyInfo->aColl[i] : 0);
if( rc!=0 ){
assert( mem1.szMalloc==0 ); /* See comment below */
- if( pKeyInfo->aSortOrder[i] ){
+ if( (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_BIGNULL)
+ && ((mem1.flags & MEM_Null) || (pPKey2->aMem[i].flags & MEM_Null))
+ ){
+ rc = -rc;
+ }
+ if( pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_DESC ){
rc = -rc; /* Invert the result for DESC sort order. */
}
goto debugCompareEnd;
@@ -80746,7 +80882,7 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
VVA_ONLY( mem1.szMalloc = 0; ) /* Only needed by assert() statements */
assert( pPKey2->pKeyInfo->nAllField>=pPKey2->nField
|| CORRUPT_DB );
- assert( pPKey2->pKeyInfo->aSortOrder!=0 );
+ assert( pPKey2->pKeyInfo->aSortFlags!=0 );
assert( pPKey2->pKeyInfo->nKeyField>0 );
assert( idx1<=szHdr1 || CORRUPT_DB );
do{
@@ -80869,8 +81005,14 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
}
if( rc!=0 ){
- if( pPKey2->pKeyInfo->aSortOrder[i] ){
- rc = -rc;
+ int sortFlags = pPKey2->pKeyInfo->aSortFlags[i];
+ if( sortFlags ){
+ if( (sortFlags & KEYINFO_ORDER_BIGNULL)==0
+ || ((sortFlags & KEYINFO_ORDER_DESC)
+ !=(serial_type==0 || (pRhs->flags&MEM_Null)))
+ ){
+ rc = -rc;
+ }
}
assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, rc) );
assert( mem1.szMalloc==0 ); /* See comment below */
@@ -81038,7 +81180,11 @@ static int vdbeRecordCompareString(
nCmp = MIN( pPKey2->aMem[0].n, nStr );
res = memcmp(&aKey1[szHdr], pPKey2->aMem[0].z, nCmp);
- if( res==0 ){
+ if( res>0 ){
+ res = pPKey2->r2;
+ }else if( res<0 ){
+ res = pPKey2->r1;
+ }else{
res = nStr - pPKey2->aMem[0].n;
if( res==0 ){
if( pPKey2->nField>1 ){
@@ -81052,10 +81198,6 @@ static int vdbeRecordCompareString(
}else{
res = pPKey2->r1;
}
- }else if( res>0 ){
- res = pPKey2->r2;
- }else{
- res = pPKey2->r1;
}
}
@@ -81087,7 +81229,10 @@ SQLITE_PRIVATE RecordCompare sqlite3VdbeFindCompare(UnpackedRecord *p){
** header size is (12*5 + 1 + 1) bytes. */
if( p->pKeyInfo->nAllField<=13 ){
int flags = p->aMem[0].flags;
- if( p->pKeyInfo->aSortOrder[0] ){
+ if( p->pKeyInfo->aSortFlags[0] ){
+ if( p->pKeyInfo->aSortFlags[0] & KEYINFO_ORDER_BIGNULL ){
+ return sqlite3VdbeRecordCompare;
+ }
p->r1 = 1;
p->r2 = -1;
}else{
@@ -81336,7 +81481,7 @@ SQLITE_PRIVATE void sqlite3VdbeSetVarmask(Vdbe *v, int iVar){
** features such as 'now'.
*/
SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context *pCtx){
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
if( pCtx->pVdbe==0 ) return 1;
#endif
if( pCtx->pVdbe->aOp[pCtx->iOp].opcode==OP_PureFunc ){
@@ -81433,7 +81578,7 @@ SQLITE_PRIVATE void sqlite3VdbePreUpdateHook(
preupdate.keyinfo.db = db;
preupdate.keyinfo.enc = ENC(db);
preupdate.keyinfo.nKeyField = pTab->nCol;
- preupdate.keyinfo.aSortOrder = (u8*)&fakeSortOrder;
+ preupdate.keyinfo.aSortFlags = (u8*)&fakeSortOrder;
preupdate.iKey1 = iKey1;
preupdate.iKey2 = iKey2;
preupdate.pTab = pTab;
@@ -82302,7 +82447,7 @@ SQLITE_API int sqlite3_vtab_nochange(sqlite3_context *p){
*/
SQLITE_PRIVATE sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context *p){
int rc;
-#ifndef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifndef SQLITE_ENABLE_STAT4
sqlite3_int64 *piTime = &p->pVdbe->iCurrentTime;
assert( p->pVdbe!=0 );
#else
@@ -82367,7 +82512,7 @@ SQLITE_API void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){
AuxData *pAuxData;
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
-#if SQLITE_ENABLE_STAT3_OR_STAT4
+#if SQLITE_ENABLE_STAT4
if( pCtx->pVdbe==0 ) return 0;
#else
assert( pCtx->pVdbe!=0 );
@@ -82401,7 +82546,7 @@ SQLITE_API void sqlite3_set_auxdata(
Vdbe *pVdbe = pCtx->pVdbe;
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
if( pVdbe==0 ) goto failed;
#else
assert( pVdbe!=0 );
@@ -84053,6 +84198,7 @@ static void applyNumericAffinity(Mem *pRec, int bTryForInt){
** Convert pRec to a text representation.
**
** SQLITE_AFF_BLOB:
+** SQLITE_AFF_NONE:
** No-op. pRec is unchanged.
*/
static void applyAffinity(
@@ -84192,13 +84338,15 @@ SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, char *zBuf){
c = 's';
}
*(zCsr++) = c;
+ *(zCsr++) = 'x';
sqlite3_snprintf(100, zCsr, "%d[", pMem->n);
zCsr += sqlite3Strlen30(zCsr);
- for(i=0; i<16 && i<pMem->n; i++){
+ for(i=0; i<25 && i<pMem->n; i++){
sqlite3_snprintf(100, zCsr, "%02X", ((int)pMem->z[i] & 0xFF));
zCsr += sqlite3Strlen30(zCsr);
}
- for(i=0; i<16 && i<pMem->n; i++){
+ *zCsr++ = '|';
+ for(i=0; i<25 && i<pMem->n; i++){
char z = pMem->z[i];
if( z<32 || z>126 ) *zCsr++ = '.';
else *zCsr++ = z;
@@ -84228,7 +84376,7 @@ SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, char *zBuf){
sqlite3_snprintf(100, &zBuf[k], "%d", pMem->n);
k += sqlite3Strlen30(&zBuf[k]);
zBuf[k++] = '[';
- for(j=0; j<15 && j<pMem->n; j++){
+ for(j=0; j<25 && j<pMem->n; j++){
u8 c = pMem->z[j];
if( c>=0x20 && c<0x7f ){
zBuf[k++] = c;
@@ -84261,7 +84409,7 @@ static void memTracePrint(Mem *p){
printf(" i:%lld", p->u.i);
#ifndef SQLITE_OMIT_FLOATING_POINT
}else if( p->flags & MEM_Real ){
- printf(" r:%g", p->u.r);
+ printf(" r:%.17g", p->u.r);
#endif
}else if( sqlite3VdbeMemIsRowSet(p) ){
printf(" (rowset)");
@@ -84939,7 +85087,6 @@ case OP_Real: { /* same as TK_FLOAT, out2 */
case OP_String8: { /* same as TK_STRING, out2 */
assert( pOp->p4.z!=0 );
pOut = out2Prerelease(p, pOp);
- pOp->opcode = OP_String;
pOp->p1 = sqlite3Strlen30(pOp->p4.z);
#ifndef SQLITE_OMIT_UTF16
@@ -84963,6 +85110,7 @@ case OP_String8: { /* same as TK_STRING, out2 */
if( pOp->p1>db->aLimit[SQLITE_LIMIT_LENGTH] ){
goto too_big;
}
+ pOp->opcode = OP_String;
assert( rc==SQLITE_OK );
/* Fall through to the next case, OP_String */
}
@@ -85630,6 +85778,7 @@ case OP_RealAffinity: { /* in1 */
testcase( pIn1->flags & MEM_Int );
testcase( pIn1->flags & MEM_IntReal );
sqlite3VdbeMemRealify(pIn1);
+ REGISTER_TRACE(pOp->p1, pIn1);
}
break;
}
@@ -86025,9 +86174,14 @@ case OP_Compare: {
REGISTER_TRACE(p2+idx, &aMem[p2+idx]);
assert( i<pKeyInfo->nKeyField );
pColl = pKeyInfo->aColl[i];
- bRev = pKeyInfo->aSortOrder[i];
+ bRev = (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_DESC);
iCompare = sqlite3MemCompare(&aMem[p1+idx], &aMem[p2+idx], pColl);
if( iCompare ){
+ if( (pKeyInfo->aSortFlags[i] & KEYINFO_ORDER_BIGNULL)
+ && ((aMem[p1+idx].flags & MEM_Null) || (aMem[p2+idx].flags & MEM_Null))
+ ){
+ iCompare = -iCompare;
+ }
if( bRev ) iCompare = -iCompare;
break;
}
@@ -86318,11 +86472,6 @@ case OP_Offset: { /* out3 */
** if the P4 argument is a P4_MEM use the value of the P4 argument as
** the result.
**
-** If the OPFLAG_CLEARCACHE bit is set on P5 and P1 is a pseudo-table cursor,
-** then the cache of the cursor is reset prior to extracting the column.
-** The first OP_Column against a pseudo-table after the value of the content
-** register has changed should have this bit set.
-**
** If the OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG bits are set on P5 then
** the result is guaranteed to only be used as the argument of a length()
** or typeof() function, respectively. The loading of large blobs can be
@@ -86611,15 +86760,27 @@ case OP_Affinity: {
assert( pOp->p2>0 );
assert( zAffinity[pOp->p2]==0 );
pIn1 = &aMem[pOp->p1];
- while( 1 /*edit-by-break*/ ){
+ while( 1 /*exit-by-break*/ ){
assert( pIn1 <= &p->aMem[(p->nMem+1 - p->nCursor)] );
assert( memIsValid(pIn1) );
applyAffinity(pIn1, zAffinity[0], encoding);
if( zAffinity[0]==SQLITE_AFF_REAL && (pIn1->flags & MEM_Int)!=0 ){
- /* When applying REAL affinity, if the result is still MEM_Int,
- ** indicate that REAL is actually desired */
- pIn1->flags |= MEM_IntReal;
- pIn1->flags &= ~MEM_Int;
+ /* When applying REAL affinity, if the result is still an MEM_Int
+ ** that will fit in 6 bytes, then change the type to MEM_IntReal
+ ** so that we keep the high-resolution integer value but know that
+ ** the type really wants to be REAL. */
+ testcase( pIn1->u.i==140737488355328LL );
+ testcase( pIn1->u.i==140737488355327LL );
+ testcase( pIn1->u.i==-140737488355328LL );
+ testcase( pIn1->u.i==-140737488355329LL );
+ if( pIn1->u.i<=140737488355327LL && pIn1->u.i>=-140737488355328LL ){
+ pIn1->flags |= MEM_IntReal;
+ pIn1->flags &= ~MEM_Int;
+ }else{
+ pIn1->u.r = (double)pIn1->u.i;
+ pIn1->flags |= MEM_Real;
+ pIn1->flags &= ~MEM_Int;
+ }
}
REGISTER_TRACE((int)(pIn1-aMem), pIn1);
zAffinity++;
@@ -86726,14 +86887,36 @@ case OP_MakeRecord: {
#endif
/* Loop through the elements that will make up the record to figure
- ** out how much space is required for the new record.
+ ** out how much space is required for the new record. After this loop,
+ ** the Mem.uTemp field of each term should hold the serial-type that will
+ ** be used for that term in the generated record:
+ **
+ ** Mem.uTemp value type
+ ** --------------- ---------------
+ ** 0 NULL
+ ** 1 1-byte signed integer
+ ** 2 2-byte signed integer
+ ** 3 3-byte signed integer
+ ** 4 4-byte signed integer
+ ** 5 6-byte signed integer
+ ** 6 8-byte signed integer
+ ** 7 IEEE float
+ ** 8 Integer constant 0
+ ** 9 Integer constant 1
+ ** 10,11 reserved for expansion
+ ** N>=12 and even BLOB
+ ** N>=13 and odd text
+ **
+ ** The following additional values are computed:
+ ** nHdr Number of bytes needed for the record header
+ ** nData Number of bytes of data space needed for the record
+ ** nZero Zero bytes at the end of the record
*/
pRec = pLast;
do{
assert( memIsValid(pRec) );
- serial_type = sqlite3VdbeSerialType(pRec, file_format, &len);
- if( pRec->flags & MEM_Zero ){
- if( serial_type==0 ){
+ if( pRec->flags & MEM_Null ){
+ if( pRec->flags & MEM_Zero ){
/* Values with MEM_Null and MEM_Zero are created by xColumn virtual
** table methods that never invoke sqlite3_result_xxxxx() while
** computing an unchanging column value in an UPDATE statement.
@@ -86741,19 +86924,83 @@ case OP_MakeRecord: {
** so that they can be passed through to xUpdate and have
** a true sqlite3_value_nochange(). */
assert( pOp->p5==OPFLAG_NOCHNG_MAGIC || CORRUPT_DB );
- serial_type = 10;
- }else if( nData ){
- if( sqlite3VdbeMemExpandBlob(pRec) ) goto no_mem;
+ pRec->uTemp = 10;
+ }else{
+ pRec->uTemp = 0;
+ }
+ nHdr++;
+ }else if( pRec->flags & (MEM_Int|MEM_IntReal) ){
+ /* Figure out whether to use 1, 2, 4, 6 or 8 bytes. */
+ i64 i = pRec->u.i;
+ u64 uu;
+ testcase( pRec->flags & MEM_Int );
+ testcase( pRec->flags & MEM_IntReal );
+ if( i<0 ){
+ uu = ~i;
}else{
- nZero += pRec->u.nZero;
- len -= pRec->u.nZero;
+ uu = i;
+ }
+ nHdr++;
+ testcase( uu==127 ); testcase( uu==128 );
+ testcase( uu==32767 ); testcase( uu==32768 );
+ testcase( uu==8388607 ); testcase( uu==8388608 );
+ testcase( uu==2147483647 ); testcase( uu==2147483648 );
+ testcase( uu==140737488355327LL ); testcase( uu==140737488355328LL );
+ if( uu<=127 ){
+ if( (i&1)==i && file_format>=4 ){
+ pRec->uTemp = 8+(u32)uu;
+ }else{
+ nData++;
+ pRec->uTemp = 1;
+ }
+ }else if( uu<=32767 ){
+ nData += 2;
+ pRec->uTemp = 2;
+ }else if( uu<=8388607 ){
+ nData += 3;
+ pRec->uTemp = 3;
+ }else if( uu<=2147483647 ){
+ nData += 4;
+ pRec->uTemp = 4;
+ }else if( uu<=140737488355327LL ){
+ nData += 6;
+ pRec->uTemp = 5;
+ }else{
+ nData += 8;
+ if( pRec->flags & MEM_IntReal ){
+ /* If the value is IntReal and is going to take up 8 bytes to store
+ ** as an integer, then we might as well make it an 8-byte floating
+ ** point value */
+ pRec->u.r = (double)pRec->u.i;
+ pRec->flags &= ~MEM_IntReal;
+ pRec->flags |= MEM_Real;
+ pRec->uTemp = 7;
+ }else{
+ pRec->uTemp = 6;
+ }
+ }
+ }else if( pRec->flags & MEM_Real ){
+ nHdr++;
+ nData += 8;
+ pRec->uTemp = 7;
+ }else{
+ assert( db->mallocFailed || pRec->flags&(MEM_Str|MEM_Blob) );
+ assert( pRec->n>=0 );
+ len = (u32)pRec->n;
+ serial_type = (len*2) + 12 + ((pRec->flags & MEM_Str)!=0);
+ if( pRec->flags & MEM_Zero ){
+ serial_type += pRec->u.nZero*2;
+ if( nData ){
+ if( sqlite3VdbeMemExpandBlob(pRec) ) goto no_mem;
+ len += pRec->u.nZero;
+ }else{
+ nZero += pRec->u.nZero;
+ }
}
+ nData += len;
+ nHdr += sqlite3VarintLen(serial_type);
+ pRec->uTemp = serial_type;
}
- nData += len;
- testcase( serial_type==127 );
- testcase( serial_type==128 );
- nHdr += serial_type<=127 ? 1 : sqlite3VarintLen(serial_type);
- pRec->uTemp = serial_type;
if( pRec==pData0 ) break;
pRec--;
}while(1);
@@ -87089,7 +87336,7 @@ case OP_AutoCommit: {
rc = SQLITE_ERROR;
goto abort_due_to_error;
}
- break;
+ /*NOTREACHED*/ assert(0);
}
/* Opcode: Transaction P1 P2 P3 P4 P5
@@ -87827,6 +88074,7 @@ case OP_SeekGT: { /* jump, in3, group */
pC->deferredMoveto = 0;
pC->cacheStatus = CACHE_STALE;
if( pC->isTable ){
+ u16 flags3, newType;
/* The BTREE_SEEK_EQ flag is only set on index cursors */
assert( sqlite3BtreeCursorHasHint(pC->uc.pCursor, BTREE_SEEK_EQ)==0
|| CORRUPT_DB );
@@ -87835,18 +88083,21 @@ case OP_SeekGT: { /* jump, in3, group */
** blob, or NULL. But it needs to be an integer before we can do
** the seek, so convert it. */
pIn3 = &aMem[pOp->p3];
- if( (pIn3->flags & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Str))==MEM_Str ){
+ flags3 = pIn3->flags;
+ if( (flags3 & (MEM_Int|MEM_Real|MEM_IntReal|MEM_Str))==MEM_Str ){
applyNumericAffinity(pIn3, 0);
}
- iKey = sqlite3VdbeIntValue(pIn3);
+ iKey = sqlite3VdbeIntValue(pIn3); /* Get the integer key value */
+ newType = pIn3->flags; /* Record the type after applying numeric affinity */
+ pIn3->flags = flags3; /* But convert the type back to its original */
/* If the P3 value could not be converted into an integer without
** loss of information, then special processing is required... */
- if( (pIn3->flags & (MEM_Int|MEM_IntReal))==0 ){
- if( (pIn3->flags & MEM_Real)==0 ){
- if( (pIn3->flags & MEM_Null) || oc>=OP_SeekGE ){
- VdbeBranchTaken(1,2); goto jump_to_p2;
- break;
+ if( (newType & (MEM_Int|MEM_IntReal))==0 ){
+ if( (newType & MEM_Real)==0 ){
+ if( (newType & MEM_Null) || oc>=OP_SeekGE ){
+ VdbeBranchTaken(1,2);
+ goto jump_to_p2;
}else{
rc = sqlite3BtreeLast(pC->uc.pCursor, &res);
if( rc!=SQLITE_OK ) goto abort_due_to_error;
@@ -88231,23 +88482,27 @@ case OP_SeekRowid: { /* jump, in3 */
pIn3 = &aMem[pOp->p3];
testcase( pIn3->flags & MEM_Int );
testcase( pIn3->flags & MEM_IntReal );
+ testcase( pIn3->flags & MEM_Real );
+ testcase( (pIn3->flags & (MEM_Str|MEM_Int))==MEM_Str );
if( (pIn3->flags & (MEM_Int|MEM_IntReal))==0 ){
- /* Make sure pIn3->u.i contains a valid integer representation of
- ** the key value, but do not change the datatype of the register, as
- ** other parts of the perpared statement might be depending on the
- ** current datatype. */
- u16 origFlags = pIn3->flags;
- int isNotInt;
- applyAffinity(pIn3, SQLITE_AFF_NUMERIC, encoding);
- isNotInt = (pIn3->flags & MEM_Int)==0;
- pIn3->flags = origFlags;
- if( isNotInt ) goto jump_to_p2;
+ /* If pIn3->u.i does not contain an integer, compute iKey as the
+ ** integer value of pIn3. Jump to P2 if pIn3 cannot be converted
+ ** into an integer without loss of information. Take care to avoid
+ ** changing the datatype of pIn3, however, as it is used by other
+ ** parts of the prepared statement. */
+ Mem x = pIn3[0];
+ applyAffinity(&x, SQLITE_AFF_NUMERIC, encoding);
+ if( (x.flags & MEM_Int)==0 ) goto jump_to_p2;
+ iKey = x.u.i;
+ goto notExistsWithKey;
}
/* Fall through into OP_NotExists */
case OP_NotExists: /* jump, in3 */
pIn3 = &aMem[pOp->p3];
assert( (pIn3->flags & MEM_Int)!=0 || pOp->opcode==OP_SeekRowid );
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ iKey = pIn3->u.i;
+notExistsWithKey:
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
#ifdef SQLITE_DEBUG
@@ -88258,7 +88513,6 @@ case OP_NotExists: /* jump, in3 */
pCrsr = pC->uc.pCursor;
assert( pCrsr!=0 );
res = 0;
- iKey = pIn3->u.i;
rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res);
assert( rc==SQLITE_OK || res==0 );
pC->movetoTarget = iKey; /* Used by OP_Delete */
@@ -89140,11 +89394,12 @@ case OP_Next: /* jump */
** The Prev opcode is only used after SeekLT, SeekLE, and Last. */
assert( pOp->opcode!=OP_Next
|| pC->seekOp==OP_SeekGT || pC->seekOp==OP_SeekGE
- || pC->seekOp==OP_Rewind || pC->seekOp==OP_Found
- || pC->seekOp==OP_NullRow|| pC->seekOp==OP_SeekRowid);
+ || pC->seekOp==OP_Rewind || pC->seekOp==OP_Found
+ || pC->seekOp==OP_NullRow|| pC->seekOp==OP_SeekRowid
+ || pC->seekOp==OP_IfNoHope);
assert( pOp->opcode!=OP_Prev
|| pC->seekOp==OP_SeekLT || pC->seekOp==OP_SeekLE
- || pC->seekOp==OP_Last
+ || pC->seekOp==OP_Last || pC->seekOp==OP_IfNoHope
|| pC->seekOp==OP_NullRow);
rc = pOp->p4.xAdvance(pC->uc.pCursor, pOp->p3);
@@ -89663,7 +89918,7 @@ case OP_ParseSchema: {
initData.pzErrMsg = &p->zErrMsg;
initData.mInitFlags = 0;
zSql = sqlite3MPrintf(db,
- "SELECT name, rootpage, sql FROM '%q'.%s WHERE %s ORDER BY rowid",
+ "SELECT*FROM\"%w\".%s WHERE %s ORDER BY rowid",
db->aDb[iDb].zDbSName, zMaster, pOp->p4.z);
if( zSql==0 ){
rc = SQLITE_NOMEM_BKPT;
@@ -91870,11 +92125,12 @@ SQLITE_API int sqlite3_blob_close(sqlite3_blob *pBlob){
sqlite3 *db;
if( p ){
+ sqlite3_stmt *pStmt = p->pStmt;
db = p->db;
sqlite3_mutex_enter(db->mutex);
- rc = sqlite3_finalize(p->pStmt);
sqlite3DbFree(db, p);
sqlite3_mutex_leave(db->mutex);
+ rc = sqlite3_finalize(pStmt);
}else{
rc = SQLITE_OK;
}
@@ -92854,7 +93110,8 @@ static int vdbeSorterCompareText(
);
}
}else{
- if( pTask->pSorter->pKeyInfo->aSortOrder[0] ){
+ assert( !(pTask->pSorter->pKeyInfo->aSortFlags[0]&KEYINFO_ORDER_BIGNULL) );
+ if( pTask->pSorter->pKeyInfo->aSortFlags[0] ){
res = res * -1;
}
}
@@ -92922,7 +93179,8 @@ static int vdbeSorterCompareInt(
pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2
);
}
- }else if( pTask->pSorter->pKeyInfo->aSortOrder[0] ){
+ }else if( pTask->pSorter->pKeyInfo->aSortFlags[0] ){
+ assert( !(pTask->pSorter->pKeyInfo->aSortFlags[0]&KEYINFO_ORDER_BIGNULL) );
res = res * -1;
}
@@ -93037,6 +93295,7 @@ SQLITE_PRIVATE int sqlite3VdbeSorterInit(
if( pKeyInfo->nAllField<13
&& (pKeyInfo->aColl[0]==0 || pKeyInfo->aColl[0]==db->pDfltColl)
+ && (pKeyInfo->aSortFlags[0] & KEYINFO_ORDER_BIGNULL)==0
){
pSorter->typeMask = SORTER_TYPE_INTEGER | SORTER_TYPE_TEXT;
}
@@ -93753,13 +94012,16 @@ static int vdbeSorterFlushPMA(VdbeSorter *pSorter){
rc = vdbeSorterListToPMA(&pSorter->aTask[nWorker], &pSorter->list);
}else{
/* Launch a background thread for this operation */
- u8 *aMem = pTask->list.aMemory;
- void *pCtx = (void*)pTask;
+ u8 *aMem;
+ void *pCtx;
+ assert( pTask!=0 );
assert( pTask->pThread==0 && pTask->bDone==0 );
assert( pTask->list.pList==0 );
assert( pTask->list.aMemory==0 || pSorter->list.aMemory!=0 );
+ aMem = pTask->list.aMemory;
+ pCtx = (void*)pTask;
pSorter->iPrev = (u8)(pTask - pSorter->aTask);
pTask->list = pSorter->list;
pSorter->list.pList = 0;
@@ -94883,14 +95145,9 @@ static int memjrnlRead(
int iChunkOffset;
FileChunk *pChunk;
-#if defined(SQLITE_ENABLE_ATOMIC_WRITE) \
- || defined(SQLITE_ENABLE_BATCH_ATOMIC_WRITE)
if( (iAmt+iOfst)>p->endpoint.iOffset ){
return SQLITE_IOERR_SHORT_READ;
}
-#endif
-
- assert( (iAmt+iOfst)<=p->endpoint.iOffset );
assert( p->readpoint.iOffset==0 || p->readpoint.pChunk!=0 );
if( p->readpoint.iOffset!=iOfst || iOfst==0 ){
sqlite3_int64 iOff = 0;
@@ -95249,9 +95506,22 @@ SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *pVfs){
static int walkWindowList(Walker *pWalker, Window *pList){
Window *pWin;
for(pWin=pList; pWin; pWin=pWin->pNextWin){
- if( sqlite3WalkExprList(pWalker, pWin->pOrderBy) ) return WRC_Abort;
- if( sqlite3WalkExprList(pWalker, pWin->pPartition) ) return WRC_Abort;
- if( sqlite3WalkExpr(pWalker, pWin->pFilter) ) return WRC_Abort;
+ int rc;
+ rc = sqlite3WalkExprList(pWalker, pWin->pOrderBy);
+ if( rc ) return WRC_Abort;
+ rc = sqlite3WalkExprList(pWalker, pWin->pPartition);
+ if( rc ) return WRC_Abort;
+ rc = sqlite3WalkExpr(pWalker, pWin->pFilter);
+ if( rc ) return WRC_Abort;
+
+ /* The next two are purely for calls to sqlite3RenameExprUnmap()
+ ** within sqlite3WindowOffsetExpr(). Because of constraints imposed
+ ** by sqlite3WindowOffsetExpr(), they can never fail. The results do
+ ** not matter anyhow. */
+ rc = sqlite3WalkExpr(pWalker, pWin->pStart);
+ if( NEVER(rc) ) return WRC_Abort;
+ rc = sqlite3WalkExpr(pWalker, pWin->pEnd);
+ if( NEVER(rc) ) return WRC_Abort;
}
return WRC_Continue;
}
@@ -95287,18 +95557,22 @@ static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){
if( pExpr->pLeft && walkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort;
assert( pExpr->x.pList==0 || pExpr->pRight==0 );
if( pExpr->pRight ){
+ assert( !ExprHasProperty(pExpr, EP_WinFunc) );
pExpr = pExpr->pRight;
continue;
}else if( ExprHasProperty(pExpr, EP_xIsSelect) ){
+ assert( !ExprHasProperty(pExpr, EP_WinFunc) );
if( sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort;
- }else if( pExpr->x.pList ){
- if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort;
- }
+ }else{
+ if( pExpr->x.pList ){
+ if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort;
+ }
#ifndef SQLITE_OMIT_WINDOWFUNC
- if( ExprHasProperty(pExpr, EP_WinFunc) ){
- if( walkWindowList(pWalker, pExpr->y.pWin) ) return WRC_Abort;
- }
+ if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ if( walkWindowList(pWalker, pExpr->y.pWin) ) return WRC_Abort;
+ }
#endif
+ }
}
break;
}
@@ -95340,8 +95614,9 @@ SQLITE_PRIVATE int sqlite3WalkSelectExpr(Walker *pWalker, Select *p){
{
Parse *pParse = pWalker->pParse;
if( pParse && IN_RENAME_OBJECT ){
+ /* The following may return WRC_Abort if there are unresolvable
+ ** symbols (e.g. a table that does not exist) in a window definition. */
int rc = walkWindowList(pWalker, p->pWinDefn);
- assert( rc==WRC_Continue );
return rc;
}
}
@@ -95513,6 +95788,13 @@ static void resolveAlias(
pExpr->u.zToken = sqlite3DbStrDup(db, pExpr->u.zToken);
pExpr->flags |= EP_MemToken;
}
+ if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ if( pExpr->y.pWin!=0 ){
+ pExpr->y.pWin->pOwner = pExpr;
+ }else{
+ assert( db->mallocFailed );
+ }
+ }
sqlite3DbFree(db, pDup);
}
ExprSetProperty(pExpr, EP_Alias);
@@ -95798,7 +96080,7 @@ static int lookupName(
{
#ifndef SQLITE_OMIT_TRIGGER
if( iCol<0 ){
- pExpr->affinity = SQLITE_AFF_INTEGER;
+ pExpr->affExpr = SQLITE_AFF_INTEGER;
}else if( pExpr->iTable==0 ){
testcase( iCol==31 );
testcase( iCol==32 );
@@ -95830,7 +96112,7 @@ static int lookupName(
){
cnt = 1;
pExpr->iColumn = -1;
- pExpr->affinity = SQLITE_AFF_INTEGER;
+ pExpr->affExpr = SQLITE_AFF_INTEGER;
}
/*
@@ -96106,7 +96388,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
pExpr->y.pTab = pItem->pTab;
pExpr->iTable = pItem->iCursor;
pExpr->iColumn = -1;
- pExpr->affinity = SQLITE_AFF_INTEGER;
+ pExpr->affExpr = SQLITE_AFF_INTEGER;
break;
}
#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT)
@@ -96166,7 +96448,9 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
FuncDef *pDef; /* Information about the function */
u8 enc = ENC(pParse->db); /* The database encoding */
int savedAllowFlags = (pNC->ncFlags & (NC_AllowAgg | NC_AllowWin));
-
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ Window *pWin = (IsWindowFunc(pExpr) ? pExpr->y.pWin : 0);
+#endif
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
zId = pExpr->u.zToken;
nId = sqlite3Strlen30(zId);
@@ -96238,6 +96522,15 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
** SQL is being compiled using sqlite3NestedParse() */
no_such_func = 1;
pDef = 0;
+ }else
+ if( (pDef->funcFlags & SQLITE_FUNC_DIRECT)!=0
+ && ExprHasProperty(pExpr, EP_Indirect)
+ && !IN_RENAME_OBJECT
+ ){
+ /* Functions tagged with SQLITE_DIRECTONLY may not be used
+ ** inside of triggers and views */
+ sqlite3ErrorMsg(pParse, "%s() prohibited in triggers and views",
+ pDef->zName);
}
}
@@ -96247,18 +96540,18 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
|| (pDef->xValue==0 && pDef->xInverse==0)
|| (pDef->xValue && pDef->xInverse && pDef->xSFunc && pDef->xFinalize)
);
- if( pDef && pDef->xValue==0 && ExprHasProperty(pExpr, EP_WinFunc) ){
+ if( pDef && pDef->xValue==0 && pWin ){
sqlite3ErrorMsg(pParse,
"%.*s() may not be used as a window function", nId, zId
);
pNC->nErr++;
}else if(
(is_agg && (pNC->ncFlags & NC_AllowAgg)==0)
- || (is_agg && (pDef->funcFlags&SQLITE_FUNC_WINDOW) && !pExpr->y.pWin)
- || (is_agg && pExpr->y.pWin && (pNC->ncFlags & NC_AllowWin)==0)
+ || (is_agg && (pDef->funcFlags&SQLITE_FUNC_WINDOW) && !pWin)
+ || (is_agg && pWin && (pNC->ncFlags & NC_AllowWin)==0)
){
const char *zType;
- if( (pDef->funcFlags & SQLITE_FUNC_WINDOW) || pExpr->y.pWin ){
+ if( (pDef->funcFlags & SQLITE_FUNC_WINDOW) || pWin ){
zType = "window";
}else{
zType = "aggregate";
@@ -96286,34 +96579,44 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
nId, zId);
pNC->nErr++;
}
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ else if( is_agg==0 && ExprHasProperty(pExpr, EP_WinFunc) ){
+ sqlite3ErrorMsg(pParse,
+ "FILTER may not be used with non-aggregate %.*s()",
+ nId, zId
+ );
+ pNC->nErr++;
+ }
+#endif
if( is_agg ){
/* Window functions may not be arguments of aggregate functions.
** Or arguments of other window functions. But aggregate functions
** may be arguments for window functions. */
#ifndef SQLITE_OMIT_WINDOWFUNC
- pNC->ncFlags &= ~(NC_AllowWin | (!pExpr->y.pWin ? NC_AllowAgg : 0));
+ pNC->ncFlags &= ~(NC_AllowWin | (!pWin ? NC_AllowAgg : 0));
#else
pNC->ncFlags &= ~NC_AllowAgg;
#endif
}
}
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ else if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ is_agg = 1;
+ }
+#endif
sqlite3WalkExprList(pWalker, pList);
if( is_agg ){
#ifndef SQLITE_OMIT_WINDOWFUNC
- if( pExpr->y.pWin ){
+ if( pWin ){
Select *pSel = pNC->pWinSelect;
+ assert( pWin==pExpr->y.pWin );
if( IN_RENAME_OBJECT==0 ){
- sqlite3WindowUpdate(pParse, pSel->pWinDefn, pExpr->y.pWin, pDef);
- }
- sqlite3WalkExprList(pWalker, pExpr->y.pWin->pPartition);
- sqlite3WalkExprList(pWalker, pExpr->y.pWin->pOrderBy);
- sqlite3WalkExpr(pWalker, pExpr->y.pWin->pFilter);
- if( 0==pSel->pWin
- || 0==sqlite3WindowCompare(pParse, pSel->pWin, pExpr->y.pWin)
- ){
- pExpr->y.pWin->pNextWin = pSel->pWin;
- pSel->pWin = pExpr->y.pWin;
+ sqlite3WindowUpdate(pParse, pSel->pWinDefn, pWin, pDef);
}
+ sqlite3WalkExprList(pWalker, pWin->pPartition);
+ sqlite3WalkExprList(pWalker, pWin->pOrderBy);
+ sqlite3WalkExpr(pWalker, pWin->pFilter);
+ sqlite3WindowLink(pSel, pWin);
pNC->ncFlags |= NC_HasWin;
}else
#endif /* SQLITE_OMIT_WINDOWFUNC */
@@ -96321,12 +96624,17 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
NameContext *pNC2 = pNC;
pExpr->op = TK_AGG_FUNCTION;
pExpr->op2 = 0;
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ sqlite3WalkExpr(pWalker, pExpr->y.pWin->pFilter);
+ }
+#endif
while( pNC2 && !sqlite3FunctionUsesThisSrc(pExpr, pNC2->pSrcList) ){
pExpr->op2++;
pNC2 = pNC2->pNext;
}
- assert( pDef!=0 );
- if( pNC2 ){
+ assert( pDef!=0 || IN_RENAME_OBJECT );
+ if( pNC2 && pDef ){
assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg );
testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 );
pNC2->ncFlags |= NC_HasAgg | (pDef->funcFlags & SQLITE_FUNC_MINMAX);
@@ -96364,7 +96672,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
}
case TK_IS:
case TK_ISNOT: {
- Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight);
+ Expr *pRight = sqlite3ExprSkipCollateAndLikely(pExpr->pRight);
assert( !ExprHasProperty(pExpr, EP_Reduced) );
/* Handle special cases of "x IS TRUE", "x IS FALSE", "x IS NOT TRUE",
** and "x IS NOT FALSE". */
@@ -96575,7 +96883,7 @@ static int resolveCompoundOrderBy(
int iCol = -1;
Expr *pE, *pDup;
if( pItem->done ) continue;
- pE = sqlite3ExprSkipCollate(pItem->pExpr);
+ pE = sqlite3ExprSkipCollateAndLikely(pItem->pExpr);
if( sqlite3ExprIsInteger(pE, &iCol) ){
if( iCol<=0 || iCol>pEList->nExpr ){
resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr);
@@ -96669,7 +96977,7 @@ SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(
ExprList *pEList;
struct ExprList_item *pItem;
- if( pOrderBy==0 || pParse->db->mallocFailed ) return 0;
+ if( pOrderBy==0 || pParse->db->mallocFailed || IN_RENAME_OBJECT ) return 0;
if( pOrderBy->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){
sqlite3ErrorMsg(pParse, "too many terms in %s BY clause", zType);
return 1;
@@ -96691,17 +96999,13 @@ SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(
#ifndef SQLITE_OMIT_WINDOWFUNC
/*
-** Walker callback for resolveRemoveWindows().
+** Walker callback for windowRemoveExprFromSelect().
*/
static int resolveRemoveWindowsCb(Walker *pWalker, Expr *pExpr){
+ UNUSED_PARAMETER(pWalker);
if( ExprHasProperty(pExpr, EP_WinFunc) ){
- Window **pp;
- for(pp=&pWalker->u.pSelect->pWin; *pp; pp=&(*pp)->pNextWin){
- if( *pp==pExpr->y.pWin ){
- *pp = (*pp)->pNextWin;
- break;
- }
- }
+ Window *pWin = pExpr->y.pWin;
+ sqlite3WindowUnlinkFromSelect(pWin);
}
return WRC_Continue;
}
@@ -96710,16 +97014,18 @@ static int resolveRemoveWindowsCb(Walker *pWalker, Expr *pExpr){
** Remove any Window objects owned by the expression pExpr from the
** Select.pWin list of Select object pSelect.
*/
-static void resolveRemoveWindows(Select *pSelect, Expr *pExpr){
- Walker sWalker;
- memset(&sWalker, 0, sizeof(Walker));
- sWalker.xExprCallback = resolveRemoveWindowsCb;
- sWalker.u.pSelect = pSelect;
- sqlite3WalkExpr(&sWalker, pExpr);
+static void windowRemoveExprFromSelect(Select *pSelect, Expr *pExpr){
+ if( pSelect->pWin ){
+ Walker sWalker;
+ memset(&sWalker, 0, sizeof(Walker));
+ sWalker.xExprCallback = resolveRemoveWindowsCb;
+ sWalker.u.pSelect = pSelect;
+ sqlite3WalkExpr(&sWalker, pExpr);
+ }
}
#else
-# define resolveRemoveWindows(x,y)
-#endif
+# define windowRemoveExprFromSelect(a, b)
+#endif /* SQLITE_OMIT_WINDOWFUNC */
/*
** pOrderBy is an ORDER BY or GROUP BY clause in SELECT statement pSelect.
@@ -96756,7 +97062,7 @@ static int resolveOrderGroupBy(
pParse = pNC->pParse;
for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
Expr *pE = pItem->pExpr;
- Expr *pE2 = sqlite3ExprSkipCollate(pE);
+ Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pE);
if( zType[0]!='G' ){
iCol = resolveAsName(pParse, pSelect->pEList, pE2);
if( iCol>0 ){
@@ -96790,7 +97096,7 @@ static int resolveOrderGroupBy(
/* Since this expresion is being changed into a reference
** to an identical expression in the result set, remove all Window
** objects belonging to the expression from the Select.pWin list. */
- resolveRemoveWindows(pSelect, pE);
+ windowRemoveExprFromSelect(pSelect, pE);
pItem->u.x.iOrderByCol = j+1;
}
}
@@ -97258,7 +97564,6 @@ SQLITE_PRIVATE char sqlite3TableColumnAffinity(Table *pTab, int iCol){
*/
SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr){
int op;
- if( pExpr->flags & EP_Generic ) return 0;
while( ExprHasProperty(pExpr, EP_Skip) ){
assert( pExpr->op==TK_COLLATE );
pExpr = pExpr->pLeft;
@@ -97285,7 +97590,7 @@ SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr){
pExpr->pLeft->x.pSelect->pEList->a[pExpr->iColumn].pExpr
);
}
- return pExpr->affinity;
+ return pExpr->affExpr;
}
/*
@@ -97320,10 +97625,22 @@ SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse *pParse, Expr *pExpr, con
}
/*
-** Skip over any TK_COLLATE operators and any unlikely()
-** or likelihood() function at the root of an expression.
+** Skip over any TK_COLLATE operators.
*/
SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr *pExpr){
+ while( pExpr && ExprHasProperty(pExpr, EP_Skip) ){
+ assert( pExpr->op==TK_COLLATE );
+ pExpr = pExpr->pLeft;
+ }
+ return pExpr;
+}
+
+/*
+** Skip over any TK_COLLATE operators and/or any unlikely()
+** or likelihood() or likely() functions at the root of an
+** expression.
+*/
+SQLITE_PRIVATE Expr *sqlite3ExprSkipCollateAndLikely(Expr *pExpr){
while( pExpr && ExprHasProperty(pExpr, EP_Skip|EP_Unlikely) ){
if( ExprHasProperty(pExpr, EP_Unlikely) ){
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
@@ -97358,7 +97675,6 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){
Expr *p = pExpr;
while( p ){
int op = p->op;
- if( p->flags & EP_Generic ) break;
if( op==TK_REGISTER ) op = p->op2;
if( (op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_TRIGGER)
&& p->y.pTab!=0
@@ -97444,7 +97760,7 @@ SQLITE_PRIVATE int sqlite3ExprCollSeqMatch(Parse *pParse, Expr *pE1, Expr *pE2){
*/
SQLITE_PRIVATE char sqlite3CompareAffinity(Expr *pExpr, char aff2){
char aff1 = sqlite3ExprAffinity(pExpr);
- if( aff1 && aff2 ){
+ if( aff1>SQLITE_AFF_NONE && aff2>SQLITE_AFF_NONE ){
/* Both sides of the comparison are columns. If one has numeric
** affinity, use that. Otherwise use no affinity.
*/
@@ -97453,15 +97769,10 @@ SQLITE_PRIVATE char sqlite3CompareAffinity(Expr *pExpr, char aff2){
}else{
return SQLITE_AFF_BLOB;
}
- }else if( !aff1 && !aff2 ){
- /* Neither side of the comparison is a column. Compare the
- ** results directly.
- */
- return SQLITE_AFF_BLOB;
}else{
/* One side is a column, the other is not. Use the columns affinity. */
- assert( aff1==0 || aff2==0 );
- return (aff1 + aff2);
+ assert( aff1<=SQLITE_AFF_NONE || aff2<=SQLITE_AFF_NONE );
+ return (aff1<=SQLITE_AFF_NONE ? aff2 : aff1) | SQLITE_AFF_NONE;
}
}
@@ -97494,14 +97805,13 @@ static char comparisonAffinity(Expr *pExpr){
*/
SQLITE_PRIVATE int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity){
char aff = comparisonAffinity(pExpr);
- switch( aff ){
- case SQLITE_AFF_BLOB:
- return 1;
- case SQLITE_AFF_TEXT:
- return idx_affinity==SQLITE_AFF_TEXT;
- default:
- return sqlite3IsNumericAffinity(idx_affinity);
+ if( aff<SQLITE_AFF_TEXT ){
+ return 1;
+ }
+ if( aff==SQLITE_AFF_TEXT ){
+ return idx_affinity==SQLITE_AFF_TEXT;
}
+ return sqlite3IsNumericAffinity(idx_affinity);
}
/*
@@ -98115,7 +98425,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprAnd(Parse *pParse, Expr *pLeft, Expr *pRight){
}else if( ExprAlwaysFalse(pLeft) || ExprAlwaysFalse(pRight) ){
sqlite3ExprUnmapAndDelete(pParse, pLeft);
sqlite3ExprUnmapAndDelete(pParse, pRight);
- return sqlite3ExprAlloc(db, TK_INTEGER, &sqlite3IntTokens[0], 0);
+ return sqlite3Expr(db, TK_INTEGER, "0");
}else{
return sqlite3PExpr(pParse, TK_AND, pLeft, pRight);
}
@@ -98254,15 +98564,18 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){
assert( p->x.pList==0 || p->pRight==0 );
if( p->pLeft && p->op!=TK_SELECT_COLUMN ) sqlite3ExprDeleteNN(db, p->pLeft);
if( p->pRight ){
+ assert( !ExprHasProperty(p, EP_WinFunc) );
sqlite3ExprDeleteNN(db, p->pRight);
}else if( ExprHasProperty(p, EP_xIsSelect) ){
+ assert( !ExprHasProperty(p, EP_WinFunc) );
sqlite3SelectDelete(db, p->x.pSelect);
}else{
sqlite3ExprListDelete(db, p->x.pList);
- }
- if( ExprHasProperty(p, EP_WinFunc) ){
- assert( p->op==TK_FUNCTION );
- sqlite3WindowDelete(db, p->y.pWin);
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ if( ExprHasProperty(p, EP_WinFunc) ){
+ sqlite3WindowDelete(db, p->y.pWin);
+ }
+#endif
}
}
if( ExprHasProperty(p, EP_MemToken) ) sqlite3DbFree(db, p->u.zToken);
@@ -98298,16 +98611,6 @@ static int exprStructSize(Expr *p){
}
/*
-** Copy the complete content of an Expr node, taking care not to read
-** past the end of the structure for a reduced-size version of the source
-** Expr.
-*/
-static void exprNodeCopy(Expr *pDest, Expr *pSrc){
- memset(pDest, 0, sizeof(Expr));
- memcpy(pDest, pSrc, exprStructSize(pSrc));
-}
-
-/*
** The dupedExpr*Size() routines each return the number of bytes required
** to store a copy of an expression or expression tree. They differ in
** how much of the tree is measured.
@@ -98546,10 +98849,13 @@ static With *withDup(sqlite3 *db, With *p){
** objects found there, assembling them onto the linked list at Select->pWin.
*/
static int gatherSelectWindowsCallback(Walker *pWalker, Expr *pExpr){
- if( pExpr->op==TK_FUNCTION && pExpr->y.pWin!=0 ){
- assert( ExprHasProperty(pExpr, EP_WinFunc) );
- pExpr->y.pWin->pNextWin = pWalker->u.pSelect->pWin;
- pWalker->u.pSelect->pWin = pExpr->y.pWin;
+ if( pExpr->op==TK_FUNCTION && ExprHasProperty(pExpr, EP_WinFunc) ){
+ Select *pSelect = pWalker->u.pSelect;
+ Window *pWin = pExpr->y.pWin;
+ assert( pWin );
+ assert( IsWindowFunc(pExpr) );
+ assert( pWin->ppThis==0 );
+ sqlite3WindowLink(pSelect, pWin);
}
return WRC_Continue;
}
@@ -98623,8 +98929,9 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags)
}
pItem->zName = sqlite3DbStrDup(db, pOldItem->zName);
pItem->zSpan = sqlite3DbStrDup(db, pOldItem->zSpan);
- pItem->sortOrder = pOldItem->sortOrder;
+ pItem->sortFlags = pOldItem->sortFlags;
pItem->done = 0;
+ pItem->bNulls = pOldItem->bNulls;
pItem->bSpanIsTab = pOldItem->bSpanIsTab;
pItem->bSorterRef = pOldItem->bSorterRef;
pItem->u = pOldItem->u;
@@ -98735,7 +99042,7 @@ SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, Select *pDup, int flags){
#ifndef SQLITE_OMIT_WINDOWFUNC
pNew->pWin = 0;
pNew->pWinDefn = sqlite3WindowListDup(db, p->pWinDefn);
- if( p->pWin ) gatherSelectWindows(pNew);
+ if( p->pWin && db->mallocFailed==0 ) gatherSelectWindows(pNew);
#endif
pNew->selId = p->selId;
*pp = pNew;
@@ -98844,6 +99151,10 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector(
for(i=0; i<pColumns->nId; i++){
Expr *pSubExpr = sqlite3ExprForVectorField(pParse, pExpr, i);
+ assert( pSubExpr!=0 || db->mallocFailed );
+ assert( pSubExpr==0 || pSubExpr->iTable==0 );
+ if( pSubExpr==0 ) continue;
+ pSubExpr->iTable = pColumns->nId;
pList = sqlite3ExprListAppend(pParse, pList, pSubExpr);
if( pList ){
assert( pList->nExpr==iFirst+i+1 );
@@ -98876,15 +99187,34 @@ vector_append_error:
/*
** Set the sort order for the last element on the given ExprList.
*/
-SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList *p, int iSortOrder){
+SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList *p, int iSortOrder, int eNulls){
+ struct ExprList_item *pItem;
if( p==0 ) return;
- assert( SQLITE_SO_UNDEFINED<0 && SQLITE_SO_ASC>=0 && SQLITE_SO_DESC>0 );
assert( p->nExpr>0 );
- if( iSortOrder<0 ){
- assert( p->a[p->nExpr-1].sortOrder==SQLITE_SO_ASC );
- return;
+
+ assert( SQLITE_SO_UNDEFINED<0 && SQLITE_SO_ASC==0 && SQLITE_SO_DESC>0 );
+ assert( iSortOrder==SQLITE_SO_UNDEFINED
+ || iSortOrder==SQLITE_SO_ASC
+ || iSortOrder==SQLITE_SO_DESC
+ );
+ assert( eNulls==SQLITE_SO_UNDEFINED
+ || eNulls==SQLITE_SO_ASC
+ || eNulls==SQLITE_SO_DESC
+ );
+
+ pItem = &p->a[p->nExpr-1];
+ assert( pItem->bNulls==0 );
+ if( iSortOrder==SQLITE_SO_UNDEFINED ){
+ iSortOrder = SQLITE_SO_ASC;
+ }
+ pItem->sortFlags = (u8)iSortOrder;
+
+ if( eNulls!=SQLITE_SO_UNDEFINED ){
+ pItem->bNulls = 1;
+ if( iSortOrder!=eNulls ){
+ pItem->sortFlags |= KEYINFO_ORDER_BIGNULL;
+ }
}
- p->a[p->nExpr-1].sortOrder = (u8)iSortOrder;
}
/*
@@ -99383,27 +99713,30 @@ SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr *p){
*/
SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr *p, char aff){
u8 op;
+ int unaryMinus = 0;
if( aff==SQLITE_AFF_BLOB ) return 1;
- while( p->op==TK_UPLUS || p->op==TK_UMINUS ){ p = p->pLeft; }
+ while( p->op==TK_UPLUS || p->op==TK_UMINUS ){
+ if( p->op==TK_UMINUS ) unaryMinus = 1;
+ p = p->pLeft;
+ }
op = p->op;
if( op==TK_REGISTER ) op = p->op2;
switch( op ){
case TK_INTEGER: {
- return aff==SQLITE_AFF_INTEGER || aff==SQLITE_AFF_NUMERIC;
+ return aff>=SQLITE_AFF_NUMERIC;
}
case TK_FLOAT: {
- return aff==SQLITE_AFF_REAL || aff==SQLITE_AFF_NUMERIC;
+ return aff>=SQLITE_AFF_NUMERIC;
}
case TK_STRING: {
- return aff==SQLITE_AFF_TEXT;
+ return !unaryMinus && aff==SQLITE_AFF_TEXT;
}
case TK_BLOB: {
- return 1;
+ return !unaryMinus;
}
case TK_COLUMN: {
assert( p->iTable>=0 ); /* p cannot be part of a CHECK constraint */
- return p->iColumn<0
- && (aff==SQLITE_AFF_INTEGER || aff==SQLITE_AFF_NUMERIC);
+ return aff>=SQLITE_AFF_NUMERIC && p->iColumn<0;
}
default: {
return 0;
@@ -99586,7 +99919,7 @@ static int sqlite3InRhsIsConstant(Expr *pIn){
#ifndef SQLITE_OMIT_SUBQUERY
SQLITE_PRIVATE int sqlite3FindInIndex(
Parse *pParse, /* Parsing context */
- Expr *pX, /* The right-hand side (RHS) of the IN operator */
+ Expr *pX, /* The IN expression */
u32 inFlags, /* IN_INDEX_LOOP, _MEMBERSHIP, and/or _NOOP_OK */
int *prRhsHasNull, /* Register holding NULL status. See notes */
int *aiMap, /* Mapping from Index fields to RHS fields */
@@ -100011,9 +100344,9 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN(
int i;
ExprList *pList = pExpr->x.pList;
struct ExprList_item *pItem;
- int r1, r2, r3;
+ int r1, r2;
affinity = sqlite3ExprAffinity(pLeft);
- if( !affinity ){
+ if( affinity<=SQLITE_AFF_NONE ){
affinity = SQLITE_AFF_BLOB;
}
if( pKeyInfo ){
@@ -100039,9 +100372,9 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN(
}
/* Evaluate the expression and insert it into the temp table */
- r3 = sqlite3ExprCodeTarget(pParse, pE2, r1);
- sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1);
- sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iTab, r2, r3, 1);
+ sqlite3ExprCode(pParse, pE2, r1);
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, r1, 1, r2, &affinity, 1);
+ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iTab, r2, r1, 1);
}
sqlite3ReleaseTempReg(pParse, r1);
sqlite3ReleaseTempReg(pParse, r2);
@@ -100054,6 +100387,7 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN(
/* Subroutine return */
sqlite3VdbeAddOp1(v, OP_Return, pExpr->y.sub.regReturn);
sqlite3VdbeChangeP1(v, pExpr->y.sub.iAddr-1, sqlite3VdbeCurrentAddr(v)-1);
+ sqlite3ClearTempRegCache(pParse);
}
}
#endif /* SQLITE_OMIT_SUBQUERY */
@@ -100067,7 +100401,7 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN(
**
** The pExpr parameter is the SELECT or EXISTS operator to be coded.
**
-** The register that holds the result. For a multi-column SELECT,
+** Return the register that holds the result. For a multi-column SELECT,
** the result is stored in a contiguous array of registers and the
** return value is the register of the left-most result column.
** Return 0 if an error occurs.
@@ -100145,11 +100479,21 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
sqlite3VdbeAddOp2(v, OP_Integer, 0, dest.iSDParm);
VdbeComment((v, "Init EXISTS result"));
}
- pLimit = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[1], 0);
if( pSel->pLimit ){
- sqlite3ExprDelete(pParse->db, pSel->pLimit->pLeft);
+ /* The subquery already has a limit. If the pre-existing limit is X
+ ** then make the new limit X<>0 so that the new limit is either 1 or 0 */
+ sqlite3 *db = pParse->db;
+ pLimit = sqlite3Expr(db, TK_INTEGER, "0");
+ if( pLimit ){
+ pLimit->affExpr = SQLITE_AFF_NUMERIC;
+ pLimit = sqlite3PExpr(pParse, TK_NE,
+ sqlite3ExprDup(db, pSel->pLimit->pLeft, 0), pLimit);
+ }
+ sqlite3ExprDelete(db, pSel->pLimit->pLeft);
pSel->pLimit->pLeft = pLimit;
}else{
+ /* If there is no pre-existing limit add a limit of 1 */
+ pLimit = sqlite3Expr(pParse->db, TK_INTEGER, "1");
pSel->pLimit = sqlite3PExpr(pParse, TK_LIMIT, pLimit, 0);
}
pSel->iLimit = 0;
@@ -100164,6 +100508,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *pParse, Expr *pExpr){
/* Subroutine return */
sqlite3VdbeAddOp1(v, OP_Return, pExpr->y.sub.regReturn);
sqlite3VdbeChangeP1(v, pExpr->y.sub.iAddr-1, sqlite3VdbeCurrentAddr(v)-1);
+ sqlite3ClearTempRegCache(pParse);
}
return rReg;
@@ -100311,13 +100656,21 @@ static void sqlite3ExprCodeIN(
int r2, regToFree;
int regCkNull = 0;
int ii;
+ int bLhsReal; /* True if the LHS of the IN has REAL affinity */
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
if( destIfNull!=destIfFalse ){
regCkNull = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_BitAnd, rLhs, rLhs, regCkNull);
}
+ bLhsReal = sqlite3ExprAffinity(pExpr->pLeft)==SQLITE_AFF_REAL;
for(ii=0; ii<pList->nExpr; ii++){
- r2 = sqlite3ExprCodeTemp(pParse, pList->a[ii].pExpr, &regToFree);
+ if( bLhsReal ){
+ r2 = regToFree = sqlite3GetTempReg(pParse);
+ sqlite3ExprCode(pParse, pList->a[ii].pExpr, r2);
+ sqlite3VdbeAddOp4(v, OP_Affinity, r2, 1, 0, "E", P4_STATIC);
+ }else{
+ r2 = sqlite3ExprCodeTemp(pParse, pList->a[ii].pExpr, &regToFree);
+ }
if( regCkNull && sqlite3ExprCanBeNull(pList->a[ii].pExpr) ){
sqlite3VdbeAddOp3(v, OP_BitAnd, regCkNull, r2, regCkNull);
}
@@ -100602,7 +100955,7 @@ SQLITE_PRIVATE void sqlite3ExprCodeMove(Parse *pParse, int iFrom, int iTo, int n
** the correct value for the expression.
*/
static void exprToRegister(Expr *pExpr, int iReg){
- Expr *p = sqlite3ExprSkipCollate(pExpr);
+ Expr *p = sqlite3ExprSkipCollateAndLikely(pExpr);
p->op2 = p->op;
p->op = TK_REGISTER;
p->iTable = iReg;
@@ -100703,7 +101056,7 @@ expr_code_doover:
*/
int iReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft,target);
int aff = sqlite3TableColumnAffinity(pExpr->y.pTab, pExpr->iColumn);
- if( aff!=SQLITE_AFF_BLOB ){
+ if( aff>SQLITE_AFF_BLOB ){
static const char zAff[] = "B\000C\000D\000E";
assert( SQLITE_AFF_BLOB=='A' );
assert( SQLITE_AFF_TEXT=='B' );
@@ -100719,7 +101072,19 @@ expr_code_doover:
if( iTab<0 ){
if( pParse->iSelfTab<0 ){
/* Generating CHECK constraints or inserting into partial index */
- return pExpr->iColumn - pParse->iSelfTab;
+ assert( pExpr->y.pTab!=0 );
+ assert( pExpr->iColumn>=XN_ROWID );
+ assert( pExpr->iColumn<pExpr->y.pTab->nCol );
+ if( pExpr->iColumn>=0
+ && pExpr->y.pTab->aCol[pExpr->iColumn].affinity==SQLITE_AFF_REAL
+ ){
+ sqlite3VdbeAddOp2(v, OP_SCopy, pExpr->iColumn - pParse->iSelfTab,
+ target);
+ sqlite3VdbeAddOp1(v, OP_RealAffinity, target);
+ return target;
+ }else{
+ return pExpr->iColumn - pParse->iSelfTab;
+ }
}else{
/* Coding an expression that is part of an index where column names
** in the index refer to the table to which the index belongs */
@@ -101006,7 +101371,7 @@ expr_code_doover:
assert( nFarg==1 );
aff = sqlite3ExprAffinity(pFarg->a[0].pExpr);
sqlite3VdbeLoadString(v, target,
- aff ? azAff[aff-SQLITE_AFF_BLOB] : "none");
+ (aff<=SQLITE_AFF_NONE) ? "none" : azAff[aff-SQLITE_AFF_BLOB]);
return target;
}
#endif
@@ -101114,8 +101479,8 @@ expr_code_doover:
pExpr->pLeft->iTable = sqlite3CodeSubselect(pParse, pExpr->pLeft);
}
assert( pExpr->iTable==0 || pExpr->pLeft->op==TK_SELECT );
- if( pExpr->iTable
- && pExpr->iTable!=(n = sqlite3ExprVectorSize(pExpr->pLeft))
+ if( pExpr->iTable!=0
+ && pExpr->iTable!=(n = sqlite3ExprVectorSize(pExpr->pLeft))
){
sqlite3ErrorMsg(pParse, "%d columns assigned %d values",
pExpr->iTable, n);
@@ -101218,10 +101583,23 @@ expr_code_doover:
break;
}
+ /* TK_IF_NULL_ROW Expr nodes are inserted ahead of expressions
+ ** that derive from the right-hand table of a LEFT JOIN. The
+ ** Expr.iTable value is the table number for the right-hand table.
+ ** The expression is only evaluated if that table is not currently
+ ** on a LEFT JOIN NULL row.
+ */
case TK_IF_NULL_ROW: {
int addrINR;
+ u8 okConstFactor = pParse->okConstFactor;
addrINR = sqlite3VdbeAddOp1(v, OP_IfNullRow, pExpr->iTable);
+ /* Temporarily disable factoring of constant expressions, since
+ ** even though expressions may appear to be constant, they are not
+ ** really constant because they originate from the right-hand side
+ ** of a LEFT JOIN. */
+ pParse->okConstFactor = 0;
inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
+ pParse->okConstFactor = okConstFactor;
sqlite3VdbeJumpHere(v, addrINR);
sqlite3VdbeChangeP3(v, addrINR, inReg);
break;
@@ -101258,6 +101636,8 @@ expr_code_doover:
Expr opCompare; /* The X==Ei expression */
Expr *pX; /* The X expression */
Expr *pTest = 0; /* X==Ei (form A) or just Ei (form B) */
+ Expr *pDel = 0;
+ sqlite3 *db = pParse->db;
assert( !ExprHasProperty(pExpr, EP_xIsSelect) && pExpr->x.pList );
assert(pExpr->x.pList->nExpr > 0);
@@ -101266,13 +101646,17 @@ expr_code_doover:
nExpr = pEList->nExpr;
endLabel = sqlite3VdbeMakeLabel(pParse);
if( (pX = pExpr->pLeft)!=0 ){
- exprNodeCopy(&tempX, pX);
+ pDel = sqlite3ExprDup(db, pX, 0);
+ if( db->mallocFailed ){
+ sqlite3ExprDelete(db, pDel);
+ break;
+ }
testcase( pX->op==TK_COLUMN );
- exprToRegister(&tempX, exprCodeVector(pParse, &tempX, &regFree1));
+ exprToRegister(pDel, exprCodeVector(pParse, pDel, &regFree1));
testcase( regFree1==0 );
memset(&opCompare, 0, sizeof(opCompare));
opCompare.op = TK_EQ;
- opCompare.pLeft = &tempX;
+ opCompare.pLeft = pDel;
pTest = &opCompare;
/* Ticket b351d95f9cd5ef17e9d9dbae18f5ca8611190001:
** The value in regFree1 might get SCopy-ed into the file result.
@@ -101300,32 +101684,33 @@ expr_code_doover:
}else{
sqlite3VdbeAddOp2(v, OP_Null, 0, target);
}
+ sqlite3ExprDelete(db, pDel);
sqlite3VdbeResolveLabel(v, endLabel);
break;
}
#ifndef SQLITE_OMIT_TRIGGER
case TK_RAISE: {
- assert( pExpr->affinity==OE_Rollback
- || pExpr->affinity==OE_Abort
- || pExpr->affinity==OE_Fail
- || pExpr->affinity==OE_Ignore
+ assert( pExpr->affExpr==OE_Rollback
+ || pExpr->affExpr==OE_Abort
+ || pExpr->affExpr==OE_Fail
+ || pExpr->affExpr==OE_Ignore
);
if( !pParse->pTriggerTab ){
sqlite3ErrorMsg(pParse,
"RAISE() may only be used within a trigger-program");
return 0;
}
- if( pExpr->affinity==OE_Abort ){
+ if( pExpr->affExpr==OE_Abort ){
sqlite3MayAbort(pParse);
}
assert( !ExprHasProperty(pExpr, EP_IntValue) );
- if( pExpr->affinity==OE_Ignore ){
+ if( pExpr->affExpr==OE_Ignore ){
sqlite3VdbeAddOp4(
v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0);
VdbeCoverage(v);
}else{
sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_TRIGGER,
- pExpr->affinity, pExpr->u.zToken, 0, 0);
+ pExpr->affExpr, pExpr->u.zToken, 0, 0);
}
break;
@@ -101390,7 +101775,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeAtInit(
*/
SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){
int r2;
- pExpr = sqlite3ExprSkipCollate(pExpr);
+ pExpr = sqlite3ExprSkipCollateAndLikely(pExpr);
if( ConstFactorOk(pParse)
&& pExpr->op!=TK_REGISTER
&& sqlite3ExprIsConstantNotJoin(pExpr)
@@ -101581,40 +101966,44 @@ static void exprCodeBetween(
void (*xJump)(Parse*,Expr*,int,int), /* Action to take */
int jumpIfNull /* Take the jump if the BETWEEN is NULL */
){
- Expr exprAnd; /* The AND operator in x>=y AND x<=z */
+ Expr exprAnd; /* The AND operator in x>=y AND x<=z */
Expr compLeft; /* The x>=y term */
Expr compRight; /* The x<=z term */
- Expr exprX; /* The x subexpression */
int regFree1 = 0; /* Temporary use register */
+ Expr *pDel = 0;
+ sqlite3 *db = pParse->db;
memset(&compLeft, 0, sizeof(Expr));
memset(&compRight, 0, sizeof(Expr));
memset(&exprAnd, 0, sizeof(Expr));
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
- exprNodeCopy(&exprX, pExpr->pLeft);
- exprAnd.op = TK_AND;
- exprAnd.pLeft = &compLeft;
- exprAnd.pRight = &compRight;
- compLeft.op = TK_GE;
- compLeft.pLeft = &exprX;
- compLeft.pRight = pExpr->x.pList->a[0].pExpr;
- compRight.op = TK_LE;
- compRight.pLeft = &exprX;
- compRight.pRight = pExpr->x.pList->a[1].pExpr;
- exprToRegister(&exprX, exprCodeVector(pParse, &exprX, &regFree1));
- if( xJump ){
- xJump(pParse, &exprAnd, dest, jumpIfNull);
- }else{
- /* Mark the expression is being from the ON or USING clause of a join
- ** so that the sqlite3ExprCodeTarget() routine will not attempt to move
- ** it into the Parse.pConstExpr list. We should use a new bit for this,
- ** for clarity, but we are out of bits in the Expr.flags field so we
- ** have to reuse the EP_FromJoin bit. Bummer. */
- exprX.flags |= EP_FromJoin;
- sqlite3ExprCodeTarget(pParse, &exprAnd, dest);
+ pDel = sqlite3ExprDup(db, pExpr->pLeft, 0);
+ if( db->mallocFailed==0 ){
+ exprAnd.op = TK_AND;
+ exprAnd.pLeft = &compLeft;
+ exprAnd.pRight = &compRight;
+ compLeft.op = TK_GE;
+ compLeft.pLeft = pDel;
+ compLeft.pRight = pExpr->x.pList->a[0].pExpr;
+ compRight.op = TK_LE;
+ compRight.pLeft = pDel;
+ compRight.pRight = pExpr->x.pList->a[1].pExpr;
+ exprToRegister(pDel, exprCodeVector(pParse, pDel, &regFree1));
+ if( xJump ){
+ xJump(pParse, &exprAnd, dest, jumpIfNull);
+ }else{
+ /* Mark the expression is being from the ON or USING clause of a join
+ ** so that the sqlite3ExprCodeTarget() routine will not attempt to move
+ ** it into the Parse.pConstExpr list. We should use a new bit for this,
+ ** for clarity, but we are out of bits in the Expr.flags field so we
+ ** have to reuse the EP_FromJoin bit. Bummer. */
+ pDel->flags |= EP_FromJoin;
+ sqlite3ExprCodeTarget(pParse, &exprAnd, dest);
+ }
+ sqlite3ReleaseTempReg(pParse, regFree1);
}
- sqlite3ReleaseTempReg(pParse, regFree1);
+ sqlite3ExprDelete(db, pDel);
/* Ensure adequate test coverage */
testcase( xJump==sqlite3ExprIfTrue && jumpIfNull==0 && regFree1==0 );
@@ -102053,20 +102442,17 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTa
return 2;
}
if( pA->op!=TK_COLUMN && pA->op!=TK_AGG_COLUMN && pA->u.zToken ){
- if( pA->op==TK_FUNCTION ){
+ if( pA->op==TK_FUNCTION || pA->op==TK_AGG_FUNCTION ){
if( sqlite3StrICmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2;
#ifndef SQLITE_OMIT_WINDOWFUNC
- /* Justification for the assert():
- ** window functions have p->op==TK_FUNCTION but aggregate functions
- ** have p->op==TK_AGG_FUNCTION. So any comparison between an aggregate
- ** function and a window function should have failed before reaching
- ** this point. And, it is not possible to have a window function and
- ** a scalar function with the same name and number of arguments. So
- ** if we reach this point, either A and B both window functions or
- ** neither are a window functions. */
- assert( ExprHasProperty(pA,EP_WinFunc)==ExprHasProperty(pB,EP_WinFunc) );
+ assert( pA->op==pB->op );
+ if( ExprHasProperty(pA,EP_WinFunc)!=ExprHasProperty(pB,EP_WinFunc) ){
+ return 2;
+ }
if( ExprHasProperty(pA,EP_WinFunc) ){
- if( sqlite3WindowCompare(pParse,pA->y.pWin,pB->y.pWin)!=0 ) return 2;
+ if( sqlite3WindowCompare(pParse, pA->y.pWin, pB->y.pWin, 1)!=0 ){
+ return 2;
+ }
}
#endif
}else if( pA->op==TK_NULL ){
@@ -102090,7 +102476,8 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Parse *pParse, Expr *pA, Expr *pB, int iTa
){
if( pA->iColumn!=pB->iColumn ) return 2;
if( pA->op2!=pB->op2 ) return 2;
- if( pA->iTable!=pB->iTable
+ if( pA->op!=TK_IN
+ && pA->iTable!=pB->iTable
&& (pA->iTable!=iTab || NEVER(pB->iTable>=0)) ) return 2;
}
}
@@ -102120,7 +102507,7 @@ SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList *pA, ExprList *pB, int iTab){
for(i=0; i<pA->nExpr; i++){
Expr *pExprA = pA->a[i].pExpr;
Expr *pExprB = pB->a[i].pExpr;
- if( pA->a[i].sortOrder!=pB->a[i].sortOrder ) return 1;
+ if( pA->a[i].sortFlags!=pB->a[i].sortFlags ) return 1;
if( sqlite3ExprCompare(0, pExprA, pExprB, iTab) ) return 1;
}
return 0;
@@ -102132,42 +102519,47 @@ SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList *pA, ExprList *pB, int iTab){
*/
SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr *pA, Expr *pB, int iTab){
return sqlite3ExprCompare(0,
- sqlite3ExprSkipCollate(pA),
- sqlite3ExprSkipCollate(pB),
+ sqlite3ExprSkipCollateAndLikely(pA),
+ sqlite3ExprSkipCollateAndLikely(pB),
iTab);
}
/*
** Return non-zero if Expr p can only be true if pNN is not NULL.
+**
+** Or if seenNot is true, return non-zero if Expr p can only be
+** non-NULL if pNN is not NULL
*/
static int exprImpliesNotNull(
Parse *pParse, /* Parsing context */
Expr *p, /* The expression to be checked */
Expr *pNN, /* The expression that is NOT NULL */
int iTab, /* Table being evaluated */
- int seenNot /* True if p is an operand of NOT */
+ int seenNot /* Return true only if p can be any non-NULL value */
){
assert( p );
assert( pNN );
- if( sqlite3ExprCompare(pParse, p, pNN, iTab)==0 ) return 1;
+ if( sqlite3ExprCompare(pParse, p, pNN, iTab)==0 ){
+ return pNN->op!=TK_NULL;
+ }
switch( p->op ){
case TK_IN: {
if( seenNot && ExprHasProperty(p, EP_xIsSelect) ) return 0;
assert( ExprHasProperty(p,EP_xIsSelect)
|| (p->x.pList!=0 && p->x.pList->nExpr>0) );
- return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, seenNot);
+ return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1);
}
case TK_BETWEEN: {
ExprList *pList = p->x.pList;
assert( pList!=0 );
assert( pList->nExpr==2 );
if( seenNot ) return 0;
- if( exprImpliesNotNull(pParse, pList->a[0].pExpr, pNN, iTab, seenNot)
- || exprImpliesNotNull(pParse, pList->a[1].pExpr, pNN, iTab, seenNot)
+ if( exprImpliesNotNull(pParse, pList->a[0].pExpr, pNN, iTab, 1)
+ || exprImpliesNotNull(pParse, pList->a[1].pExpr, pNN, iTab, 1)
){
return 1;
}
- return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, seenNot);
+ return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1);
}
case TK_EQ:
case TK_NE:
@@ -102177,20 +102569,21 @@ static int exprImpliesNotNull(
case TK_GE:
case TK_PLUS:
case TK_MINUS:
- case TK_STAR:
- case TK_REM:
- case TK_BITAND:
case TK_BITOR:
- case TK_SLASH:
case TK_LSHIFT:
case TK_RSHIFT:
- case TK_CONCAT: {
+ case TK_CONCAT:
+ seenNot = 1;
+ /* Fall thru */
+ case TK_STAR:
+ case TK_REM:
+ case TK_BITAND:
+ case TK_SLASH: {
if( exprImpliesNotNull(pParse, p->pRight, pNN, iTab, seenNot) ) return 1;
/* Fall thru into the next case */
}
case TK_SPAN:
case TK_COLLATE:
- case TK_BITNOT:
case TK_UPLUS:
case TK_UMINUS: {
return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, seenNot);
@@ -102198,8 +102591,9 @@ static int exprImpliesNotNull(
case TK_TRUTH: {
if( seenNot ) return 0;
if( p->op2!=TK_IS ) return 0;
- return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, seenNot);
+ return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1);
}
+ case TK_BITNOT:
case TK_NOT: {
return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1);
}
@@ -102265,7 +102659,6 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
if( ExprHasProperty(pExpr, EP_FromJoin) ) return WRC_Prune;
switch( pExpr->op ){
case TK_ISNOT:
- case TK_NOT:
case TK_ISNULL:
case TK_NOTNULL:
case TK_IS:
@@ -102273,8 +102666,8 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
case TK_CASE:
case TK_IN:
case TK_FUNCTION:
+ case TK_TRUTH:
testcase( pExpr->op==TK_ISNOT );
- testcase( pExpr->op==TK_NOT );
testcase( pExpr->op==TK_ISNULL );
testcase( pExpr->op==TK_NOTNULL );
testcase( pExpr->op==TK_IS );
@@ -102282,6 +102675,7 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
testcase( pExpr->op==TK_CASE );
testcase( pExpr->op==TK_IN );
testcase( pExpr->op==TK_FUNCTION );
+ testcase( pExpr->op==TK_TRUTH );
return WRC_Prune;
case TK_COLUMN:
if( pWalker->u.iCur==pExpr->iTable ){
@@ -102290,6 +102684,18 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
}
return WRC_Prune;
+ case TK_AND:
+ if( sqlite3ExprImpliesNonNullRow(pExpr->pLeft, pWalker->u.iCur)
+ && sqlite3ExprImpliesNonNullRow(pExpr->pRight, pWalker->u.iCur)
+ ){
+ pWalker->eCode = 1;
+ }
+ return WRC_Prune;
+
+ case TK_BETWEEN:
+ sqlite3WalkExpr(pWalker, pExpr->pLeft);
+ return WRC_Prune;
+
/* Virtual tables are allowed to use constraints like x=NULL. So
** a term of the form x=y does not prove that y is not null if x
** is the column of a virtual table */
@@ -102310,6 +102716,7 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
){
return WRC_Prune;
}
+
default:
return WRC_Continue;
}
@@ -102339,7 +102746,7 @@ static int impliesNotNullRow(Walker *pWalker, Expr *pExpr){
*/
SQLITE_PRIVATE int sqlite3ExprImpliesNonNullRow(Expr *p, int iTab){
Walker w;
- p = sqlite3ExprSkipCollate(p);
+ p = sqlite3ExprSkipCollateAndLikely(p);
while( p ){
if( p->op==TK_NOTNULL ){
p = p->pLeft;
@@ -102445,7 +102852,10 @@ static int exprSrcCount(Walker *pWalker, Expr *pExpr){
}
if( i<nSrc ){
p->nThis++;
- }else{
+ }else if( nSrc==0 || pExpr->iTable<pSrc->a[0].iCursor ){
+ /* In a well-formed parse tree (no name resolution errors),
+ ** TK_COLUMN nodes with smaller Expr.iTable values are in an
+ ** outer context. Those are the only ones to count as "other" */
p->nOther++;
}
}
@@ -102462,8 +102872,9 @@ SQLITE_PRIVATE int sqlite3FunctionUsesThisSrc(Expr *pExpr, SrcList *pSrcList){
Walker w;
struct SrcCount cnt;
assert( pExpr->op==TK_AGG_FUNCTION );
+ memset(&w, 0, sizeof(w));
w.xExprCallback = exprSrcCount;
- w.xSelectCallback = 0;
+ w.xSelectCallback = sqlite3SelectWalkNoop;
w.u.pSrcCount = &cnt;
cnt.pSrc = pSrcList;
cnt.nThis = 0;
@@ -102732,6 +103143,11 @@ SQLITE_PRIVATE void sqlite3ReleaseTempRange(Parse *pParse, int iReg, int nReg){
/*
** Mark all temporary registers as being unavailable for reuse.
+**
+** Always invoke this procedure after coding a subroutine or co-routine
+** that might be invoked from other parts of the code, to ensure that
+** the sub/co-routine does not use registers in common with the code that
+** invokes the sub/co-routine.
*/
SQLITE_PRIVATE void sqlite3ClearTempRegCache(Parse *pParse){
pParse->nTempReg = 0;
@@ -102901,8 +103317,8 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable(
if( SQLITE_OK!=isAlterableTable(pParse, pTab) ){
goto exit_rename_table;
}
- if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ goto
- exit_rename_table;
+ if( SQLITE_OK!=sqlite3CheckObjectName(pParse,zName,"table",zName) ){
+ goto exit_rename_table;
}
#ifndef SQLITE_OMIT_VIEW
@@ -103200,6 +103616,7 @@ SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
goto exit_begin_add_column;
}
+ sqlite3MayAbort(pParse);
assert( pTab->addColOffset>0 );
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
@@ -104463,13 +104880,13 @@ SQLITE_PRIVATE void sqlite3AlterFunctions(void){
** is between 3.6.18 and 3.7.8, inclusive, and unless SQLite is compiled
** with SQLITE_ENABLE_STAT2. The sqlite_stat2 table is deprecated.
** The sqlite_stat2 table is superseded by sqlite_stat3, which is only
-** created and used by SQLite versions 3.7.9 and later and with
+** created and used by SQLite versions 3.7.9 through 3.29.0 when
** SQLITE_ENABLE_STAT3 defined. The functionality of sqlite_stat3
-** is a superset of sqlite_stat2. The sqlite_stat4 is an enhanced
-** version of sqlite_stat3 and is only available when compiled with
-** SQLITE_ENABLE_STAT4 and in SQLite versions 3.8.1 and later. It is
-** not possible to enable both STAT3 and STAT4 at the same time. If they
-** are both enabled, then STAT4 takes precedence.
+** is a superset of sqlite_stat2 and is also now deprecated. The
+** sqlite_stat4 is an enhanced version of sqlite_stat3 and is only
+** available when compiled with SQLITE_ENABLE_STAT4 and in SQLite
+** versions 3.8.1 and later. STAT4 is the only variant that is still
+** supported.
**
** For most applications, sqlite_stat1 provides all the statistics required
** for the query planner to make good choices.
@@ -104580,17 +104997,11 @@ SQLITE_PRIVATE void sqlite3AlterFunctions(void){
#if defined(SQLITE_ENABLE_STAT4)
# define IsStat4 1
-# define IsStat3 0
-#elif defined(SQLITE_ENABLE_STAT3)
-# define IsStat4 0
-# define IsStat3 1
#else
# define IsStat4 0
-# define IsStat3 0
# undef SQLITE_STAT4_SAMPLES
# define SQLITE_STAT4_SAMPLES 1
#endif
-#define IsStat34 (IsStat3+IsStat4) /* 1 for STAT3 or STAT4. 0 otherwise */
/*
** This routine generates code that opens the sqlite_statN tables.
@@ -104619,14 +105030,10 @@ static void openStatTable(
{ "sqlite_stat1", "tbl,idx,stat" },
#if defined(SQLITE_ENABLE_STAT4)
{ "sqlite_stat4", "tbl,idx,neq,nlt,ndlt,sample" },
- { "sqlite_stat3", 0 },
-#elif defined(SQLITE_ENABLE_STAT3)
- { "sqlite_stat3", "tbl,idx,neq,nlt,ndlt,sample" },
- { "sqlite_stat4", 0 },
#else
- { "sqlite_stat3", 0 },
{ "sqlite_stat4", 0 },
#endif
+ { "sqlite_stat3", 0 },
};
int i;
sqlite3 *db = pParse->db;
@@ -104707,7 +105114,7 @@ typedef struct Stat4Sample Stat4Sample;
struct Stat4Sample {
tRowcnt *anEq; /* sqlite_stat4.nEq */
tRowcnt *anDLt; /* sqlite_stat4.nDLt */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
tRowcnt *anLt; /* sqlite_stat4.nLt */
union {
i64 iRowid; /* Rowid in main table of the key */
@@ -104738,7 +105145,7 @@ struct Stat4Accum {
/* Reclaim memory used by a Stat4Sample
*/
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
static void sampleClear(sqlite3 *db, Stat4Sample *p){
assert( db!=0 );
if( p->nRowid ){
@@ -104750,7 +105157,7 @@ static void sampleClear(sqlite3 *db, Stat4Sample *p){
/* Initialize the BLOB value of a ROWID
*/
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
static void sampleSetRowid(sqlite3 *db, Stat4Sample *p, int n, const u8 *pData){
assert( db!=0 );
if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid);
@@ -104766,7 +105173,7 @@ static void sampleSetRowid(sqlite3 *db, Stat4Sample *p, int n, const u8 *pData){
/* Initialize the INTEGER value of a ROWID.
*/
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
static void sampleSetRowidInt64(sqlite3 *db, Stat4Sample *p, i64 iRowid){
assert( db!=0 );
if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid);
@@ -104779,7 +105186,7 @@ static void sampleSetRowidInt64(sqlite3 *db, Stat4Sample *p, i64 iRowid){
/*
** Copy the contents of object (*pFrom) into (*pTo).
*/
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
static void sampleCopy(Stat4Accum *p, Stat4Sample *pTo, Stat4Sample *pFrom){
pTo->isPSample = pFrom->isPSample;
pTo->iCol = pFrom->iCol;
@@ -104800,7 +105207,7 @@ static void sampleCopy(Stat4Accum *p, Stat4Sample *pTo, Stat4Sample *pFrom){
*/
static void stat4Destructor(void *pOld){
Stat4Accum *p = (Stat4Accum*)pOld;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
int i;
for(i=0; i<p->nCol; i++) sampleClear(p->db, p->aBest+i);
for(i=0; i<p->mxSample; i++) sampleClear(p->db, p->a+i);
@@ -104820,7 +105227,7 @@ static void stat4Destructor(void *pOld){
** WITHOUT ROWID table, N is the number of PRIMARY KEY columns, not the
** total number of columns in the table.
**
-** Note 2: C is only used for STAT3 and STAT4.
+** Note 2: C is only used for STAT4.
**
** For indexes on ordinary rowid tables, N==K+1. But for indexes on
** WITHOUT ROWID tables, N=K+P where P is the number of columns in the
@@ -104843,7 +105250,7 @@ static void statInit(
int nColUp; /* nCol rounded up for alignment */
int n; /* Bytes of space to allocate */
sqlite3 *db; /* Database connection */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
int mxSample = SQLITE_STAT4_SAMPLES;
#endif
@@ -104860,7 +105267,7 @@ static void statInit(
n = sizeof(*p)
+ sizeof(tRowcnt)*nColUp /* Stat4Accum.anEq */
+ sizeof(tRowcnt)*nColUp /* Stat4Accum.anDLt */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
+ sizeof(tRowcnt)*nColUp /* Stat4Accum.anLt */
+ sizeof(Stat4Sample)*(nCol+mxSample) /* Stat4Accum.aBest[], a[] */
+ sizeof(tRowcnt)*3*nColUp*(nCol+mxSample)
@@ -104880,7 +105287,7 @@ static void statInit(
p->current.anDLt = (tRowcnt*)&p[1];
p->current.anEq = &p->current.anDLt[nColUp];
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
{
u8 *pSpace; /* Allocated space not yet assigned */
int i; /* Used to iterate through p->aSample[] */
@@ -104915,7 +105322,7 @@ static void statInit(
sqlite3_result_blob(context, p, sizeof(*p), stat4Destructor);
}
static const FuncDef statInitFuncdef = {
- 2+IsStat34, /* nArg */
+ 2+IsStat4, /* nArg */
SQLITE_UTF8, /* funcFlags */
0, /* pUserData */
0, /* pNext */
@@ -104955,7 +105362,7 @@ static int sampleIsBetterPost(
}
#endif
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
/*
** Return true if pNew is to be preferred over pOld.
**
@@ -104974,15 +105381,11 @@ static int sampleIsBetter(
assert( IsStat4 || (pNew->iCol==0 && pOld->iCol==0) );
if( (nEqNew>nEqOld) ) return 1;
-#ifdef SQLITE_ENABLE_STAT4
if( nEqNew==nEqOld ){
if( pNew->iCol<pOld->iCol ) return 1;
return (pNew->iCol==pOld->iCol && sampleIsBetterPost(pAccum, pNew, pOld));
}
return 0;
-#else
- return (nEqNew==nEqOld && pNew->iHash>pOld->iHash);
-#endif
}
/*
@@ -104995,7 +105398,6 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
assert( IsStat4 || nEqZero==0 );
-#ifdef SQLITE_ENABLE_STAT4
/* Stat4Accum.nMaxEqZero is set to the maximum number of leading 0
** values in the anEq[] array of any sample in Stat4Accum.a[]. In
** other words, if nMaxEqZero is n, then it is guaranteed that there
@@ -105029,7 +105431,6 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
goto find_new_min;
}
}
-#endif
/* If necessary, remove sample iMin to make room for the new sample. */
if( p->nSample>=p->mxSample ){
@@ -105050,10 +105451,8 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
/* The "rows less-than" for the rowid column must be greater than that
** for the last sample in the p->a[] array. Otherwise, the samples would
** be out of order. */
-#ifdef SQLITE_ENABLE_STAT4
assert( p->nSample==0
|| pNew->anLt[p->nCol-1] > p->a[p->nSample-1].anLt[p->nCol-1] );
-#endif
/* Insert the new sample */
pSample = &p->a[p->nSample];
@@ -105063,9 +105462,7 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
/* Zero the first nEqZero entries in the anEq[] array. */
memset(pSample->anEq, 0, sizeof(tRowcnt)*nEqZero);
-#ifdef SQLITE_ENABLE_STAT4
- find_new_min:
-#endif
+find_new_min:
if( p->nSample>=p->mxSample ){
int iMin = -1;
for(i=0; i<p->mxSample; i++){
@@ -105078,7 +105475,7 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
p->iMin = iMin;
}
}
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
/*
** Field iChng of the index being scanned has changed. So at this point
@@ -105119,28 +105516,7 @@ static void samplePushPrevious(Stat4Accum *p, int iChng){
}
#endif
-#if defined(SQLITE_ENABLE_STAT3) && !defined(SQLITE_ENABLE_STAT4)
- if( iChng==0 ){
- tRowcnt nLt = p->current.anLt[0];
- tRowcnt nEq = p->current.anEq[0];
-
- /* Check if this is to be a periodic sample. If so, add it. */
- if( (nLt/p->nPSample)!=(nLt+nEq)/p->nPSample ){
- p->current.isPSample = 1;
- sampleInsert(p, &p->current, 0);
- p->current.isPSample = 0;
- }else
-
- /* Or if it is a non-periodic sample. Add it in this case too. */
- if( p->nSample<p->mxSample
- || sampleIsBetter(p, &p->current, &p->a[p->iMin])
- ){
- sampleInsert(p, &p->current, 0);
- }
- }
-#endif
-
-#ifndef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifndef SQLITE_ENABLE_STAT4
UNUSED_PARAMETER( p );
UNUSED_PARAMETER( iChng );
#endif
@@ -105160,7 +105536,7 @@ static void samplePushPrevious(Stat4Accum *p, int iChng){
** index being analyzed. The stat_get() SQL function will later be used to
** extract relevant information for constructing the sqlite_statN tables.
**
-** The R parameter is only used for STAT3 and STAT4
+** The R parameter is only used for STAT4
*/
static void statPush(
sqlite3_context *context,
@@ -105192,14 +105568,14 @@ static void statPush(
}
for(i=iChng; i<p->nCol; i++){
p->current.anDLt[i]++;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
p->current.anLt[i] += p->current.anEq[i];
#endif
p->current.anEq[i] = 1;
}
}
p->nRow++;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){
sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2]));
}else{
@@ -105232,7 +105608,7 @@ static void statPush(
#endif
}
static const FuncDef statPushFuncdef = {
- 2+IsStat34, /* nArg */
+ 2+IsStat4, /* nArg */
SQLITE_UTF8, /* funcFlags */
0, /* pUserData */
0, /* pNext */
@@ -105263,7 +105639,7 @@ static const FuncDef statPushFuncdef = {
** parameter will always be a poiner to a Stat4Accum object, never a
** NULL.
**
-** If neither STAT3 nor STAT4 are enabled, then J is always
+** If STAT4 is not enabled, then J is always
** STAT_GET_STAT1 and is hence omitted and this routine becomes
** a one-parameter function, stat_get(P), that always returns the
** stat1 table entry information.
@@ -105274,8 +105650,8 @@ static void statGet(
sqlite3_value **argv
){
Stat4Accum *p = (Stat4Accum*)sqlite3_value_blob(argv[0]);
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
- /* STAT3 and STAT4 have a parameter on this routine. */
+#ifdef SQLITE_ENABLE_STAT4
+ /* STAT4 has a parameter on this routine. */
int eCall = sqlite3_value_int(argv[1]);
assert( argc==2 );
assert( eCall==STAT_GET_STAT1 || eCall==STAT_GET_NEQ
@@ -105330,7 +105706,7 @@ static void statGet(
sqlite3_result_text(context, zRet, -1, sqlite3_free);
}
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
else if( eCall==STAT_GET_ROWID ){
if( p->iGet<0 ){
samplePushPrevious(p, 0);
@@ -105359,9 +105735,7 @@ static void statGet(
}
}
- if( IsStat3 ){
- sqlite3_result_int64(context, (i64)aCnt[0]);
- }else{
+ {
char *zRet = sqlite3MallocZero(p->nCol * 25);
if( zRet==0 ){
sqlite3_result_error_nomem(context);
@@ -105378,13 +105752,13 @@ static void statGet(
}
}
}
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
#ifndef SQLITE_DEBUG
UNUSED_PARAMETER( argc );
#endif
}
static const FuncDef statGetFuncdef = {
- 1+IsStat34, /* nArg */
+ 1+IsStat4, /* nArg */
SQLITE_UTF8, /* funcFlags */
0, /* pUserData */
0, /* pNext */
@@ -105397,7 +105771,7 @@ static const FuncDef statGetFuncdef = {
static void callStatGet(Vdbe *v, int regStat4, int iParam, int regOut){
assert( regOut!=regStat4 && regOut!=regStat4+1 );
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
sqlite3VdbeAddOp2(v, OP_Integer, iParam, regStat4+1);
#elif SQLITE_DEBUG
assert( iParam==STAT_GET_STAT1 );
@@ -105406,7 +105780,7 @@ static void callStatGet(Vdbe *v, int regStat4, int iParam, int regOut){
#endif
sqlite3VdbeAddOp4(v, OP_Function0, 0, regStat4, regOut,
(char*)&statGetFuncdef, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, 1 + IsStat34);
+ sqlite3VdbeChangeP5(v, 1 + IsStat4);
}
/*
@@ -105433,7 +105807,7 @@ static void analyzeOneTable(
int regNewRowid = iMem++; /* Rowid for the inserted record */
int regStat4 = iMem++; /* Register to hold Stat4Accum object */
int regChng = iMem++; /* Index of changed index field */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
int regRowid = iMem++; /* Rowid argument passed to stat_push() */
#endif
int regTemp = iMem++; /* Temporary use register */
@@ -105567,16 +105941,16 @@ static void analyzeOneTable(
** (3) the number of rows in the index,
**
**
- ** The third argument is only used for STAT3 and STAT4
+ ** The third argument is only used for STAT4
*/
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat4+3);
#endif
sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat4+1);
sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regStat4+2);
sqlite3VdbeAddOp4(v, OP_Function0, 0, regStat4+1, regStat4,
(char*)&statInitFuncdef, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, 2+IsStat34);
+ sqlite3VdbeChangeP5(v, 2+IsStat4);
/* Implementation of the following:
**
@@ -105647,12 +106021,12 @@ static void analyzeOneTable(
/*
** chng_addr_N:
- ** regRowid = idx(rowid) // STAT34 only
- ** stat_push(P, regChng, regRowid) // 3rd parameter STAT34 only
+ ** regRowid = idx(rowid) // STAT4 only
+ ** stat_push(P, regChng, regRowid) // 3rd parameter STAT4 only
** Next csr
** if !eof(csr) goto next_row;
*/
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
assert( regRowid==(regStat4+2) );
if( HasRowid(pTab) ){
sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid);
@@ -105673,7 +106047,7 @@ static void analyzeOneTable(
assert( regChng==(regStat4+1) );
sqlite3VdbeAddOp4(v, OP_Function0, 1, regStat4, regTemp,
(char*)&statPushFuncdef, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, 2+IsStat34);
+ sqlite3VdbeChangeP5(v, 2+IsStat4);
sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v);
/* Add the entry to the stat1 table. */
@@ -105687,8 +106061,8 @@ static void analyzeOneTable(
#endif
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
- /* Add the entries to the stat3 or stat4 table. */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ /* Add the entries to the stat4 table. */
+#ifdef SQLITE_ENABLE_STAT4
{
int regEq = regStat1;
int regLt = regStat1+1;
@@ -105711,21 +106085,17 @@ static void analyzeOneTable(
callStatGet(v, regStat4, STAT_GET_NDLT, regDLt);
sqlite3VdbeAddOp4Int(v, seekOp, iTabCur, addrNext, regSampleRowid, 0);
VdbeCoverage(v);
-#ifdef SQLITE_ENABLE_STAT3
- sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iTabCur, 0, regSample);
-#else
for(i=0; i<nCol; i++){
sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iTabCur, i, regCol+i);
}
sqlite3VdbeAddOp3(v, OP_MakeRecord, regCol, nCol, regSample);
-#endif
sqlite3VdbeAddOp3(v, OP_MakeRecord, regTabname, 6, regTemp);
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regNewRowid);
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regTemp, regNewRowid);
sqlite3VdbeAddOp2(v, OP_Goto, 1, addrNext); /* P1==1 for end-of-loop */
sqlite3VdbeJumpHere(v, addrIsNull);
}
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
/* End of analysis */
sqlite3VdbeJumpHere(v, addrRewind);
@@ -105900,7 +106270,7 @@ static void decodeIntArray(
int i;
tRowcnt v;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
if( z==0 ) z = "";
#else
assert( z!=0 );
@@ -105911,7 +106281,7 @@ static void decodeIntArray(
v = v*10 + c - '0';
z++;
}
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
if( aOut ) aOut[i] = v;
if( aLog ) aLog[i] = sqlite3LogEst(v);
#else
@@ -105922,7 +106292,7 @@ static void decodeIntArray(
#endif
if( *z==' ' ) z++;
}
-#ifndef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifndef SQLITE_ENABLE_STAT4
assert( pIndex!=0 ); {
#else
if( pIndex ){
@@ -105989,7 +106359,7 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
if( pIndex ){
tRowcnt *aiRowEst = 0;
int nCol = pIndex->nKeyCol+1;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
/* Index.aiRowEst may already be set here if there are duplicate
** sqlite_stat1 entries for this index. In that case just clobber
** the old data with the new instead of allocating a new array. */
@@ -106025,7 +106395,7 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
** and its contents.
*/
SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
if( pIdx->aSample ){
int j;
for(j=0; j<pIdx->nSample; j++){
@@ -106041,10 +106411,10 @@ SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
#else
UNUSED_PARAMETER(db);
UNUSED_PARAMETER(pIdx);
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
}
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
/*
** Populate the pIdx->aAvgEq[] array based on the samples currently
** stored in pIdx->aSample[].
@@ -106122,12 +106492,11 @@ static Index *findIndexOrPrimaryKey(
}
/*
-** Load the content from either the sqlite_stat4 or sqlite_stat3 table
+** Load the content from either the sqlite_stat4
** into the relevant Index.aSample[] arrays.
**
** Arguments zSql1 and zSql2 must point to SQL statements that return
-** data equivalent to the following (statements are different for stat3,
-** see the caller of this function for details):
+** data equivalent to the following:
**
** zSql1: SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx
** zSql2: SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4
@@ -106136,7 +106505,6 @@ static Index *findIndexOrPrimaryKey(
*/
static int loadStatTbl(
sqlite3 *db, /* Database handle */
- int bStat3, /* Assume single column records only */
const char *zSql1, /* SQL statement 1 (see above) */
const char *zSql2, /* SQL statement 2 (see above) */
const char *zDb /* Database name (e.g. "main") */
@@ -106170,17 +106538,13 @@ static int loadStatTbl(
if( zIndex==0 ) continue;
nSample = sqlite3_column_int(pStmt, 1);
pIdx = findIndexOrPrimaryKey(db, zIndex, zDb);
- assert( pIdx==0 || bStat3 || pIdx->nSample==0 );
- /* Index.nSample is non-zero at this point if data has already been
- ** loaded from the stat4 table. In this case ignore stat3 data. */
- if( pIdx==0 || pIdx->nSample ) continue;
- if( bStat3==0 ){
- assert( !HasRowid(pIdx->pTable) || pIdx->nColumn==pIdx->nKeyCol+1 );
- if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){
- nIdxCol = pIdx->nKeyCol;
- }else{
- nIdxCol = pIdx->nColumn;
- }
+ assert( pIdx==0 || pIdx->nSample==0 );
+ if( pIdx==0 ) continue;
+ assert( !HasRowid(pIdx->pTable) || pIdx->nColumn==pIdx->nKeyCol+1 );
+ if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){
+ nIdxCol = pIdx->nKeyCol;
+ }else{
+ nIdxCol = pIdx->nColumn;
}
pIdx->nSampleCol = nIdxCol;
nByte = sizeof(IndexSample) * nSample;
@@ -106222,9 +106586,8 @@ static int loadStatTbl(
pIdx = findIndexOrPrimaryKey(db, zIndex, zDb);
if( pIdx==0 ) continue;
/* This next condition is true if data has already been loaded from
- ** the sqlite_stat4 table. In this case ignore stat3 data. */
+ ** the sqlite_stat4 table. */
nCol = pIdx->nSampleCol;
- if( bStat3 && nCol>1 ) continue;
if( pIdx!=pPrevIdx ){
initAvgEq(pPrevIdx);
pPrevIdx = pIdx;
@@ -106257,7 +106620,7 @@ static int loadStatTbl(
}
/*
-** Load content from the sqlite_stat4 and sqlite_stat3 tables into
+** Load content from the sqlite_stat4 table into
** the Index.aSample[] arrays of all indices.
*/
static int loadStat4(sqlite3 *db, const char *zDb){
@@ -106265,37 +106628,28 @@ static int loadStat4(sqlite3 *db, const char *zDb){
assert( db->lookaside.bDisable );
if( sqlite3FindTable(db, "sqlite_stat4", zDb) ){
- rc = loadStatTbl(db, 0,
+ rc = loadStatTbl(db,
"SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx",
"SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4",
zDb
);
}
-
- if( rc==SQLITE_OK && sqlite3FindTable(db, "sqlite_stat3", zDb) ){
- rc = loadStatTbl(db, 1,
- "SELECT idx,count(*) FROM %Q.sqlite_stat3 GROUP BY idx",
- "SELECT idx,neq,nlt,ndlt,sqlite_record(sample) FROM %Q.sqlite_stat3",
- zDb
- );
- }
-
return rc;
}
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
/*
-** Load the content of the sqlite_stat1 and sqlite_stat3/4 tables. The
+** Load the content of the sqlite_stat1 and sqlite_stat4 tables. The
** contents of sqlite_stat1 are used to populate the Index.aiRowEst[]
-** arrays. The contents of sqlite_stat3/4 are used to populate the
+** arrays. The contents of sqlite_stat4 are used to populate the
** Index.aSample[] arrays.
**
** If the sqlite_stat1 table is not present in the database, SQLITE_ERROR
-** is returned. In this case, even if SQLITE_ENABLE_STAT3/4 was defined
-** during compilation and the sqlite_stat3/4 table is present, no data is
+** is returned. In this case, even if SQLITE_ENABLE_STAT4 was defined
+** during compilation and the sqlite_stat4 table is present, no data is
** read from it.
**
-** If SQLITE_ENABLE_STAT3/4 was defined during compilation and the
+** If SQLITE_ENABLE_STAT4 was defined during compilation and the
** sqlite_stat4 table is not present in the database, SQLITE_ERROR is
** returned. However, in this case, data is read from the sqlite_stat1
** table (if it is present) before returning.
@@ -106323,7 +106677,7 @@ SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
for(i=sqliteHashFirst(&pSchema->idxHash); i; i=sqliteHashNext(i)){
Index *pIdx = sqliteHashData(i);
pIdx->hasStat1 = 0;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
sqlite3DeleteIndexSamples(db, pIdx);
pIdx->aSample = 0;
#endif
@@ -106351,7 +106705,7 @@ SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
}
/* Load the statistics from the sqlite_stat4 table. */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
if( rc==SQLITE_OK ){
db->lookaside.bDisable++;
rc = loadStat4(db, sInfo.zDatabase);
@@ -106676,6 +107030,7 @@ static void detachFunc(
sqlite3 *db = sqlite3_context_db_handle(context);
int i;
Db *pDb = 0;
+ HashElem *pEntry;
char zErr[128];
UNUSED_PARAMETER(NotUsed);
@@ -106700,6 +107055,18 @@ static void detachFunc(
goto detach_error;
}
+ /* If any TEMP triggers reference the schema being detached, move those
+ ** triggers to reference the TEMP schema itself. */
+ assert( db->aDb[1].pSchema );
+ pEntry = sqliteHashFirst(&db->aDb[1].pSchema->trigHash);
+ while( pEntry ){
+ Trigger *pTrig = (Trigger*)sqliteHashData(pEntry);
+ if( pTrig->pTabSchema==pDb->pSchema ){
+ pTrig->pTabSchema = pTrig->pSchema;
+ }
+ pEntry = sqliteHashNext(pEntry);
+ }
+
sqlite3BtreeClose(pDb->pBt);
pDb->pBt = 0;
pDb->pSchema = 0;
@@ -106937,6 +107304,7 @@ SQLITE_PRIVATE int sqlite3FixExpr(
Expr *pExpr /* The expression to be fixed to one database */
){
while( pExpr ){
+ ExprSetProperty(pExpr, EP_Indirect);
if( pExpr->op==TK_VARIABLE ){
if( pFix->pParse->db->init.busy ){
pExpr->op = TK_NULL;
@@ -107089,7 +107457,7 @@ SQLITE_API int sqlite3_set_authorizer(
sqlite3_mutex_enter(db->mutex);
db->xAuth = (sqlite3_xauth)xAuth;
db->pAuthArg = pArg;
- sqlite3ExpirePreparedStatements(db, 0);
+ if( db->xAuth ) sqlite3ExpirePreparedStatements(db, 1);
sqlite3_mutex_leave(db->mutex);
return SQLITE_OK;
}
@@ -107743,7 +108111,7 @@ SQLITE_PRIVATE void sqlite3FreeIndex(sqlite3 *db, Index *p){
sqlite3ExprListDelete(db, p->aColExpr);
sqlite3DbFree(db, p->zColAff);
if( p->isResized ) sqlite3DbFree(db, (void *)p->azColl);
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
sqlite3_free(p->aiRowEst);
#endif
sqlite3DbFree(db, p);
@@ -108116,13 +108484,40 @@ SQLITE_PRIVATE int sqlite3WritableSchema(sqlite3 *db){
** trigger). All names are legal except those that begin with the string
** "sqlite_" (in upper, lower or mixed case). This portion of the namespace
** is reserved for internal use.
+**
+** When parsing the sqlite_master table, this routine also checks to
+** make sure the "type", "name", and "tbl_name" columns are consistent
+** with the SQL.
*/
-SQLITE_PRIVATE int sqlite3CheckObjectName(Parse *pParse, const char *zName){
- if( !pParse->db->init.busy && pParse->nested==0
- && sqlite3WritableSchema(pParse->db)==0
- && 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){
- sqlite3ErrorMsg(pParse, "object name reserved for internal use: %s", zName);
- return SQLITE_ERROR;
+SQLITE_PRIVATE int sqlite3CheckObjectName(
+ Parse *pParse, /* Parsing context */
+ const char *zName, /* Name of the object to check */
+ const char *zType, /* Type of this object */
+ const char *zTblName /* Parent table name for triggers and indexes */
+){
+ sqlite3 *db = pParse->db;
+ if( sqlite3WritableSchema(db) || db->init.imposterTable ){
+ /* Skip these error checks for writable_schema=ON */
+ return SQLITE_OK;
+ }
+ if( db->init.busy ){
+ if( sqlite3_stricmp(zType, db->init.azInit[0])
+ || sqlite3_stricmp(zName, db->init.azInit[1])
+ || sqlite3_stricmp(zTblName, db->init.azInit[2])
+ ){
+ if( sqlite3Config.bExtraSchemaChecks ){
+ sqlite3ErrorMsg(pParse, ""); /* corruptSchema() will supply the error */
+ return SQLITE_ERROR;
+ }
+ }
+ }else{
+ if( pParse->nested==0
+ && 0==sqlite3StrNICmp(zName, "sqlite_", 7)
+ ){
+ sqlite3ErrorMsg(pParse, "object name reserved for internal use: %s",
+ zName);
+ return SQLITE_ERROR;
+ }
}
return SQLITE_OK;
}
@@ -108203,7 +108598,7 @@ SQLITE_PRIVATE void sqlite3StartTable(
}
pParse->sNameToken = *pName;
if( zName==0 ) return;
- if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
+ if( sqlite3CheckObjectName(pParse, zName, isView?"view":"table", zName) ){
goto begin_table_error;
}
if( db->init.iDb==1 ) isTemp = 1;
@@ -108703,7 +109098,7 @@ SQLITE_PRIVATE void sqlite3AddPrimaryKey(
pTab->keyConf = (u8)onError;
assert( autoInc==0 || autoInc==1 );
pTab->tabFlags |= autoInc*TF_Autoincrement;
- if( pList ) pParse->iPkSortOrder = pList->a[0].sortOrder;
+ if( pList ) pParse->iPkSortOrder = pList->a[0].sortFlags;
}else if( autoInc ){
#ifndef SQLITE_OMIT_AUTOINCREMENT
sqlite3ErrorMsg(pParse, "AUTOINCREMENT is only allowed on an "
@@ -109118,6 +109513,7 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
Index *pIdx;
Index *pPk;
int nPk;
+ int nExtra;
int i, j;
sqlite3 *db = pParse->db;
Vdbe *v = pParse->pVdbe;
@@ -109153,13 +109549,14 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
if( IN_RENAME_OBJECT ){
sqlite3RenameTokenRemap(pParse, pList->a[0].pExpr, &pTab->iPKey);
}
- pList->a[0].sortOrder = pParse->iPkSortOrder;
+ pList->a[0].sortFlags = pParse->iPkSortOrder;
assert( pParse->pNewTable==pTab );
pTab->iPKey = -1;
sqlite3CreateIndex(pParse, 0, 0, 0, pList, pTab->keyConf, 0, 0, 0, 0,
SQLITE_IDXTYPE_PRIMARYKEY);
if( db->mallocFailed || pParse->nErr ) return;
pPk = sqlite3PrimaryKeyIndex(pTab);
+ assert( pPk->nKeyCol==1 );
}else{
pPk = sqlite3PrimaryKeyIndex(pTab);
assert( pPk!=0 );
@@ -109174,6 +109571,8 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
pPk->nColumn--;
}else{
testcase( hasColumn(pPk->aiColumn, j, pPk->aiColumn[i]) );
+ pPk->azColl[j] = pPk->azColl[i];
+ pPk->aSortOrder[j] = pPk->aSortOrder[i];
pPk->aiColumn[j++] = pPk->aiColumn[i];
}
}
@@ -109182,7 +109581,7 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
assert( pPk!=0 );
pPk->isCovering = 1;
if( !db->init.imposterTable ) pPk->uniqNotNull = 1;
- nPk = pPk->nKeyCol;
+ nPk = pPk->nColumn = pPk->nKeyCol;
/* Bypass the creation of the PRIMARY KEY btree and the sqlite_master
** table entry. This is only required if currently generating VDBE
@@ -109232,21 +109631,21 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
/* Add all table columns to the PRIMARY KEY index
*/
- if( nPk<pTab->nCol ){
- if( resizeIndexObject(db, pPk, pTab->nCol) ) return;
- for(i=0, j=nPk; i<pTab->nCol; i++){
- if( !hasColumn(pPk->aiColumn, j, i) ){
- assert( j<pPk->nColumn );
- pPk->aiColumn[j] = i;
- pPk->azColl[j] = sqlite3StrBINARY;
- j++;
- }
+ nExtra = 0;
+ for(i=0; i<pTab->nCol; i++){
+ if( !hasColumn(pPk->aiColumn, nPk, i) ) nExtra++;
+ }
+ if( resizeIndexObject(db, pPk, nPk+nExtra) ) return;
+ for(i=0, j=nPk; i<pTab->nCol; i++){
+ if( !hasColumn(pPk->aiColumn, j, i) ){
+ assert( j<pPk->nColumn );
+ pPk->aiColumn[j] = i;
+ pPk->azColl[j] = sqlite3StrBINARY;
+ j++;
}
- assert( pPk->nColumn==j );
- assert( pTab->nCol==j );
- }else{
- pPk->nColumn = pTab->nCol;
}
+ assert( pPk->nColumn==j );
+ assert( pTab->nCol<=j );
recomputeColumnsNotIndexed(pPk);
}
@@ -109443,7 +109842,7 @@ SQLITE_PRIVATE void sqlite3EndTable(
addrTop = sqlite3VdbeCurrentAddr(v) + 1;
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
if( pParse->nErr ) return;
- pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect);
+ pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect, SQLITE_AFF_BLOB);
if( pSelTab==0 ) return;
assert( p->aCol==0 );
p->nCol = pSelTab->nCol;
@@ -109707,10 +110106,10 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
#ifndef SQLITE_OMIT_AUTHORIZATION
xAuth = db->xAuth;
db->xAuth = 0;
- pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
+ pSelTab = sqlite3ResultSetOfSelect(pParse, pSel, SQLITE_AFF_NONE);
db->xAuth = xAuth;
#else
- pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
+ pSelTab = sqlite3ResultSetOfSelect(pParse, pSel, SQLITE_AFF_NONE);
#endif
pParse->nTab = n;
if( pTable->pCheck ){
@@ -109726,7 +110125,8 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
&& pParse->nErr==0
&& pTable->nCol==pSel->pEList->nExpr
){
- sqlite3SelectAddColumnTypeAndCollation(pParse, pTable, pSel);
+ sqlite3SelectAddColumnTypeAndCollation(pParse, pTable, pSel,
+ SQLITE_AFF_NONE);
}
}else if( pSelTab ){
/* CREATE VIEW name AS... without an argument list. Construct
@@ -110071,7 +110471,8 @@ SQLITE_PRIVATE void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView,
}
#endif
if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0
- && sqlite3StrNICmp(pTab->zName, "sqlite_stat", 11)!=0 ){
+ && sqlite3StrNICmp(pTab->zName+7, "stat", 4)!=0
+ && sqlite3StrNICmp(pTab->zName+7, "parameters", 10)!=0 ){
sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName);
goto exit_drop_table;
}
@@ -110409,6 +110810,27 @@ SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(
}
/*
+** If expression list pList contains an expression that was parsed with
+** an explicit "NULLS FIRST" or "NULLS LAST" clause, leave an error in
+** pParse and return non-zero. Otherwise, return zero.
+*/
+SQLITE_PRIVATE int sqlite3HasExplicitNulls(Parse *pParse, ExprList *pList){
+ if( pList ){
+ int i;
+ for(i=0; i<pList->nExpr; i++){
+ if( pList->a[i].bNulls ){
+ u8 sf = pList->a[i].sortFlags;
+ sqlite3ErrorMsg(pParse, "unsupported use of NULLS %s",
+ (sf==0 || sf==3) ? "FIRST" : "LAST"
+ );
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
** Create a new index for an SQL table. pName1.pName2 is the name of the index
** and pTblList is the name of the table that is to be indexed. Both will
** be NULL for a primary key or an index that is created to satisfy a
@@ -110459,6 +110881,9 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
goto exit_create_index;
}
+ if( sqlite3HasExplicitNulls(pParse, pList) ){
+ goto exit_create_index;
+ }
/*
** Find the table that is to be indexed. Return early if not found.
@@ -110557,7 +110982,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
zName = sqlite3NameFromToken(db, pName);
if( zName==0 ) goto exit_create_index;
assert( pName->z!=0 );
- if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
+ if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName,"index",pTab->zName) ){
goto exit_create_index;
}
if( !IN_RENAME_OBJECT ){
@@ -110623,7 +111048,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
sqlite3ExprAlloc(db, TK_ID, &prevCol, 0));
if( pList==0 ) goto exit_create_index;
assert( pList->nExpr==1 );
- sqlite3ExprListSetSortOrder(pList, sortOrder);
+ sqlite3ExprListSetSortOrder(pList, sortOrder, SQLITE_SO_UNDEFINED);
}else{
sqlite3ExprListCheckLength(pParse, pList, "index");
if( pParse->nErr ) goto exit_create_index;
@@ -110741,7 +111166,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
goto exit_create_index;
}
pIndex->azColl[i] = zColl;
- requestedSortOrder = pListItem->sortOrder & sortOrderMask;
+ requestedSortOrder = pListItem->sortFlags & sortOrderMask;
pIndex->aSortOrder[i] = (u8)requestedSortOrder;
}
@@ -110916,6 +111341,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
/* Gather the complete text of the CREATE INDEX statement into
** the zStmt variable
*/
+ assert( pName!=0 || pStart==0 );
if( pStart ){
int n = (int)(pParse->sLastToken.z - pName->z) + pParse->sLastToken.n;
if( pName->z[n-1]==';' ) n--;
@@ -111958,7 +112384,8 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){
const char *zColl = pIdx->azColl[i];
pKey->aColl[i] = zColl==sqlite3StrBINARY ? 0 :
sqlite3LocateCollSeq(pParse, zColl);
- pKey->aSortOrder[i] = pIdx->aSortOrder[i];
+ pKey->aSortFlags[i] = pIdx->aSortOrder[i];
+ assert( 0==(pKey->aSortFlags[i] & KEYINFO_ORDER_BIGNULL) );
}
if( pParse->nErr ){
assert( pParse->rc==SQLITE_ERROR_MISSING_COLLSEQ );
@@ -113714,6 +114141,8 @@ static void instrFunc(
int N = 1;
int isText;
unsigned char firstChar;
+ sqlite3_value *pC1 = 0;
+ sqlite3_value *pC2 = 0;
UNUSED_PARAMETER(argc);
typeHaystack = sqlite3_value_type(argv[0]);
@@ -113726,12 +114155,22 @@ static void instrFunc(
zHaystack = sqlite3_value_blob(argv[0]);
zNeedle = sqlite3_value_blob(argv[1]);
isText = 0;
- }else{
+ }else if( typeHaystack!=SQLITE_BLOB && typeNeedle!=SQLITE_BLOB ){
zHaystack = sqlite3_value_text(argv[0]);
zNeedle = sqlite3_value_text(argv[1]);
isText = 1;
+ }else{
+ pC1 = sqlite3_value_dup(argv[0]);
+ zHaystack = sqlite3_value_text(pC1);
+ if( zHaystack==0 ) goto endInstrOOM;
+ nHaystack = sqlite3_value_bytes(pC1);
+ pC2 = sqlite3_value_dup(argv[1]);
+ zNeedle = sqlite3_value_text(pC2);
+ if( zNeedle==0 ) goto endInstrOOM;
+ nNeedle = sqlite3_value_bytes(pC2);
+ isText = 1;
}
- if( zNeedle==0 || (nHaystack && zHaystack==0) ) return;
+ if( zNeedle==0 || (nHaystack && zHaystack==0) ) goto endInstrOOM;
firstChar = zNeedle[0];
while( nNeedle<=nHaystack
&& (zHaystack[0]!=firstChar || memcmp(zHaystack, zNeedle, nNeedle)!=0)
@@ -113745,6 +114184,13 @@ static void instrFunc(
if( nNeedle>nHaystack ) N = 0;
}
sqlite3_result_int(context, N);
+endInstr:
+ sqlite3_value_free(pC1);
+ sqlite3_value_free(pC2);
+ return;
+endInstrOOM:
+ sqlite3_result_error_nomem(context);
+ goto endInstr;
}
/*
@@ -115497,9 +115943,6 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
sqlite3AlterFunctions();
#endif
sqlite3WindowFunctions();
-#if defined(SQLITE_ENABLE_STAT3) || defined(SQLITE_ENABLE_STAT4)
- sqlite3AnalyzeFunctions();
-#endif
sqlite3RegisterDateTimeFunctions();
sqlite3InsertBuiltinFuncs(aBuiltinFunc, ArraySize(aBuiltinFunc));
@@ -116002,13 +116445,13 @@ static Expr *exprTableRegister(
if( iCol>=0 && iCol!=pTab->iPKey ){
pCol = &pTab->aCol[iCol];
pExpr->iTable = regBase + iCol + 1;
- pExpr->affinity = pCol->affinity;
+ pExpr->affExpr = pCol->affinity;
zColl = pCol->zColl;
if( zColl==0 ) zColl = db->pDfltColl->zName;
pExpr = sqlite3ExprAddCollateString(pParse, pExpr, zColl);
}else{
pExpr->iTable = regBase;
- pExpr->affinity = SQLITE_AFF_INTEGER;
+ pExpr->affExpr = SQLITE_AFF_INTEGER;
}
}
return pExpr;
@@ -116811,7 +117254,7 @@ static Trigger *fkActionTrigger(
tFrom.n = nFrom;
pRaise = sqlite3Expr(db, TK_RAISE, "FOREIGN KEY constraint failed");
if( pRaise ){
- pRaise->affinity = OE_Abort;
+ pRaise->affExpr = OE_Abort;
}
pSelect = sqlite3SelectNew(pParse,
sqlite3ExprListAppend(pParse, 0, pRaise),
@@ -116856,6 +117299,7 @@ static Trigger *fkActionTrigger(
return 0;
}
assert( pStep!=0 );
+ assert( pTrigger!=0 );
switch( action ){
case OE_Restrict:
@@ -117046,18 +117490,19 @@ SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(sqlite3 *db, Index *pIdx){
}
for(n=0; n<pIdx->nColumn; n++){
i16 x = pIdx->aiColumn[n];
+ char aff;
if( x>=0 ){
- pIdx->zColAff[n] = pTab->aCol[x].affinity;
+ aff = pTab->aCol[x].affinity;
}else if( x==XN_ROWID ){
- pIdx->zColAff[n] = SQLITE_AFF_INTEGER;
+ aff = SQLITE_AFF_INTEGER;
}else{
- char aff;
assert( x==XN_EXPR );
assert( pIdx->aColExpr!=0 );
aff = sqlite3ExprAffinity(pIdx->aColExpr->a[n].pExpr);
- if( aff==0 ) aff = SQLITE_AFF_BLOB;
- pIdx->zColAff[n] = aff;
}
+ if( aff<SQLITE_AFF_BLOB ) aff = SQLITE_AFF_BLOB;
+ if( aff>SQLITE_AFF_NUMERIC) aff = SQLITE_AFF_NUMERIC;
+ pIdx->zColAff[n] = aff;
}
pIdx->zColAff[n] = 0;
}
@@ -117097,11 +117542,12 @@ SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){
}
for(i=0; i<pTab->nCol; i++){
+ assert( pTab->aCol[i].affinity!=0 );
zColAff[i] = pTab->aCol[i].affinity;
}
do{
zColAff[i--] = 0;
- }while( i>=0 && zColAff[i]==SQLITE_AFF_BLOB );
+ }while( i>=0 && zColAff[i]<=SQLITE_AFF_BLOB );
pTab->zColAff = zColAff;
}
assert( zColAff!=0 );
@@ -117790,6 +118236,9 @@ SQLITE_PRIVATE void sqlite3Insert(
pTab->zName);
goto insert_cleanup;
}
+ if( sqlite3HasExplicitNulls(pParse, pUpsert->pUpsertTarget) ){
+ goto insert_cleanup;
+ }
pTabList->a[0].iCursor = iDataCur;
pUpsert->pUpsertSrc = pTabList;
pUpsert->regData = regData;
@@ -119903,6 +120352,8 @@ struct sqlite3_api_routines {
/* Version 3.28.0 and later */
int (*stmt_isexplain)(sqlite3_stmt*);
int (*value_frombind)(sqlite3_value*);
+ /* Version 3.30.0 and later */
+ int (*drop_modules)(sqlite3*,const char**);
};
/*
@@ -120195,6 +120646,8 @@ typedef int (*sqlite3_loadext_entry)(
/* Version 3.28.0 and later */
#define sqlite3_stmt_isexplain sqlite3_api->isexplain
#define sqlite3_value_frombind sqlite3_api->frombind
+/* Version 3.30.0 and later */
+#define sqlite3_drop_modules sqlite3_api->drop_modules
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
@@ -120660,7 +121113,13 @@ static const sqlite3_api_routines sqlite3Apis = {
#endif
/* Version 3.28.0 and later */
sqlite3_stmt_isexplain,
- sqlite3_value_frombind
+ sqlite3_value_frombind,
+ /* Version 3.30.0 and later */
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ sqlite3_drop_modules,
+#else
+ 0,
+#endif
};
/*
@@ -121373,7 +121832,7 @@ static const PragmaName aPragmaName[] = {
/* iArg: */ SQLITE_FullFSync },
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
-#if defined(SQLITE_INTROSPECTION_PRAGMAS)
+#if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS)
{/* zName: */ "function_list",
/* ePragTyp: */ PragTyp_FUNCTION_LIST,
/* ePragFlg: */ PragFlg_Result0,
@@ -121497,7 +121956,7 @@ static const PragmaName aPragmaName[] = {
#endif
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
#if !defined(SQLITE_OMIT_VIRTUALTABLE)
-#if defined(SQLITE_INTROSPECTION_PRAGMAS)
+#if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS)
{/* zName: */ "module_list",
/* ePragTyp: */ PragTyp_MODULE_LIST,
/* ePragFlg: */ PragFlg_Result0,
@@ -121532,7 +121991,7 @@ static const PragmaName aPragmaName[] = {
/* iArg: */ SQLITE_ParserTrace },
#endif
#endif
-#if defined(SQLITE_INTROSPECTION_PRAGMAS)
+#if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS)
{/* zName: */ "pragma_list",
/* ePragTyp: */ PragTyp_PRAGMA_LIST,
/* ePragFlg: */ PragFlg_Result0,
@@ -121730,7 +122189,7 @@ static const PragmaName aPragmaName[] = {
/* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError },
#endif
};
-/* Number of pragmas: 62 on by default, 81 total. */
+/* Number of pragmas: 65 on by default, 81 total. */
/************** End of pragma.h **********************************************/
/************** Continuing where we left off in pragma.c *********************/
@@ -122862,6 +123321,15 @@ SQLITE_PRIVATE void sqlite3Pragma(
Index *pIdx;
Table *pTab;
pIdx = sqlite3FindIndex(db, zRight, zDb);
+ if( pIdx==0 ){
+ /* If there is no index named zRight, check to see if there is a
+ ** WITHOUT ROWID table named zRight, and if there is, show the
+ ** structure of the PRIMARY KEY index for that table. */
+ pTab = sqlite3LocateTable(pParse, LOCATE_NOERR, zRight, zDb);
+ if( pTab && !HasRowid(pTab) ){
+ pIdx = sqlite3PrimaryKeyIndex(pTab);
+ }
+ }
if( pIdx ){
int iIdxDb = sqlite3SchemaToIndex(db, pIdx->pSchema);
int i;
@@ -122941,7 +123409,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
break;
-#ifdef SQLITE_INTROSPECTION_PRAGMAS
+#ifndef SQLITE_OMIT_INTROSPECTION_PRAGMAS
case PragTyp_FUNCTION_LIST: {
int i;
HashElem *j;
@@ -124276,9 +124744,11 @@ SQLITE_PRIVATE int sqlite3IndexHasDuplicateRootPage(Index *pIndex){
**
** Each callback contains the following information:
**
-** argv[0] = name of thing being created
-** argv[1] = root page number for table or index. 0 for trigger or view.
-** argv[2] = SQL text for the CREATE statement.
+** argv[0] = type of object: "table", "index", "trigger", or "view".
+** argv[1] = name of thing being created
+** argv[2] = associated table if an index or trigger
+** argv[3] = root page number for table or index. 0 for trigger or view.
+** argv[4] = SQL text for the CREATE statement.
**
*/
SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char **NotUsed){
@@ -124286,21 +124756,21 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char
sqlite3 *db = pData->db;
int iDb = pData->iDb;
- assert( argc==3 );
+ assert( argc==5 );
UNUSED_PARAMETER2(NotUsed, argc);
assert( sqlite3_mutex_held(db->mutex) );
DbClearProperty(db, iDb, DB_Empty);
pData->nInitRow++;
if( db->mallocFailed ){
- corruptSchema(pData, argv[0], 0);
+ corruptSchema(pData, argv[1], 0);
return 1;
}
assert( iDb>=0 && iDb<db->nDb );
if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */
- if( argv[1]==0 ){
- corruptSchema(pData, argv[0], 0);
- }else if( sqlite3_strnicmp(argv[2],"create ",7)==0 ){
+ if( argv[3]==0 ){
+ corruptSchema(pData, argv[1], 0);
+ }else if( sqlite3_strnicmp(argv[4],"create ",7)==0 ){
/* Call the parser to process a CREATE TABLE, INDEX or VIEW.
** But because db->init.busy is set to 1, no VDBE code is generated
** or executed. All the parser does is build the internal data
@@ -124313,9 +124783,10 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char
assert( db->init.busy );
db->init.iDb = iDb;
- db->init.newTnum = sqlite3Atoi(argv[1]);
+ db->init.newTnum = sqlite3Atoi(argv[3]);
db->init.orphanTrigger = 0;
- TESTONLY(rcp = ) sqlite3_prepare(db, argv[2], -1, &pStmt, 0);
+ db->init.azInit = argv;
+ TESTONLY(rcp = ) sqlite3_prepare(db, argv[4], -1, &pStmt, 0);
rc = db->errCode;
assert( (rc&0xFF)==(rcp&0xFF) );
db->init.iDb = saved_iDb;
@@ -124324,17 +124795,17 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char
if( db->init.orphanTrigger ){
assert( iDb==1 );
}else{
- pData->rc = rc;
+ if( rc > pData->rc ) pData->rc = rc;
if( rc==SQLITE_NOMEM ){
sqlite3OomFault(db);
}else if( rc!=SQLITE_INTERRUPT && (rc&0xFF)!=SQLITE_LOCKED ){
- corruptSchema(pData, argv[0], sqlite3_errmsg(db));
+ corruptSchema(pData, argv[1], sqlite3_errmsg(db));
}
}
}
sqlite3_finalize(pStmt);
- }else if( argv[0]==0 || (argv[2]!=0 && argv[2][0]!=0) ){
- corruptSchema(pData, argv[0], 0);
+ }else if( argv[1]==0 || (argv[4]!=0 && argv[4][0]!=0) ){
+ corruptSchema(pData, argv[1], 0);
}else{
/* If the SQL column is blank it means this is an index that
** was created to be the PRIMARY KEY or to fulfill a UNIQUE
@@ -124343,13 +124814,13 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char
** to do here is record the root page number for that index.
*/
Index *pIndex;
- pIndex = sqlite3FindIndex(db, argv[0], db->aDb[iDb].zDbSName);
+ pIndex = sqlite3FindIndex(db, argv[1], db->aDb[iDb].zDbSName);
if( pIndex==0
- || sqlite3GetInt32(argv[1],&pIndex->tnum)==0
+ || sqlite3GetInt32(argv[3],&pIndex->tnum)==0
|| pIndex->tnum<2
|| sqlite3IndexHasDuplicateRootPage(pIndex)
){
- corruptSchema(pData, argv[0], pIndex?"invalid rootpage":"orphan index");
+ corruptSchema(pData, argv[1], pIndex?"invalid rootpage":"orphan index");
}
}
return 0;
@@ -124370,7 +124841,7 @@ SQLITE_PRIVATE int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFl
int size;
#endif
Db *pDb;
- char const *azArg[4];
+ char const *azArg[6];
int meta[5];
InitData initData;
const char *zMasterName;
@@ -124389,18 +124860,20 @@ SQLITE_PRIVATE int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFl
** table name will be inserted automatically by the parser so we can just
** use the abbreviation "x" here. The parser will also automatically tag
** the schema table as read-only. */
- azArg[0] = zMasterName = SCHEMA_TABLE(iDb);
- azArg[1] = "1";
- azArg[2] = "CREATE TABLE x(type text,name text,tbl_name text,"
+ azArg[0] = "table";
+ azArg[1] = zMasterName = SCHEMA_TABLE(iDb);
+ azArg[2] = azArg[1];
+ azArg[3] = "1";
+ azArg[4] = "CREATE TABLE x(type text,name text,tbl_name text,"
"rootpage int,sql text)";
- azArg[3] = 0;
+ azArg[5] = 0;
initData.db = db;
initData.iDb = iDb;
initData.rc = SQLITE_OK;
initData.pzErrMsg = pzErrMsg;
initData.mInitFlags = mFlags;
initData.nInitRow = 0;
- sqlite3InitCallback(&initData, 3, (char **)azArg, 0);
+ sqlite3InitCallback(&initData, 5, (char **)azArg, 0);
if( initData.rc ){
rc = initData.rc;
goto error_out;
@@ -124526,7 +124999,7 @@ SQLITE_PRIVATE int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFl
{
char *zSql;
zSql = sqlite3MPrintf(db,
- "SELECT name, rootpage, sql FROM \"%w\".%s ORDER BY rowid",
+ "SELECT*FROM\"%w\".%s ORDER BY rowid",
db->aDb[iDb].zDbSName, zMasterName);
#ifndef SQLITE_OMIT_AUTHORIZATION
{
@@ -124847,7 +125320,10 @@ static int sqlite3Prepare(
rc = sParse.rc;
#ifndef SQLITE_OMIT_EXPLAIN
- if( rc==SQLITE_OK && sParse.pVdbe && sParse.explain ){
+ /* Justification for the ALWAYS(): The only way for rc to be SQLITE_OK and
+ ** sParse.pVdbe to be NULL is if the input SQL is an empty string, but in
+ ** that case, sParse.explain will be false. */
+ if( sParse.explain && rc==SQLITE_OK && ALWAYS(sParse.pVdbe) ){
static const char * const azColName[] = {
"addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment",
"id", "parent", "notused", "detail"
@@ -124872,8 +125348,8 @@ static int sqlite3Prepare(
if( db->init.busy==0 ){
sqlite3VdbeSetSql(sParse.pVdbe, zSql, (int)(sParse.zTail-zSql), prepFlags);
}
- if( sParse.pVdbe && (rc!=SQLITE_OK || db->mallocFailed) ){
- sqlite3VdbeFinalize(sParse.pVdbe);
+ if( rc!=SQLITE_OK || db->mallocFailed ){
+ if( sParse.pVdbe ) sqlite3VdbeFinalize(sParse.pVdbe);
assert(!(*ppStmt));
}else{
*ppStmt = (sqlite3_stmt*)sParse.pVdbe;
@@ -125244,6 +125720,7 @@ static void clearSelect(sqlite3 *db, Select *p, int bFree){
if( OK_IF_ALWAYS_TRUE(p->pWinDefn) ){
sqlite3WindowListDelete(db, p->pWinDefn);
}
+ assert( p->pWin==0 );
#endif
if( OK_IF_ALWAYS_TRUE(p->pWith) ) sqlite3WithDelete(db, p->pWith);
if( bFree ) sqlite3DbFreeNN(db, p);
@@ -125807,7 +126284,7 @@ static void pushOntoSorter(
if( pParse->db->mallocFailed ) return;
pOp->p2 = nKey + nData;
pKI = pOp->p4.pKeyInfo;
- memset(pKI->aSortOrder, 0, pKI->nKeyField); /* Makes OP_Jump testable */
+ memset(pKI->aSortFlags, 0, pKI->nKeyField); /* Makes OP_Jump testable */
sqlite3VdbeChangeP4(v, -1, (char*)pKI, P4_KEYINFO);
testcase( pKI->nAllField > pKI->nKeyField+2 );
pOp->p4.pKeyInfo = sqlite3KeyInfoFromExprList(pParse,pSort->pOrderBy,nOBSat,
@@ -126418,7 +126895,7 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
int nExtra = (N+X)*(sizeof(CollSeq*)+1) - sizeof(CollSeq*);
KeyInfo *p = sqlite3DbMallocRawNN(db, sizeof(KeyInfo) + nExtra);
if( p ){
- p->aSortOrder = (u8*)&p->aColl[N+X];
+ p->aSortFlags = (u8*)&p->aColl[N+X];
p->nKeyField = (u16)N;
p->nAllField = (u16)(N+X);
p->enc = ENC(db);
@@ -126495,7 +126972,7 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoFromExprList(
assert( sqlite3KeyInfoIsWriteable(pInfo) );
for(i=iStart, pItem=pList->a+iStart; i<nExpr; i++, pItem++){
pInfo->aColl[i-iStart] = sqlite3ExprNNCollSeq(pParse, pItem->pExpr);
- pInfo->aSortOrder[i-iStart] = pItem->sortOrder;
+ pInfo->aSortFlags[i-iStart] = pItem->sortFlags;
}
}
return pInfo;
@@ -126787,8 +127264,6 @@ static const char *columnTypeImpl(
assert( pExpr!=0 );
assert( pNC->pSrcList!=0 );
- assert( pExpr->op!=TK_AGG_COLUMN ); /* This routine runes before aggregates
- ** are processed */
switch( pExpr->op ){
case TK_COLUMN: {
/* The expression is a column. Locate the table the column is being
@@ -127105,12 +127580,11 @@ SQLITE_PRIVATE int sqlite3ColumnsFromExprList(
if( (zName = pEList->a[i].zName)!=0 ){
/* If the column contains an "AS <name>" phrase, use <name> as the name */
}else{
- Expr *pColExpr = sqlite3ExprSkipCollate(pEList->a[i].pExpr);
+ Expr *pColExpr = sqlite3ExprSkipCollateAndLikely(pEList->a[i].pExpr);
while( pColExpr->op==TK_DOT ){
pColExpr = pColExpr->pRight;
assert( pColExpr!=0 );
}
- assert( pColExpr->op!=TK_AGG_COLUMN );
if( pColExpr->op==TK_COLUMN ){
/* For columns use the column name name */
int iCol = pColExpr->iColumn;
@@ -127178,7 +127652,8 @@ SQLITE_PRIVATE int sqlite3ColumnsFromExprList(
SQLITE_PRIVATE void sqlite3SelectAddColumnTypeAndCollation(
Parse *pParse, /* Parsing contexts */
Table *pTab, /* Add column type information to this table */
- Select *pSelect /* SELECT used to determine types and collations */
+ Select *pSelect, /* SELECT used to determine types and collations */
+ char aff /* Default affinity for columns */
){
sqlite3 *db = pParse->db;
NameContext sNC;
@@ -127211,7 +127686,7 @@ SQLITE_PRIVATE void sqlite3SelectAddColumnTypeAndCollation(
pCol->colFlags |= COLFLAG_HASTYPE;
}
}
- if( pCol->affinity==0 ) pCol->affinity = SQLITE_AFF_BLOB;
+ if( pCol->affinity<=SQLITE_AFF_NONE ) pCol->affinity = aff;
pColl = sqlite3ExprCollSeq(pParse, p);
if( pColl && pCol->zColl==0 ){
pCol->zColl = sqlite3DbStrDup(db, pColl->zName);
@@ -127224,7 +127699,7 @@ SQLITE_PRIVATE void sqlite3SelectAddColumnTypeAndCollation(
** Given a SELECT statement, generate a Table structure that describes
** the result set of that SELECT.
*/
-SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
+SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect, char aff){
Table *pTab;
sqlite3 *db = pParse->db;
u64 savedFlags;
@@ -127244,7 +127719,7 @@ SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
pTab->zName = 0;
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
sqlite3ColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol);
- sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSelect);
+ sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSelect, aff);
pTab->iPKey = -1;
if( db->mallocFailed ){
sqlite3DeleteTable(db, pTab);
@@ -127398,7 +127873,7 @@ static KeyInfo *multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){
}
assert( sqlite3KeyInfoIsWriteable(pRet) );
pRet->aColl[i] = pColl;
- pRet->aSortOrder[i] = pOrderBy->a[i].sortOrder;
+ pRet->aSortFlags[i] = pOrderBy->a[i].sortFlags;
}
}
@@ -128109,11 +128584,14 @@ static int generateOutputSubroutine(
/* If this is a scalar select that is part of an expression, then
** store the results in the appropriate memory cell and break out
- ** of the scan loop.
+ ** of the scan loop. Note that the select might return multiple columns
+ ** if it is the RHS of a row-value IN operator.
*/
case SRT_Mem: {
- assert( pIn->nSdst==1 || pParse->nErr>0 ); testcase( pIn->nSdst!=1 );
- sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSDParm, 1);
+ if( pParse->nErr==0 ){
+ testcase( pIn->nSdst>1 );
+ sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSDParm, pIn->nSdst);
+ }
/* The LIMIT clause will jump out of the loop for us */
break;
}
@@ -128370,7 +128848,7 @@ static int multiSelectOrderBy(
assert( sqlite3KeyInfoIsWriteable(pKeyDup) );
for(i=0; i<nExpr; i++){
pKeyDup->aColl[i] = multiSelectCollSeq(pParse, p, i);
- pKeyDup->aSortOrder[i] = 0;
+ pKeyDup->aSortFlags[i] = 0;
}
}
}
@@ -128620,6 +129098,18 @@ static Expr *substExpr(
}
sqlite3ExprDelete(db, pExpr);
pExpr = pNew;
+
+ /* Ensure that the expression now has an implicit collation sequence,
+ ** just as it did when it was a column of a view or sub-query. */
+ if( pExpr ){
+ if( pExpr->op!=TK_COLUMN && pExpr->op!=TK_COLLATE ){
+ CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse, pExpr);
+ pExpr = sqlite3ExprAddCollateString(pSubst->pParse, pExpr,
+ (pColl ? pColl->zName : "BINARY")
+ );
+ }
+ ExprClearProperty(pExpr, EP_Collate);
+ }
}
}
}else{
@@ -128633,6 +129123,14 @@ static Expr *substExpr(
}else{
substExprList(pSubst, pExpr->x.pList);
}
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ Window *pWin = pExpr->y.pWin;
+ pWin->pFilter = substExpr(pSubst, pWin->pFilter);
+ substExprList(pSubst, pWin->pPartition);
+ substExprList(pSubst, pWin->pOrderBy);
+ }
+#endif
}
return pExpr;
}
@@ -129093,6 +129591,7 @@ static int flattenSubquery(
for(pParent=p; pParent; pParent=pParent->pPrior, pSub=pSub->pPrior){
int nSubSrc;
u8 jointype = 0;
+ assert( pSub!=0 );
pSubSrc = pSub->pSrc; /* FROM clause of subquery */
nSubSrc = pSubSrc->nSrc; /* Number of terms in subquery FROM clause */
pSrc = pParent->pSrc; /* FROM clause of the outer query */
@@ -129543,24 +130042,27 @@ static u8 minMaxQuery(sqlite3 *db, Expr *pFunc, ExprList **ppMinMax){
ExprList *pEList = pFunc->x.pList; /* Arguments to agg function */
const char *zFunc; /* Name of aggregate function pFunc */
ExprList *pOrderBy;
- u8 sortOrder;
+ u8 sortFlags;
assert( *ppMinMax==0 );
assert( pFunc->op==TK_AGG_FUNCTION );
- if( pEList==0 || pEList->nExpr!=1 ) return eRet;
+ assert( !IsWindowFunc(pFunc) );
+ if( pEList==0 || pEList->nExpr!=1 || ExprHasProperty(pFunc, EP_WinFunc) ){
+ return eRet;
+ }
zFunc = pFunc->u.zToken;
if( sqlite3StrICmp(zFunc, "min")==0 ){
eRet = WHERE_ORDERBY_MIN;
- sortOrder = SQLITE_SO_ASC;
+ sortFlags = KEYINFO_ORDER_BIGNULL;
}else if( sqlite3StrICmp(zFunc, "max")==0 ){
eRet = WHERE_ORDERBY_MAX;
- sortOrder = SQLITE_SO_DESC;
+ sortFlags = KEYINFO_ORDER_DESC;
}else{
return eRet;
}
*ppMinMax = pOrderBy = sqlite3ExprListDup(db, pEList, 0);
assert( pOrderBy!=0 || db->mallocFailed );
- if( pOrderBy ) pOrderBy->a[0].sortOrder = sortOrder;
+ if( pOrderBy ) pOrderBy->a[0].sortFlags = sortFlags;
return eRet;
}
@@ -129594,7 +130096,7 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){
if( pExpr->op!=TK_AGG_FUNCTION ) return 0;
if( NEVER(pAggInfo->nFunc==0) ) return 0;
if( (pAggInfo->aFunc[0].pFunc->funcFlags&SQLITE_FUNC_COUNT)==0 ) return 0;
- if( pExpr->flags&EP_Distinct ) return 0;
+ if( ExprHasProperty(pExpr, EP_Distinct|EP_WinFunc) ) return 0;
return pTab;
}
@@ -130041,6 +130543,10 @@ static int selectExpander(Walker *pWalker, Select *p){
u8 eCodeOrig = pWalker->eCode;
if( sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort;
assert( pFrom->pSelect==0 );
+ if( pTab->pSelect && (db->flags & SQLITE_EnableView)==0 ){
+ sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited",
+ pTab->zName);
+ }
pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect, 0);
nCol = pTab->nCol;
pTab->nCol = -1;
@@ -130334,7 +130840,8 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
Select *pSel = pFrom->pSelect;
if( pSel ){
while( pSel->pPrior ) pSel = pSel->pPrior;
- sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSel);
+ sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSel,
+ SQLITE_AFF_NONE);
}
}
}
@@ -130474,6 +130981,25 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){
int regAgg;
ExprList *pList = pF->pExpr->x.pList;
assert( !ExprHasProperty(pF->pExpr, EP_xIsSelect) );
+ assert( !IsWindowFunc(pF->pExpr) );
+ if( ExprHasProperty(pF->pExpr, EP_WinFunc) ){
+ Expr *pFilter = pF->pExpr->y.pWin->pFilter;
+ if( pAggInfo->nAccumulator
+ && (pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL)
+ ){
+ if( regHit==0 ) regHit = ++pParse->nMem;
+ /* If this is the first row of the group (regAcc==0), clear the
+ ** "magnet" register regHit so that the accumulator registers
+ ** are populated if the FILTER clause jumps over the the
+ ** invocation of min() or max() altogether. Or, if this is not
+ ** the first row (regAcc==1), set the magnet register so that the
+ ** accumulators are not populated unless the min()/max() is invoked and
+ ** indicates that they should be. */
+ sqlite3VdbeAddOp2(v, OP_Copy, regAcc, regHit);
+ }
+ addrNext = sqlite3VdbeMakeLabel(pParse);
+ sqlite3ExprIfFalse(pParse, pFilter, addrNext, SQLITE_JUMPIFNULL);
+ }
if( pList ){
nArg = pList->nExpr;
regAgg = sqlite3GetTempRange(pParse, nArg);
@@ -130483,7 +131009,9 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){
regAgg = 0;
}
if( pF->iDistinct>=0 ){
- addrNext = sqlite3VdbeMakeLabel(pParse);
+ if( addrNext==0 ){
+ addrNext = sqlite3VdbeMakeLabel(pParse);
+ }
testcase( nArg==0 ); /* Error condition */
testcase( nArg>1 ); /* Also an error */
codeDistinct(pParse, pF->iDistinct, addrNext, 1, regAgg);
@@ -130519,6 +131047,7 @@ static void updateAccumulator(Parse *pParse, int regAcc, AggInfo *pAggInfo){
for(i=0, pC=pAggInfo->aCol; i<pAggInfo->nAccumulator; i++, pC++){
sqlite3ExprCode(pParse, pC->pExpr, pC->iMem);
}
+
pAggInfo->directMode = 0;
if( addrHitTest ){
sqlite3VdbeJumpHere(v, addrHitTest);
@@ -130564,7 +131093,7 @@ static int havingToWhereExprCb(Walker *pWalker, Expr *pExpr){
Select *pS = pWalker->u.pSelect;
if( sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, pS->pGroupBy) ){
sqlite3 *db = pWalker->pParse->db;
- Expr *pNew = sqlite3ExprAlloc(db, TK_INTEGER, &sqlite3IntTokens[1], 0);
+ Expr *pNew = sqlite3Expr(db, TK_INTEGER, "1");
if( pNew ){
Expr *pWhere = pS->pWhere;
SWAP(Expr, *pNew, *pExpr);
@@ -130986,7 +131515,7 @@ SQLITE_PRIVATE int sqlite3Select(
** assume the column name is non-NULL and segfault. The use of an empty
** string for the fake column name seems safer.
*/
- if( pItem->colUsed==0 ){
+ if( pItem->colUsed==0 && pItem->zName!=0 ){
sqlite3AuthCheck(pParse, SQLITE_READ, pItem->zName, "", pItem->zDatabase);
}
@@ -131000,8 +131529,15 @@ SQLITE_PRIVATE int sqlite3Select(
** technically harmless for it to be generated multiple times. The
** following assert() will detect if something changes to cause
** the same subquery to be coded multiple times, as a signal to the
- ** developers to try to optimize the situation. */
- assert( pItem->addrFillSub==0 );
+ ** developers to try to optimize the situation.
+ **
+ ** Update 2019-07-24:
+ ** See ticket https://sqlite.org/src/tktview/c52b09c7f38903b1311cec40.
+ ** The dbsqlfuzz fuzzer found a case where the same subquery gets
+ ** coded twice. So this assert() now becomes a testcase(). It should
+ ** be very rare, though.
+ */
+ testcase( pItem->addrFillSub!=0 );
/* Increment Parse.nHeight by the height of the largest expression
** tree referred to by this, the parent select. The child select
@@ -131075,7 +131611,7 @@ SQLITE_PRIVATE int sqlite3Select(
int retAddr;
struct SrcList_item *pPrior;
- assert( pItem->addrFillSub==0 );
+ testcase( pItem->addrFillSub==0 ); /* Ticket c52b09c7f38903b1311 */
pItem->regReturn = ++pParse->nMem;
topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn);
pItem->addrFillSub = topAddr+1;
@@ -131315,23 +131851,35 @@ SQLITE_PRIVATE int sqlite3Select(
}
assert( 66==sqlite3LogEst(100) );
if( p->nSelectRow>66 ) p->nSelectRow = 66;
+
+ /* If there is both a GROUP BY and an ORDER BY clause and they are
+ ** identical, then it may be possible to disable the ORDER BY clause
+ ** on the grounds that the GROUP BY will cause elements to come out
+ ** in the correct order. It also may not - the GROUP BY might use a
+ ** database index that causes rows to be grouped together as required
+ ** but not actually sorted. Either way, record the fact that the
+ ** ORDER BY and GROUP BY clauses are the same by setting the orderByGrp
+ ** variable. */
+ if( sSort.pOrderBy && pGroupBy->nExpr==sSort.pOrderBy->nExpr ){
+ int ii;
+ /* The GROUP BY processing doesn't care whether rows are delivered in
+ ** ASC or DESC order - only that each group is returned contiguously.
+ ** So set the ASC/DESC flags in the GROUP BY to match those in the
+ ** ORDER BY to maximize the chances of rows being delivered in an
+ ** order that makes the ORDER BY redundant. */
+ for(ii=0; ii<pGroupBy->nExpr; ii++){
+ u8 sortFlags = sSort.pOrderBy->a[ii].sortFlags & KEYINFO_ORDER_DESC;
+ pGroupBy->a[ii].sortFlags = sortFlags;
+ }
+ if( sqlite3ExprListCompare(pGroupBy, sSort.pOrderBy, -1)==0 ){
+ orderByGrp = 1;
+ }
+ }
}else{
assert( 0==sqlite3LogEst(1) );
p->nSelectRow = 0;
}
- /* If there is both a GROUP BY and an ORDER BY clause and they are
- ** identical, then it may be possible to disable the ORDER BY clause
- ** on the grounds that the GROUP BY will cause elements to come out
- ** in the correct order. It also may not - the GROUP BY might use a
- ** database index that causes rows to be grouped together as required
- ** but not actually sorted. Either way, record the fact that the
- ** ORDER BY and GROUP BY clauses are the same by setting the orderByGrp
- ** variable. */
- if( sqlite3ExprListCompare(pGroupBy, sSort.pOrderBy, -1)==0 ){
- orderByGrp = 1;
- }
-
/* Create a label to jump to when we want to abort the query */
addrEnd = sqlite3VdbeMakeLabel(pParse);
@@ -131366,9 +131914,16 @@ SQLITE_PRIVATE int sqlite3Select(
minMaxFlag = WHERE_ORDERBY_NORMAL;
}
for(i=0; i<sAggInfo.nFunc; i++){
- assert( !ExprHasProperty(sAggInfo.aFunc[i].pExpr, EP_xIsSelect) );
+ Expr *pExpr = sAggInfo.aFunc[i].pExpr;
+ assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
sNC.ncFlags |= NC_InAggFunc;
- sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->x.pList);
+ sqlite3ExprAnalyzeAggList(&sNC, pExpr->x.pList);
+#ifndef SQLITE_OMIT_WINDOWFUNC
+ assert( !IsWindowFunc(pExpr) );
+ if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ sqlite3ExprAnalyzeAggregates(&sNC, pExpr->y.pWin->pFilter);
+ }
+#endif
sNC.ncFlags &= ~NC_InAggFunc;
}
sAggInfo.mxReg = pParse->nMem;
@@ -131680,13 +132235,18 @@ SQLITE_PRIVATE int sqlite3Select(
{
int regAcc = 0; /* "populate accumulators" flag */
- /* If there are accumulator registers but no min() or max() functions,
- ** allocate register regAcc. Register regAcc will contain 0 the first
- ** time the inner loop runs, and 1 thereafter. The code generated
- ** by updateAccumulator() only updates the accumulator registers if
- ** regAcc contains 0. */
+ /* If there are accumulator registers but no min() or max() functions
+ ** without FILTER clauses, allocate register regAcc. Register regAcc
+ ** will contain 0 the first time the inner loop runs, and 1 thereafter.
+ ** The code generated by updateAccumulator() uses this to ensure
+ ** that the accumulator registers are (a) updated only once if
+ ** there are no min() or max functions or (b) always updated for the
+ ** first row visited by the aggregate, so that they are updated at
+ ** least once even if the FILTER clause means the min() or max()
+ ** function visits zero rows. */
if( sAggInfo.nAccumulator ){
for(i=0; i<sAggInfo.nFunc; i++){
+ if( ExprHasProperty(sAggInfo.aFunc[i].pExpr, EP_WinFunc) ) continue;
if( sAggInfo.aFunc[i].pFunc->funcFlags&SQLITE_FUNC_NEEDCOLL ) break;
}
if( i==sAggInfo.nFunc ){
@@ -132157,7 +132717,11 @@ SQLITE_PRIVATE void sqlite3BeginTrigger(
/* Check that the trigger name is not reserved and that no trigger of the
** specified name exists */
zName = sqlite3NameFromToken(db, pName);
- if( !zName || SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
+ if( zName==0 ){
+ assert( db->mallocFailed );
+ goto trigger_cleanup;
+ }
+ if( sqlite3CheckObjectName(pParse, zName, "trigger", pTab->zName) ){
goto trigger_cleanup;
}
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
@@ -132320,6 +132884,7 @@ SQLITE_PRIVATE void sqlite3FinishTrigger(
Trigger *pLink = pTrig;
Hash *pHash = &db->aDb[iDb].pSchema->trigHash;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
+ assert( pLink!=0 );
pTrig = sqlite3HashInsert(pHash, zName, pTrig);
if( pTrig ){
sqlite3OomFault(db);
@@ -132438,6 +133003,9 @@ SQLITE_PRIVATE TriggerStep *sqlite3TriggerInsertStep(
pTriggerStep->pIdList = pColumn;
pTriggerStep->pUpsert = pUpsert;
pTriggerStep->orconf = orconf;
+ if( pUpsert ){
+ sqlite3HasExplicitNulls(pParse, pUpsert->pUpsertTarget);
+ }
}else{
testcase( pColumn );
sqlite3IdListDelete(db, pColumn);
@@ -132593,10 +133161,9 @@ SQLITE_PRIVATE void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
iDb = sqlite3SchemaToIndex(pParse->db, pTrigger->pSchema);
assert( iDb>=0 && iDb<db->nDb );
pTable = tableOfTrigger(pTrigger);
- assert( pTable );
- assert( pTable->pSchema==pTrigger->pSchema || iDb==1 );
+ assert( (pTable && pTable->pSchema==pTrigger->pSchema) || iDb==1 );
#ifndef SQLITE_OMIT_AUTHORIZATION
- {
+ if( pTable ){
int code = SQLITE_DROP_TRIGGER;
const char *zDb = db->aDb[iDb].zDbSName;
const char *zTab = SCHEMA_TABLE(iDb);
@@ -132610,7 +133177,6 @@ SQLITE_PRIVATE void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
/* Generate code to destroy the database record of the trigger.
*/
- assert( pTable!=0 );
if( (v = sqlite3GetVdbe(pParse))!=0 ){
sqlite3NestedParse(pParse,
"DELETE FROM %Q.%s WHERE name=%Q AND type='trigger'",
@@ -132634,9 +133200,11 @@ SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTrigger(sqlite3 *db, int iDb, const ch
if( ALWAYS(pTrigger) ){
if( pTrigger->pSchema==pTrigger->pTabSchema ){
Table *pTab = tableOfTrigger(pTrigger);
- Trigger **pp;
- for(pp=&pTab->pTrigger; *pp!=pTrigger; pp=&((*pp)->pNext));
- *pp = (*pp)->pNext;
+ if( pTab ){
+ Trigger **pp;
+ for(pp=&pTab->pTrigger; *pp!=pTrigger; pp=&((*pp)->pNext));
+ *pp = (*pp)->pNext;
+ }
}
sqlite3DeleteTrigger(db, pTrigger);
db->mDbFlags |= DBFLAG_SchemaChange;
@@ -133884,28 +134452,30 @@ SQLITE_PRIVATE void sqlite3Update(
}
if( !isView ){
- int addr1 = 0; /* Address of jump instruction */
-
/* Do constraint checks. */
assert( regOldRowid>0 );
sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
regNewRowid, regOldRowid, chngKey, onError, labelContinue, &bReplace,
aXRef, 0);
- /* Do FK constraint checks. */
- if( hasFK ){
- sqlite3FkCheck(pParse, pTab, regOldRowid, 0, aXRef, chngKey);
- }
-
- /* Delete the index entries associated with the current record. */
+ /* If REPLACE conflict handling may have been used, or if the PK of the
+ ** row is changing, then the GenerateConstraintChecks() above may have
+ ** moved cursor iDataCur. Reseek it. */
if( bReplace || chngKey ){
if( pPk ){
- addr1 = sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, 0, regKey, nKey);
+ sqlite3VdbeAddOp4Int(v, OP_NotFound,iDataCur,labelContinue,regKey,nKey);
}else{
- addr1 = sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, 0, regOldRowid);
+ sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue,regOldRowid);
}
VdbeCoverageNeverTaken(v);
}
+
+ /* Do FK constraint checks. */
+ if( hasFK ){
+ sqlite3FkCheck(pParse, pTab, regOldRowid, 0, aXRef, chngKey);
+ }
+
+ /* Delete the index entries associated with the current record. */
sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, aRegIdx, -1);
/* If changing the rowid value, or if there are foreign key constraints
@@ -133935,9 +134505,6 @@ SQLITE_PRIVATE void sqlite3Update(
sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, 0);
}
#endif
- if( bReplace || chngKey ){
- sqlite3VdbeJumpHere(v, addr1);
- }
if( hasFK ){
sqlite3FkCheck(pParse, pTab, 0, regNewRowid, aXRef, chngKey);
@@ -134376,6 +134943,7 @@ SQLITE_PRIVATE void sqlite3UpsertDoUpdate(
sqlite3 *db = pParse->db;
SrcList *pSrc; /* FROM clause for the UPDATE */
int iDataCur;
+ int i;
assert( v!=0 );
assert( pUpsert!=0 );
@@ -134392,7 +134960,6 @@ SQLITE_PRIVATE void sqlite3UpsertDoUpdate(
Index *pPk = sqlite3PrimaryKeyIndex(pTab);
int nPk = pPk->nKeyCol;
int iPk = pParse->nMem+1;
- int i;
pParse->nMem += nPk;
for(i=0; i<nPk; i++){
int k;
@@ -134413,6 +134980,12 @@ SQLITE_PRIVATE void sqlite3UpsertDoUpdate(
/* pUpsert does not own pUpsertSrc - the outer INSERT statement does. So
** we have to make a copy before passing it down into sqlite3Update() */
pSrc = sqlite3SrcListDup(db, pUpsert->pUpsertSrc, 0);
+ /* excluded.* columns of type REAL need to be converted to a hard real */
+ for(i=0; i<pTab->nCol; i++){
+ if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){
+ sqlite3VdbeAddOp1(v, OP_RealAffinity, pUpsert->regData+i);
+ }
+ }
sqlite3Update(pParse, pSrc, pUpsert->pUpsertSet,
pUpsert->pUpsertWhere, OE_Abort, 0, 0, pUpsert);
pUpsert->pUpsertSet = 0; /* Will have been deleted by sqlite3Update() */
@@ -134876,6 +135449,9 @@ struct VtabCtx {
** Construct and install a Module object for a virtual table. When this
** routine is called, it is guaranteed that all appropriate locks are held
** and the module is not already part of the connection.
+**
+** If there already exists a module with zName, replace it with the new one.
+** If pModule==0, then delete the module zName if it exists.
*/
SQLITE_PRIVATE Module *sqlite3VtabCreateModule(
sqlite3 *db, /* Database in which module is registered */
@@ -134885,25 +135461,36 @@ SQLITE_PRIVATE Module *sqlite3VtabCreateModule(
void (*xDestroy)(void *) /* Module destructor function */
){
Module *pMod;
- int nName = sqlite3Strlen30(zName);
- pMod = (Module *)sqlite3Malloc(sizeof(Module) + nName + 1);
- if( pMod==0 ){
- sqlite3OomFault(db);
+ Module *pDel;
+ char *zCopy;
+ if( pModule==0 ){
+ zCopy = (char*)zName;
+ pMod = 0;
}else{
- Module *pDel;
- char *zCopy = (char *)(&pMod[1]);
+ int nName = sqlite3Strlen30(zName);
+ pMod = (Module *)sqlite3Malloc(sizeof(Module) + nName + 1);
+ if( pMod==0 ){
+ sqlite3OomFault(db);
+ return 0;
+ }
+ zCopy = (char *)(&pMod[1]);
memcpy(zCopy, zName, nName+1);
pMod->zName = zCopy;
pMod->pModule = pModule;
pMod->pAux = pAux;
pMod->xDestroy = xDestroy;
pMod->pEpoTab = 0;
- pDel = (Module *)sqlite3HashInsert(&db->aModule,zCopy,(void*)pMod);
- assert( pDel==0 || pDel==pMod );
- if( pDel ){
+ pMod->nRefModule = 1;
+ }
+ pDel = (Module *)sqlite3HashInsert(&db->aModule,zCopy,(void*)pMod);
+ if( pDel ){
+ if( pDel==pMod ){
sqlite3OomFault(db);
sqlite3DbFree(db, pDel);
pMod = 0;
+ }else{
+ sqlite3VtabEponymousTableClear(db, pDel);
+ sqlite3VtabModuleUnref(db, pDel);
}
}
return pMod;
@@ -134924,11 +135511,7 @@ static int createModule(
int rc = SQLITE_OK;
sqlite3_mutex_enter(db->mutex);
- if( sqlite3HashFind(&db->aModule, zName) ){
- rc = SQLITE_MISUSE_BKPT;
- }else{
- (void)sqlite3VtabCreateModule(db, zName, pModule, pAux, xDestroy);
- }
+ (void)sqlite3VtabCreateModule(db, zName, pModule, pAux, xDestroy);
rc = sqlite3ApiExit(db, rc);
if( rc!=SQLITE_OK && xDestroy ) xDestroy(pAux);
sqlite3_mutex_leave(db->mutex);
@@ -134968,6 +135551,44 @@ SQLITE_API int sqlite3_create_module_v2(
}
/*
+** External API to drop all virtual-table modules, except those named
+** on the azNames list.
+*/
+SQLITE_API int sqlite3_drop_modules(sqlite3 *db, const char** azNames){
+ HashElem *pThis, *pNext;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
+ for(pThis=sqliteHashFirst(&db->aModule); pThis; pThis=pNext){
+ Module *pMod = (Module*)sqliteHashData(pThis);
+ pNext = sqliteHashNext(pThis);
+ if( azNames ){
+ int ii;
+ for(ii=0; azNames[ii]!=0 && strcmp(azNames[ii],pMod->zName)!=0; ii++){}
+ if( azNames[ii]!=0 ) continue;
+ }
+ createModule(db, pMod->zName, 0, 0, 0);
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Decrement the reference count on a Module object. Destroy the
+** module when the reference count reaches zero.
+*/
+SQLITE_PRIVATE void sqlite3VtabModuleUnref(sqlite3 *db, Module *pMod){
+ assert( pMod->nRefModule>0 );
+ pMod->nRefModule--;
+ if( pMod->nRefModule==0 ){
+ if( pMod->xDestroy ){
+ pMod->xDestroy(pMod->pAux);
+ }
+ assert( pMod->pEpoTab==0 );
+ sqlite3DbFree(db, pMod);
+ }
+}
+
+/*
** Lock the virtual table so that it cannot be disconnected.
** Locks nest. Every lock should have a corresponding unlock.
** If an unlock is omitted, resources leaks will occur.
@@ -135006,6 +135627,7 @@ SQLITE_PRIVATE void sqlite3VtabUnlock(VTable *pVTab){
pVTab->nRef--;
if( pVTab->nRef==0 ){
sqlite3_vtab *p = pVTab->pVtab;
+ sqlite3VtabModuleUnref(pVTab->db, pVTab->pMod);
if( p ){
p->pModule->xDisconnect(p);
}
@@ -135410,6 +136032,7 @@ static int vtabCallConstructor(
** the sqlite3_vtab object if successful. */
memset(pVTable->pVtab, 0, sizeof(pVTable->pVtab[0]));
pVTable->pVtab->pModule = pMod->pModule;
+ pMod->nRefModule++;
pVTable->nRef = 1;
if( sCtx.bDeclared==0 ){
const char *zFormat = "vtable constructor did not declare schema: %s";
@@ -136194,13 +136817,15 @@ struct WhereLevel {
int addrCont; /* Jump here to continue with the next loop cycle */
int addrFirst; /* First instruction of interior of the loop */
int addrBody; /* Beginning of the body of this loop */
+ int regBignull; /* big-null flag reg. True if a NULL-scan is needed */
+ int addrBignull; /* Jump here for next part of big-null scan */
#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS
u32 iLikeRepCntr; /* LIKE range processing counter register (times 2) */
int addrLikeRep; /* LIKE range processing address */
#endif
u8 iFrom; /* Which entry in the FROM clause */
u8 op, p3, p5; /* Opcode, P3 & P5 of the opcode that ends the loop */
- int p1, p2; /* Operands of the opcode used to ends the loop */
+ int p1, p2; /* Operands of the opcode used to end the loop */
union { /* Information that depends on pWLoop->wsFlags */
struct {
int nIn; /* Number of entries in aInLoop[] */
@@ -136251,7 +136876,7 @@ struct WhereLoop {
u16 nEq; /* Number of equality constraints */
u16 nBtm; /* Size of BTM vector */
u16 nTop; /* Size of TOP vector */
- u16 nIdxCol; /* Index column used for ORDER BY */
+ u16 nDistinctCol; /* Index columns used to sort for DISTINCT */
Index *pIndex; /* Index used, or NULL */
} btree;
struct { /* Information for virtual tables */
@@ -136402,16 +137027,17 @@ struct WhereTerm {
#define TERM_ORINFO 0x10 /* Need to free the WhereTerm.u.pOrInfo object */
#define TERM_ANDINFO 0x20 /* Need to free the WhereTerm.u.pAndInfo obj */
#define TERM_OR_OK 0x40 /* Used during OR-clause processing */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
# define TERM_VNULL 0x80 /* Manufactured x>NULL or x<=NULL term */
#else
-# define TERM_VNULL 0x00 /* Disabled if not using stat3 */
+# define TERM_VNULL 0x00 /* Disabled if not using stat4 */
#endif
#define TERM_LIKEOPT 0x100 /* Virtual terms from the LIKE optimization */
#define TERM_LIKECOND 0x200 /* Conditionally this LIKE operator term */
#define TERM_LIKE 0x400 /* The original LIKE operator */
#define TERM_IS 0x800 /* Term.pExpr is an IS operator */
#define TERM_VARSELECT 0x1000 /* Term.pExpr contains a correlated sub-query */
+#define TERM_NOPARTIDX 0x2000 /* Not for use to enable a partial index */
/*
** An instance of the WhereScan object is used as an iterator for locating
@@ -136522,7 +137148,7 @@ struct WhereLoopBuilder {
ExprList *pOrderBy; /* ORDER BY clause */
WhereLoop *pNew; /* Template WhereLoop */
WhereOrSet *pOrSet; /* Record best loops here, if not NULL */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
UnpackedRecord *pRec; /* Probe for stat4 (if required) */
int nRecValid; /* Number of valid fields currently in pRec */
#endif
@@ -136709,6 +137335,7 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(Parse*, struct SrcList_item*, WhereC
#define WHERE_UNQ_WANTED 0x00010000 /* WHERE_ONEROW would have been helpful*/
#define WHERE_PARTIALIDX 0x00020000 /* The automatic index is partial */
#define WHERE_IN_EARLYOUT 0x00040000 /* Perhaps quit IN loops early */
+#define WHERE_BIGNULL_SORT 0x00080000 /* Column nEq of index is BIGNULL */
#endif /* !defined(SQLITE_WHEREINT_H) */
@@ -137013,9 +137640,9 @@ static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){
** Code an OP_Affinity opcode to apply the column affinity string zAff
** to the n registers starting at base.
**
-** As an optimization, SQLITE_AFF_BLOB entries (which are no-ops) at the
-** beginning and end of zAff are ignored. If all entries in zAff are
-** SQLITE_AFF_BLOB, then no code gets generated.
+** As an optimization, SQLITE_AFF_BLOB and SQLITE_AFF_NONE entries (which
+** are no-ops) at the beginning and end of zAff are ignored. If all entries
+** in zAff are SQLITE_AFF_BLOB or SQLITE_AFF_NONE, then no code gets generated.
**
** This routine makes its own copy of zAff so that the caller is free
** to modify zAff after this routine returns.
@@ -137028,15 +137655,16 @@ static void codeApplyAffinity(Parse *pParse, int base, int n, char *zAff){
}
assert( v!=0 );
- /* Adjust base and n to skip over SQLITE_AFF_BLOB entries at the beginning
- ** and end of the affinity string.
+ /* Adjust base and n to skip over SQLITE_AFF_BLOB and SQLITE_AFF_NONE
+ ** entries at the beginning and end of the affinity string.
*/
- while( n>0 && zAff[0]==SQLITE_AFF_BLOB ){
+ assert( SQLITE_AFF_NONE<SQLITE_AFF_BLOB );
+ while( n>0 && zAff[0]<=SQLITE_AFF_BLOB ){
n--;
base++;
zAff++;
}
- while( n>1 && zAff[n-1]==SQLITE_AFF_BLOB ){
+ while( n>1 && zAff[n-1]<=SQLITE_AFF_BLOB ){
n--;
}
@@ -137811,6 +138439,7 @@ typedef struct IdxExprTrans {
static int whereIndexExprTransNode(Walker *p, Expr *pExpr){
IdxExprTrans *pX = p->u.pIdxTrans;
if( sqlite3ExprCompare(0, pExpr, pX->pIdxExpr, pX->iTabCur)==0 ){
+ pExpr->affExpr = sqlite3ExprAffinity(pExpr);
pExpr->op = TK_COLUMN;
pExpr->iTable = pX->iIdxCur;
pExpr->iColumn = pX->iIdxCol;
@@ -138243,32 +138872,12 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
u8 bSeekPastNull = 0; /* True to seek past initial nulls */
u8 bStopAtNull = 0; /* Add condition to terminate at NULLs */
int omitTable; /* True if we use the index only */
-
+ int regBignull = 0; /* big-null flag register */
pIdx = pLoop->u.btree.pIndex;
iIdxCur = pLevel->iIdxCur;
assert( nEq>=pLoop->nSkip );
- /* If this loop satisfies a sort order (pOrderBy) request that
- ** was passed to this function to implement a "SELECT min(x) ..."
- ** query, then the caller will only allow the loop to run for
- ** a single iteration. This means that the first row returned
- ** should not have a NULL value stored in 'x'. If column 'x' is
- ** the first one after the nEq equality constraints in the index,
- ** this requires some special handling.
- */
- assert( pWInfo->pOrderBy==0
- || pWInfo->pOrderBy->nExpr==1
- || (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)==0 );
- if( (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)!=0
- && pWInfo->nOBSat>0
- && (pIdx->nKeyCol>nEq)
- ){
- assert( pLoop->nSkip==0 );
- bSeekPastNull = 1;
- nExtraReg = 1;
- }
-
/* Find any inequality constraint terms for the start and end
** of the range.
*/
@@ -138309,6 +138918,25 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
}
assert( pRangeEnd==0 || (pRangeEnd->wtFlags & TERM_VNULL)==0 );
+ /* If the WHERE_BIGNULL_SORT flag is set, then index column nEq uses
+ ** a non-default "big-null" sort (either ASC NULLS LAST or DESC NULLS
+ ** FIRST). In both cases separate ordered scans are made of those
+ ** index entries for which the column is null and for those for which
+ ** it is not. For an ASC sort, the non-NULL entries are scanned first.
+ ** For DESC, NULL entries are scanned first.
+ */
+ if( (pLoop->wsFlags & (WHERE_TOP_LIMIT|WHERE_BTM_LIMIT))==0
+ && (pLoop->wsFlags & WHERE_BIGNULL_SORT)!=0
+ ){
+ assert( bSeekPastNull==0 && nExtraReg==0 && nBtm==0 && nTop==0 );
+ assert( pRangeEnd==0 && pRangeStart==0 );
+ assert( pLoop->nSkip==0 );
+ nExtraReg = 1;
+ bSeekPastNull = 1;
+ pLevel->regBignull = regBignull = ++pParse->nMem;
+ pLevel->addrBignull = sqlite3VdbeMakeLabel(pParse);
+ }
+
/* If we are doing a reverse order scan on an ascending index, or
** a forward order scan on a descending index, interchange the
** start and end terms (pRangeStart and pRangeEnd).
@@ -138331,7 +138959,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
if( zStartAff && nTop ){
zEndAff = sqlite3DbStrDup(db, &zStartAff[nEq]);
}
- addrNxt = pLevel->addrNxt;
+ addrNxt = (regBignull ? pLevel->addrBignull : pLevel->addrNxt);
testcase( pRangeStart && (pRangeStart->eOperator & WO_LE)!=0 );
testcase( pRangeStart && (pRangeStart->eOperator & WO_GE)!=0 );
@@ -138365,10 +138993,14 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
}
bSeekPastNull = 0;
}else if( bSeekPastNull ){
+ startEq = 0;
sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
+ start_constraints = 1;
nConstraint++;
- startEq = 0;
+ }else if( regBignull ){
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
start_constraints = 1;
+ nConstraint++;
}
codeApplyAffinity(pParse, regBase, nConstraint - bSeekPastNull, zStartAff);
if( pLoop->nSkip>0 && nConstraint==pLoop->nSkip ){
@@ -138379,6 +139011,11 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
if( pLoop->wsFlags & WHERE_IN_EARLYOUT ){
sqlite3VdbeAddOp1(v, OP_SeekHit, iIdxCur);
}
+ if( regBignull ){
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, regBignull);
+ VdbeComment((v, "NULL-scan pass ctr"));
+ }
+
op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev];
assert( op!=0 );
sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint);
@@ -138389,6 +139026,23 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
VdbeCoverageIf(v, op==OP_SeekGE); testcase( op==OP_SeekGE );
VdbeCoverageIf(v, op==OP_SeekLE); testcase( op==OP_SeekLE );
VdbeCoverageIf(v, op==OP_SeekLT); testcase( op==OP_SeekLT );
+
+ assert( bSeekPastNull==0 || bStopAtNull==0 );
+ if( regBignull ){
+ assert( bSeekPastNull==1 || bStopAtNull==1 );
+ assert( bSeekPastNull==!bStopAtNull );
+ assert( bStopAtNull==startEq );
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+2);
+ op = aStartOp[(nConstraint>1)*4 + 2 + bRev];
+ sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase,
+ nConstraint-startEq);
+ VdbeCoverage(v);
+ VdbeCoverageIf(v, op==OP_Rewind); testcase( op==OP_Rewind );
+ VdbeCoverageIf(v, op==OP_Last); testcase( op==OP_Last );
+ VdbeCoverageIf(v, op==OP_SeekGE); testcase( op==OP_SeekGE );
+ VdbeCoverageIf(v, op==OP_SeekLE); testcase( op==OP_SeekLE );
+ assert( op==OP_Rewind || op==OP_Last || op==OP_SeekGE || op==OP_SeekLE);
+ }
}
/* Load the value for the inequality constraint at the end of the
@@ -138420,8 +139074,10 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
endEq = 1;
}
}else if( bStopAtNull ){
- sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
- endEq = 0;
+ if( regBignull==0 ){
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
+ endEq = 0;
+ }
nConstraint++;
}
sqlite3DbFree(db, zStartAff);
@@ -138432,6 +139088,12 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
/* Check if the index cursor is past the end of the range. */
if( nConstraint ){
+ if( regBignull ){
+ /* Except, skip the end-of-range check while doing the NULL-scan */
+ sqlite3VdbeAddOp2(v, OP_IfNot, regBignull, sqlite3VdbeCurrentAddr(v)+3);
+ VdbeComment((v, "If NULL-scan 2nd pass"));
+ VdbeCoverage(v);
+ }
op = aEndOp[bRev*2 + endEq];
sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint);
testcase( op==OP_IdxGT ); VdbeCoverageIf(v, op==OP_IdxGT );
@@ -138439,6 +139101,23 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
testcase( op==OP_IdxLT ); VdbeCoverageIf(v, op==OP_IdxLT );
testcase( op==OP_IdxLE ); VdbeCoverageIf(v, op==OP_IdxLE );
}
+ if( regBignull ){
+ /* During a NULL-scan, check to see if we have reached the end of
+ ** the NULLs */
+ assert( bSeekPastNull==!bStopAtNull );
+ assert( bSeekPastNull+bStopAtNull==1 );
+ assert( nConstraint+bSeekPastNull>0 );
+ sqlite3VdbeAddOp2(v, OP_If, regBignull, sqlite3VdbeCurrentAddr(v)+2);
+ VdbeComment((v, "If NULL-scan 1st pass"));
+ VdbeCoverage(v);
+ op = aEndOp[bRev*2 + bSeekPastNull];
+ sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase,
+ nConstraint+bSeekPastNull);
+ testcase( op==OP_IdxGT ); VdbeCoverageIf(v, op==OP_IdxGT );
+ testcase( op==OP_IdxGE ); VdbeCoverageIf(v, op==OP_IdxGE );
+ testcase( op==OP_IdxLT ); VdbeCoverageIf(v, op==OP_IdxLT );
+ testcase( op==OP_IdxLE ); VdbeCoverageIf(v, op==OP_IdxLE );
+ }
if( pLoop->wsFlags & WHERE_IN_EARLYOUT ){
sqlite3VdbeAddOp2(v, OP_SeekHit, iIdxCur, 1);
@@ -139065,7 +139744,7 @@ static int whereClauseInsert(WhereClause *pWC, Expr *p, u16 wtFlags){
}else{
pTerm->truthProb = 1;
}
- pTerm->pExpr = sqlite3ExprSkipCollate(p);
+ pTerm->pExpr = sqlite3ExprSkipCollateAndLikely(p);
pTerm->wtFlags = wtFlags;
pTerm->pWC = pWC;
pTerm->iParent = -1;
@@ -139098,10 +139777,16 @@ static int allowedOp(int op){
** the left hand side of a comparison overrides any collation sequence
** attached to the right. For the same reason the EP_Collate flag
** is not commuted.
+**
+** The return value is extra flags that are added to the WhereTerm object
+** after it is commuted. The only extra flag ever added is TERM_NOPARTIDX
+** which prevents the term from being used to enable a partial index if
+** COLLATE changes have been made.
*/
-static void exprCommute(Parse *pParse, Expr *pExpr){
+static u16 exprCommute(Parse *pParse, Expr *pExpr){
u16 expRight = (pExpr->pRight->flags & EP_Collate);
u16 expLeft = (pExpr->pLeft->flags & EP_Collate);
+ u16 wtFlags = 0;
assert( allowedOp(pExpr->op) && pExpr->op!=TK_IN );
if( expRight==expLeft ){
/* Either X and Y both have COLLATE operator or neither do */
@@ -139109,11 +139794,13 @@ static void exprCommute(Parse *pParse, Expr *pExpr){
/* Both X and Y have COLLATE operators. Make sure X is always
** used by clearing the EP_Collate flag from Y. */
pExpr->pRight->flags &= ~EP_Collate;
+ wtFlags |= TERM_NOPARTIDX;
}else if( sqlite3ExprCollSeq(pParse, pExpr->pLeft)!=0 ){
/* Neither X nor Y have COLLATE operators, but X has a non-default
** collating sequence. So add the EP_Collate marker on X to cause
** it to be searched first. */
pExpr->pLeft->flags |= EP_Collate;
+ wtFlags |= TERM_NOPARTIDX;
}
}
SWAP(Expr*,pExpr->pRight,pExpr->pLeft);
@@ -139125,6 +139812,7 @@ static void exprCommute(Parse *pParse, Expr *pExpr){
assert( pExpr->op>=TK_GT && pExpr->op<=TK_GE );
pExpr->op = ((pExpr->op-TK_GT)^2)+TK_GT;
}
+ return wtFlags;
}
/*
@@ -139256,6 +139944,7 @@ static int isLikeOrGlob(
** 2019-05-02 https://sqlite.org/src/info/b043a54c3de54b28
** 2019-06-10 https://sqlite.org/src/info/fd76310a5e843e07
** 2019-06-14 https://sqlite.org/src/info/ce8717f0885af975
+ ** 2019-09-03 https://sqlite.org/src/info/0f0428096f17252a
*/
if( pLeft->op!=TK_COLUMN
|| sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT
@@ -139265,9 +139954,13 @@ static int isLikeOrGlob(
double rDummy;
isNum = sqlite3AtoF(zNew, &rDummy, iTo, SQLITE_UTF8);
if( isNum<=0 ){
- zNew[iTo-1]++;
- isNum = sqlite3AtoF(zNew, &rDummy, iTo, SQLITE_UTF8);
- zNew[iTo-1]--;
+ if( iTo==1 && zNew[0]=='-' ){
+ isNum = +1;
+ }else{
+ zNew[iTo-1]++;
+ isNum = sqlite3AtoF(zNew, &rDummy, iTo, SQLITE_UTF8);
+ zNew[iTo-1]--;
+ }
}
if( isNum>0 ){
sqlite3ExprDelete(db, pPrefix);
@@ -140121,7 +140814,7 @@ static void exprAnalyze(
pDup = pExpr;
pNew = pTerm;
}
- exprCommute(pParse, pDup);
+ pNew->wtFlags |= exprCommute(pParse, pDup);
pNew->leftCursor = aiCurCol[0];
pNew->u.leftColumn = aiCurCol[1];
testcase( (prereqLeft | extraRight) != prereqLeft );
@@ -140362,8 +141055,8 @@ static void exprAnalyze(
}
}
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
- /* When sqlite_stat3 histogram data is available an operator of the
+#ifdef SQLITE_ENABLE_STAT4
+ /* When sqlite_stat4 histogram data is available an operator of the
** form "x IS NOT NULL" can sometimes be evaluated more efficiently
** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a
** virtual term of that form.
@@ -140374,7 +141067,7 @@ static void exprAnalyze(
&& pExpr->pLeft->op==TK_COLUMN
&& pExpr->pLeft->iColumn>=0
&& !ExprHasProperty(pExpr, EP_FromJoin)
- && OptimizationEnabled(db, SQLITE_Stat34)
+ && OptimizationEnabled(db, SQLITE_Stat4)
){
Expr *pNewExpr;
Expr *pLeft = pExpr->pLeft;
@@ -140399,7 +141092,7 @@ static void exprAnalyze(
pNewTerm->prereqAll = pTerm->prereqAll;
}
}
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
/* Prevent ON clause terms of a LEFT JOIN from being used to drive
** an index for tables to the left of the join.
@@ -140432,7 +141125,7 @@ static void exprAnalyze(
** all terms of the WHERE clause.
*/
SQLITE_PRIVATE void sqlite3WhereSplit(WhereClause *pWC, Expr *pExpr, u8 op){
- Expr *pE2 = sqlite3ExprSkipCollate(pExpr);
+ Expr *pE2 = sqlite3ExprSkipCollateAndLikely(pExpr);
pWC->op = op;
if( pE2==0 ) return;
if( pE2->op!=op ){
@@ -140847,7 +141540,8 @@ static WhereTerm *whereScanNext(WhereScan *pScan){
){
if( (pTerm->eOperator & WO_EQUIV)!=0
&& pScan->nEquiv<ArraySize(pScan->aiCur)
- && (pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight))->op==TK_COLUMN
+ && (pX = sqlite3ExprSkipCollateAndLikely(pTerm->pExpr->pRight))->op
+ ==TK_COLUMN
){
int j;
for(j=0; j<pScan->nEquiv; j++){
@@ -141043,7 +141737,7 @@ static int findIndexCol(
const char *zColl = pIdx->azColl[iCol];
for(i=0; i<pList->nExpr; i++){
- Expr *p = sqlite3ExprSkipCollate(pList->a[i].pExpr);
+ Expr *p = sqlite3ExprSkipCollateAndLikely(pList->a[i].pExpr);
if( p->op==TK_COLUMN
&& p->iColumn==pIdx->aiColumn[iCol]
&& p->iTable==iBase
@@ -141107,7 +141801,7 @@ static int isDistinctRedundant(
** current SELECT is a correlated sub-query.
*/
for(i=0; i<pDistinct->nExpr; i++){
- Expr *p = sqlite3ExprSkipCollate(pDistinct->a[i].pExpr);
+ Expr *p = sqlite3ExprSkipCollateAndLikely(pDistinct->a[i].pExpr);
if( p->op==TK_COLUMN && p->iTable==iBase && p->iColumn<0 ) return 1;
}
@@ -141527,6 +142221,7 @@ static sqlite3_index_info *allocateIndexInfo(
for(i=0; i<n; i++){
Expr *pExpr = pOrderBy->a[i].pExpr;
if( pExpr->op!=TK_COLUMN || pExpr->iTable!=pSrc->iCursor ) break;
+ if( pOrderBy->a[i].sortFlags & KEYINFO_ORDER_BIGNULL ) break;
}
if( i==n){
nOrderBy = n;
@@ -141625,7 +142320,7 @@ static sqlite3_index_info *allocateIndexInfo(
for(i=0; i<nOrderBy; i++){
Expr *pExpr = pOrderBy->a[i].pExpr;
pIdxOrderBy[i].iColumn = pExpr->iColumn;
- pIdxOrderBy[i].desc = pOrderBy->a[i].sortOrder;
+ pIdxOrderBy[i].desc = pOrderBy->a[i].sortFlags & KEYINFO_ORDER_DESC;
}
*pmNoOmit = mNoOmit;
@@ -141671,7 +142366,7 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
}
#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
/*
** Estimate the location of a particular key among all keys in an
** index. Store the results in aStat as follows:
@@ -141864,7 +142559,7 @@ static int whereKeyStats(
pRec->nField = nField;
return i;
}
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
/*
** If it is not NULL, pTerm is a term that provides an upper or lower
@@ -141890,7 +142585,7 @@ static LogEst whereRangeAdjust(WhereTerm *pTerm, LogEst nNew){
}
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
/*
** Return the affinity for a single column of an index.
*/
@@ -141899,12 +142594,13 @@ SQLITE_PRIVATE char sqlite3IndexColumnAffinity(sqlite3 *db, Index *pIdx, int iCo
if( !pIdx->zColAff ){
if( sqlite3IndexAffinityStr(db, pIdx)==0 ) return SQLITE_AFF_BLOB;
}
+ assert( pIdx->zColAff[iCol]!=0 );
return pIdx->zColAff[iCol];
}
#endif
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
/*
** This function is called to estimate the number of rows visited by a
** range-scan on a skip-scan index. For example:
@@ -142010,7 +142706,7 @@ static int whereRangeSkipScanEst(
return rc;
}
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
/*
** This function is used to estimate the number of rows that will be visited
@@ -142063,12 +142759,12 @@ static int whereRangeScanEst(
int nOut = pLoop->nOut;
LogEst nNew;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
Index *p = pLoop->u.btree.pIndex;
int nEq = pLoop->u.btree.nEq;
- if( p->nSample>0 && nEq<p->nSampleCol
- && OptimizationEnabled(pParse->db, SQLITE_Stat34)
+ if( p->nSample>0 && ALWAYS(nEq<p->nSampleCol)
+ && OptimizationEnabled(pParse->db, SQLITE_Stat4)
){
if( nEq==pBuilder->nRecValid ){
UnpackedRecord *pRec = pBuilder->pRec;
@@ -142166,7 +142862,7 @@ static int whereRangeScanEst(
/* TUNING: If both iUpper and iLower are derived from the same
** sample, then assume they are 4x more selective. This brings
** the estimated selectivity more in line with what it would be
- ** if estimated without the use of STAT3/4 tables. */
+ ** if estimated without the use of STAT4 tables. */
if( iLwrIdx==iUprIdx ) nNew -= 20; assert( 20==sqlite3LogEst(4) );
}else{
nNew = 10; assert( 10==sqlite3LogEst(2) );
@@ -142215,12 +142911,12 @@ static int whereRangeScanEst(
return rc;
}
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
/*
** Estimate the number of rows that will be returned based on
** an equality constraint x=VALUE and where that VALUE occurs in
** the histogram data. This only works when x is the left-most
-** column of an index and sqlite_stat3 histogram data is available
+** column of an index and sqlite_stat4 histogram data is available
** for that index. When pExpr==NULL that means the constraint is
** "x IS NULL" instead of "x=VALUE".
**
@@ -142278,9 +142974,9 @@ static int whereEqualScanEst(
return rc;
}
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
/*
** Estimate the number of rows that will be returned based on
** an IN constraint where the right-hand side of the IN operator
@@ -142327,7 +143023,7 @@ static int whereInScanEst(
assert( pBuilder->nRecValid==nRecValid );
return rc;
}
-#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#endif /* SQLITE_ENABLE_STAT4 */
#ifdef WHERETRACE_ENABLED
@@ -142859,11 +143555,12 @@ static void whereLoopOutputAdjust(
){
WhereTerm *pTerm, *pX;
Bitmask notAllowed = ~(pLoop->prereq|pLoop->maskSelf);
- int i, j, k;
+ int i, j;
LogEst iReduce = 0; /* pLoop->nOut should not exceed nRow-iReduce */
assert( (pLoop->wsFlags & WHERE_AUTO_INDEX)==0 );
for(i=pWC->nTerm, pTerm=pWC->a; i>0; i--, pTerm++){
+ assert( pTerm!=0 );
if( (pTerm->wtFlags & TERM_VIRTUAL)!=0 ) break;
if( (pTerm->prereqAll & pLoop->maskSelf)==0 ) continue;
if( (pTerm->prereqAll & notAllowed)!=0 ) continue;
@@ -142884,6 +143581,7 @@ static void whereLoopOutputAdjust(
pLoop->nOut--;
if( pTerm->eOperator&(WO_EQ|WO_IS) ){
Expr *pRight = pTerm->pExpr->pRight;
+ int k = 0;
testcase( pTerm->pExpr->op==TK_IS );
if( sqlite3ExprIsInteger(pRight, &k) && k>=(-1) && k<=1 ){
k = 10;
@@ -143047,7 +143745,7 @@ static int whereLoopAddBtreeIndex(
LogEst rCostIdx;
LogEst nOutUnadjusted; /* nOut before IN() and WHERE adjustments */
int nIn = 0;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
int nRecValid = pBuilder->nRecValid;
#endif
if( (eOp==WO_ISNULL || (pTerm->wtFlags&TERM_VNULL)!=0)
@@ -143108,8 +143806,6 @@ static int whereLoopAddBtreeIndex(
}else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){
/* "x IN (value, value, ...)" */
nIn = sqlite3LogEst(pExpr->x.pList->nExpr);
- assert( nIn>0 ); /* RHS always has 2 or more terms... The parser
- ** changes "x IN (?)" into "x=?". */
}
if( pProbe->hasStat1 ){
LogEst M, logK, safetyMargin;
@@ -143205,7 +143901,7 @@ static int whereLoopAddBtreeIndex(
** the value of pNew->nOut to account for pTerm (but not nIn/nInMul). */
assert( pNew->nOut==saved_nOut );
if( pNew->wsFlags & WHERE_COLUMN_RANGE ){
- /* Adjust nOut using stat3/stat4 data. Or, if there is no stat3/stat4
+ /* Adjust nOut using stat4 data. Or, if there is no stat4
** data, using some other estimate. */
whereRangeScanEst(pParse, pBuilder, pBtm, pTop, pNew);
}else{
@@ -143219,13 +143915,13 @@ static int whereLoopAddBtreeIndex(
pNew->nOut += pTerm->truthProb;
pNew->nOut -= nIn;
}else{
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
tRowcnt nOut = 0;
if( nInMul==0
&& pProbe->nSample
&& pNew->u.btree.nEq<=pProbe->nSampleCol
&& ((eOp & WO_IN)==0 || !ExprHasProperty(pTerm->pExpr, EP_xIsSelect))
- && OptimizationEnabled(db, SQLITE_Stat34)
+ && OptimizationEnabled(db, SQLITE_Stat4)
){
Expr *pExpr = pTerm->pExpr;
if( (eOp & (WO_EQ|WO_ISNULL|WO_IS))!=0 ){
@@ -143288,7 +143984,7 @@ static int whereLoopAddBtreeIndex(
whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nInMul+nIn);
}
pNew->nOut = saved_nOut;
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
pBuilder->nRecValid = nRecValid;
#endif
}
@@ -143361,7 +144057,7 @@ static int indexMightHelpWithOrderBy(
if( pIndex->bUnordered ) return 0;
if( (pOB = pBuilder->pWInfo->pOrderBy)==0 ) return 0;
for(ii=0; ii<pOB->nExpr; ii++){
- Expr *pExpr = sqlite3ExprSkipCollate(pOB->a[ii].pExpr);
+ Expr *pExpr = sqlite3ExprSkipCollateAndLikely(pOB->a[ii].pExpr);
if( pExpr->op==TK_COLUMN && pExpr->iTable==iCursor ){
if( pExpr->iColumn<0 ) return 1;
for(jj=0; jj<pIndex->nKeyCol; jj++){
@@ -143392,7 +144088,9 @@ static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){
}
if( pParse->db->flags & SQLITE_EnableQPSG ) pParse = 0;
for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
- Expr *pExpr = pTerm->pExpr;
+ Expr *pExpr;
+ if( pTerm->wtFlags & TERM_NOPARTIDX ) continue;
+ pExpr = pTerm->pExpr;
if( (!ExprHasProperty(pExpr, EP_FromJoin) || pExpr->iRightJoinTable==iTab)
&& sqlite3ExprImpliesExpr(pParse, pExpr, pWhere, iTab)
){
@@ -143661,7 +144359,7 @@ static int whereLoopAddBtree(
** plan */
pTab->tabFlags |= TF_StatsUsed;
}
-#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+#ifdef SQLITE_ENABLE_STAT4
sqlite3Stat4ProbeFree(pBuilder->pRec);
pBuilder->nRecValid = 0;
pBuilder->pRec = 0;
@@ -144289,8 +144987,8 @@ static i8 wherePathSatisfiesOrderBy(
if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){
if( pLoop->u.vtab.isOrdered ) obSat = obDone;
break;
- }else{
- pLoop->u.btree.nIdxCol = 0;
+ }else if( wctrlFlags & WHERE_DISTINCTBY ){
+ pLoop->u.btree.nDistinctCol = 0;
}
iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor;
@@ -144301,7 +144999,7 @@ static i8 wherePathSatisfiesOrderBy(
*/
for(i=0; i<nOrderBy; i++){
if( MASKBIT(i) & obSat ) continue;
- pOBExpr = sqlite3ExprSkipCollate(pOrderBy->a[i].pExpr);
+ pOBExpr = sqlite3ExprSkipCollateAndLikely(pOrderBy->a[i].pExpr);
if( pOBExpr->op!=TK_COLUMN ) continue;
if( pOBExpr->iTable!=iCur ) continue;
pTerm = sqlite3WhereFindTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn,
@@ -144338,7 +145036,8 @@ static i8 wherePathSatisfiesOrderBy(
assert( nColumn==nKeyCol+1 || !HasRowid(pIndex->pTable) );
assert( pIndex->aiColumn[nColumn-1]==XN_ROWID
|| !HasRowid(pIndex->pTable));
- isOrderDistinct = IsUniqueIndex(pIndex);
+ isOrderDistinct = IsUniqueIndex(pIndex)
+ && (pLoop->wsFlags & WHERE_SKIPSCAN)==0;
}
/* Loop through all columns of the index and deal with the ones
@@ -144356,15 +145055,21 @@ static i8 wherePathSatisfiesOrderBy(
u16 eOp = pLoop->aLTerm[j]->eOperator;
/* Skip over == and IS and ISNULL terms. (Also skip IN terms when
- ** doing WHERE_ORDERBY_LIMIT processing).
+ ** doing WHERE_ORDERBY_LIMIT processing). Except, IS and ISNULL
+ ** terms imply that the index is not UNIQUE NOT NULL in which case
+ ** the loop need to be marked as not order-distinct because it can
+ ** have repeated NULL rows.
**
** If the current term is a column of an ((?,?) IN (SELECT...))
** expression for which the SELECT returns more than one column,
** check that it is the only column used by this loop. Otherwise,
** if it is one of two or more, none of the columns can be
- ** considered to match an ORDER BY term. */
+ ** considered to match an ORDER BY term.
+ */
if( (eOp & eqOpMask)!=0 ){
- if( eOp & WO_ISNULL ){
+ if( eOp & (WO_ISNULL|WO_IS) ){
+ testcase( eOp & WO_ISNULL );
+ testcase( eOp & WO_IS );
testcase( isOrderDistinct );
isOrderDistinct = 0;
}
@@ -144390,7 +145095,7 @@ static i8 wherePathSatisfiesOrderBy(
*/
if( pIndex ){
iColumn = pIndex->aiColumn[j];
- revIdx = pIndex->aSortOrder[j];
+ revIdx = pIndex->aSortOrder[j] & KEYINFO_ORDER_DESC;
if( iColumn==pIndex->pTable->iPKey ) iColumn = XN_ROWID;
}else{
iColumn = XN_ROWID;
@@ -144414,7 +145119,7 @@ static i8 wherePathSatisfiesOrderBy(
isMatch = 0;
for(i=0; bOnce && i<nOrderBy; i++){
if( MASKBIT(i) & obSat ) continue;
- pOBExpr = sqlite3ExprSkipCollate(pOrderBy->a[i].pExpr);
+ pOBExpr = sqlite3ExprSkipCollateAndLikely(pOrderBy->a[i].pExpr);
testcase( wctrlFlags & WHERE_GROUPBY );
testcase( wctrlFlags & WHERE_DISTINCTBY );
if( (wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0 ) bOnce = 0;
@@ -144432,7 +145137,9 @@ static i8 wherePathSatisfiesOrderBy(
pColl = sqlite3ExprNNCollSeq(pWInfo->pParse, pOrderBy->a[i].pExpr);
if( sqlite3StrICmp(pColl->zName, pIndex->azColl[j])!=0 ) continue;
}
- pLoop->u.btree.nIdxCol = j+1;
+ if( wctrlFlags & WHERE_DISTINCTBY ){
+ pLoop->u.btree.nDistinctCol = j+1;
+ }
isMatch = 1;
break;
}
@@ -144440,13 +145147,22 @@ static i8 wherePathSatisfiesOrderBy(
/* Make sure the sort order is compatible in an ORDER BY clause.
** Sort order is irrelevant for a GROUP BY clause. */
if( revSet ){
- if( (rev ^ revIdx)!=pOrderBy->a[i].sortOrder ) isMatch = 0;
+ if( (rev ^ revIdx)!=(pOrderBy->a[i].sortFlags&KEYINFO_ORDER_DESC) ){
+ isMatch = 0;
+ }
}else{
- rev = revIdx ^ pOrderBy->a[i].sortOrder;
+ rev = revIdx ^ (pOrderBy->a[i].sortFlags & KEYINFO_ORDER_DESC);
if( rev ) *pRevMask |= MASKBIT(iLoop);
revSet = 1;
}
}
+ if( isMatch && (pOrderBy->a[i].sortFlags & KEYINFO_ORDER_BIGNULL) ){
+ if( j==pLoop->u.btree.nEq ){
+ pLoop->wsFlags |= WHERE_BIGNULL_SORT;
+ }else{
+ isMatch = 0;
+ }
+ }
if( isMatch ){
if( iColumn==XN_ROWID ){
testcase( distinctColumns==0 );
@@ -145360,6 +146076,16 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
sqlite3DebugPrintf(", limit: %d", iAuxArg);
}
sqlite3DebugPrintf(")\n");
+ if( sqlite3WhereTrace & 0x100 ){
+ Select sSelect;
+ memset(&sSelect, 0, sizeof(sSelect));
+ sSelect.selFlags = SF_WhereBegin;
+ sSelect.pSrc = pTabList;
+ sSelect.pWhere = pWhere;
+ sSelect.pOrderBy = pOrderBy;
+ sSelect.pEList = pResultSet;
+ sqlite3TreeViewSelect(0, &sSelect, 0);
+ }
}
if( sqlite3WhereTrace & 0x100 ){ /* Display all terms of the WHERE clause */
sqlite3WhereClausePrint(sWLB.pWC);
@@ -145636,6 +146362,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
sqlite3VdbeSetP4KeyInfo(pParse, pIx);
if( (pLoop->wsFlags & WHERE_CONSTRAINT)!=0
&& (pLoop->wsFlags & (WHERE_COLUMN_RANGE|WHERE_SKIPSCAN))==0
+ && (pLoop->wsFlags & WHERE_BIGNULL_SORT)==0
&& (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)==0
&& pWInfo->eDistinct!=WHERE_DISTINCT_ORDERED
){
@@ -145753,7 +146480,7 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
&& i==pWInfo->nLevel-1 /* Ticket [ef9318757b152e3] 2017-10-21 */
&& (pLoop->wsFlags & WHERE_INDEXED)!=0
&& (pIdx = pLoop->u.btree.pIndex)->hasStat1
- && (n = pLoop->u.btree.nIdxCol)>0
+ && (n = pLoop->u.btree.nDistinctCol)>0
&& pIdx->aiRowLogEst[n]>=36
){
int r1 = pParse->nMem+1;
@@ -145777,6 +146504,11 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
VdbeCoverageIf(v, pLevel->op==OP_Next);
VdbeCoverageIf(v, pLevel->op==OP_Prev);
VdbeCoverageIf(v, pLevel->op==OP_VNext);
+ if( pLevel->regBignull ){
+ sqlite3VdbeResolveLabel(v, pLevel->addrBignull);
+ sqlite3VdbeAddOp2(v, OP_DecrJumpZero, pLevel->regBignull, pLevel->p2-1);
+ VdbeCoverage(v);
+ }
#ifndef SQLITE_DISABLE_SKIPAHEAD_DISTINCT
if( addrSeek ) sqlite3VdbeJumpHere(v, addrSeek);
#endif
@@ -146716,6 +147448,8 @@ struct WindowRewrite {
static int selectWindowRewriteExprCb(Walker *pWalker, Expr *pExpr){
struct WindowRewrite *p = pWalker->u.pRewrite;
Parse *pParse = pWalker->pParse;
+ assert( p!=0 );
+ assert( p->pWin!=0 );
/* If this function is being called from within a scalar sub-select
** that used by the SELECT statement being processed, only process
@@ -146815,6 +147549,7 @@ static void selectWindowRewriteEList(
Walker sWalker;
WindowRewrite sRewrite;
+ assert( pWin!=0 );
memset(&sWalker, 0, sizeof(Walker));
memset(&sRewrite, 0, sizeof(WindowRewrite));
@@ -146853,7 +147588,7 @@ static ExprList *exprListAppendList(
pDup->flags &= ~(EP_IntValue|EP_IsTrue|EP_IsFalse);
}
pList = sqlite3ExprListAppend(pParse, pList, pDup);
- if( pList ) pList->a[nInit+i].sortOrder = pAppend->a[i].sortOrder;
+ if( pList ) pList->a[nInit+i].sortFlags = pAppend->a[i].sortFlags;
}
}
return pList;
@@ -146899,11 +147634,14 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){
** redundant, remove the ORDER BY from the parent SELECT. */
pSort = sqlite3ExprListDup(db, pMWin->pPartition, 0);
pSort = exprListAppendList(pParse, pSort, pMWin->pOrderBy, 1);
- if( pSort && p->pOrderBy ){
+ if( pSort && p->pOrderBy && p->pOrderBy->nExpr<=pSort->nExpr ){
+ int nSave = pSort->nExpr;
+ pSort->nExpr = p->pOrderBy->nExpr;
if( sqlite3ExprListCompare(pSort, p->pOrderBy, -1)==0 ){
sqlite3ExprListDelete(db, p->pOrderBy);
p->pOrderBy = 0;
}
+ pSort->nExpr = nSave;
}
/* Assign a cursor number for the ephemeral table used to buffer rows.
@@ -146927,8 +147665,15 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){
** window function - one for the accumulator, another for interim
** results. */
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
- pWin->iArgCol = (pSublist ? pSublist->nExpr : 0);
- pSublist = exprListAppendList(pParse, pSublist, pWin->pOwner->x.pList, 0);
+ ExprList *pArgs = pWin->pOwner->x.pList;
+ if( pWin->pFunc->funcFlags & SQLITE_FUNC_SUBTYPE ){
+ selectWindowRewriteEList(pParse, pMWin, pSrc, pArgs, pTab, &pSublist);
+ pWin->iArgCol = (pSublist ? pSublist->nExpr : 0);
+ pWin->bExprArgs = 1;
+ }else{
+ pWin->iArgCol = (pSublist ? pSublist->nExpr : 0);
+ pSublist = exprListAppendList(pParse, pSublist, pArgs, 0);
+ }
if( pWin->pFilter ){
Expr *pFilter = sqlite3ExprDup(db, pWin->pFilter, 0);
pSublist = sqlite3ExprListAppend(pParse, pSublist, pFilter);
@@ -146946,7 +147691,7 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){
*/
if( pSublist==0 ){
pSublist = sqlite3ExprListAppend(pParse, 0,
- sqlite3ExprAlloc(db, TK_INTEGER, &sqlite3IntTokens[0], 0)
+ sqlite3Expr(db, TK_INTEGER, "0")
);
}
@@ -146959,7 +147704,7 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){
p->pSrc->a[0].pSelect = pSub;
sqlite3SrcListAssignCursors(pParse, p->pSrc);
pSub->selFlags |= SF_Expanded;
- pTab2 = sqlite3ResultSetOfSelect(pParse, pSub);
+ pTab2 = sqlite3ResultSetOfSelect(pParse, pSub, SQLITE_AFF_NONE);
if( pTab2==0 ){
rc = SQLITE_NOMEM;
}else{
@@ -146983,10 +147728,23 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){
}
/*
+** Unlink the Window object from the Select to which it is attached,
+** if it is attached.
+*/
+SQLITE_PRIVATE void sqlite3WindowUnlinkFromSelect(Window *p){
+ if( p->ppThis ){
+ *p->ppThis = p->pNextWin;
+ if( p->pNextWin ) p->pNextWin->ppThis = p->ppThis;
+ p->ppThis = 0;
+ }
+}
+
+/*
** Free the Window object passed as the second argument.
*/
SQLITE_PRIVATE void sqlite3WindowDelete(sqlite3 *db, Window *p){
if( p ){
+ sqlite3WindowUnlinkFromSelect(p);
sqlite3ExprDelete(db, p->pFilter);
sqlite3ExprListDelete(db, p->pPartition);
sqlite3ExprListDelete(db, p->pOrderBy);
@@ -147164,17 +147922,14 @@ SQLITE_PRIVATE void sqlite3WindowChain(Parse *pParse, Window *pWin, Window *pLis
SQLITE_PRIVATE void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){
if( p ){
assert( p->op==TK_FUNCTION );
- /* This routine is only called for the parser. If pWin was not
- ** allocated due to an OOM, then the parser would fail before ever
- ** invoking this routine */
- if( ALWAYS(pWin) ){
- p->y.pWin = pWin;
- ExprSetProperty(p, EP_WinFunc);
- pWin->pOwner = p;
- if( p->flags & EP_Distinct ){
- sqlite3ErrorMsg(pParse,
- "DISTINCT is not supported for window functions");
- }
+ assert( pWin );
+ p->y.pWin = pWin;
+ ExprSetProperty(p, EP_WinFunc);
+ pWin->pOwner = p;
+ if( (p->flags & EP_Distinct) && pWin->eFrmType!=TK_FILTER ){
+ sqlite3ErrorMsg(pParse,
+ "DISTINCT is not supported for window functions"
+ );
}
}else{
sqlite3WindowDelete(pParse->db, pWin);
@@ -147182,10 +147937,29 @@ SQLITE_PRIVATE void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){
}
/*
+** Possibly link window pWin into the list at pSel->pWin (window functions
+** to be processed as part of SELECT statement pSel). The window is linked
+** in if either (a) there are no other windows already linked to this
+** SELECT, or (b) the windows already linked use a compatible window frame.
+*/
+SQLITE_PRIVATE void sqlite3WindowLink(Select *pSel, Window *pWin){
+ if( 0==pSel->pWin
+ || 0==sqlite3WindowCompare(0, pSel->pWin, pWin, 0)
+ ){
+ pWin->pNextWin = pSel->pWin;
+ if( pSel->pWin ){
+ pSel->pWin->ppThis = &pWin->pNextWin;
+ }
+ pSel->pWin = pWin;
+ pWin->ppThis = &pSel->pWin;
+ }
+}
+
+/*
** Return 0 if the two window objects are identical, or non-zero otherwise.
** Identical window objects can be processed in a single scan.
*/
-SQLITE_PRIVATE int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2){
+SQLITE_PRIVATE int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2, int bFilter){
if( p1->eFrmType!=p2->eFrmType ) return 1;
if( p1->eStart!=p2->eStart ) return 1;
if( p1->eEnd!=p2->eEnd ) return 1;
@@ -147194,6 +147968,9 @@ SQLITE_PRIVATE int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2){
if( sqlite3ExprCompare(pParse, p1->pEnd, p2->pEnd, -1) ) return 1;
if( sqlite3ExprListCompare(p1->pPartition, p2->pPartition, -1) ) return 1;
if( sqlite3ExprListCompare(p1->pOrderBy, p2->pOrderBy, -1) ) return 1;
+ if( bFilter ){
+ if( sqlite3ExprCompare(pParse, p1->pFilter, p2->pFilter, -1) ) return 1;
+ }
return 0;
}
@@ -147245,8 +148022,8 @@ SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse *pParse, Window *pMWin){
pWin->regApp = pParse->nMem+1;
pParse->nMem += 3;
if( pKeyInfo && pWin->pFunc->zName[1]=='i' ){
- assert( pKeyInfo->aSortOrder[0]==0 );
- pKeyInfo->aSortOrder[0] = 1;
+ assert( pKeyInfo->aSortFlags[0]==0 );
+ pKeyInfo->aSortFlags[0] = KEYINFO_ORDER_DESC;
}
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pWin->csrApp, 2);
sqlite3VdbeAppendP4(v, pKeyInfo, P4_KEYINFO);
@@ -147331,6 +148108,108 @@ static int windowArgCount(Window *pWin){
return (pList ? pList->nExpr : 0);
}
+typedef struct WindowCodeArg WindowCodeArg;
+typedef struct WindowCsrAndReg WindowCsrAndReg;
+
+/*
+** See comments above struct WindowCodeArg.
+*/
+struct WindowCsrAndReg {
+ int csr; /* Cursor number */
+ int reg; /* First in array of peer values */
+};
+
+/*
+** A single instance of this structure is allocated on the stack by
+** sqlite3WindowCodeStep() and a pointer to it passed to the various helper
+** routines. This is to reduce the number of arguments required by each
+** helper function.
+**
+** regArg:
+** Each window function requires an accumulator register (just as an
+** ordinary aggregate function does). This variable is set to the first
+** in an array of accumulator registers - one for each window function
+** in the WindowCodeArg.pMWin list.
+**
+** eDelete:
+** The window functions implementation sometimes caches the input rows
+** that it processes in a temporary table. If it is not zero, this
+** variable indicates when rows may be removed from the temp table (in
+** order to reduce memory requirements - it would always be safe just
+** to leave them there). Possible values for eDelete are:
+**
+** WINDOW_RETURN_ROW:
+** An input row can be discarded after it is returned to the caller.
+**
+** WINDOW_AGGINVERSE:
+** An input row can be discarded after the window functions xInverse()
+** callbacks have been invoked in it.
+**
+** WINDOW_AGGSTEP:
+** An input row can be discarded after the window functions xStep()
+** callbacks have been invoked in it.
+**
+** start,current,end
+** Consider a window-frame similar to the following:
+**
+** (ORDER BY a, b GROUPS BETWEEN 2 PRECEDING AND 2 FOLLOWING)
+**
+** The windows functions implmentation caches the input rows in a temp
+** table, sorted by "a, b" (it actually populates the cache lazily, and
+** aggressively removes rows once they are no longer required, but that's
+** a mere detail). It keeps three cursors open on the temp table. One
+** (current) that points to the next row to return to the query engine
+** once its window function values have been calculated. Another (end)
+** points to the next row to call the xStep() method of each window function
+** on (so that it is 2 groups ahead of current). And a third (start) that
+** points to the next row to call the xInverse() method of each window
+** function on.
+**
+** Each cursor (start, current and end) consists of a VDBE cursor
+** (WindowCsrAndReg.csr) and an array of registers (starting at
+** WindowCodeArg.reg) that always contains a copy of the peer values
+** read from the corresponding cursor.
+**
+** Depending on the window-frame in question, all three cursors may not
+** be required. In this case both WindowCodeArg.csr and reg are set to
+** 0.
+*/
+struct WindowCodeArg {
+ Parse *pParse; /* Parse context */
+ Window *pMWin; /* First in list of functions being processed */
+ Vdbe *pVdbe; /* VDBE object */
+ int addrGosub; /* OP_Gosub to this address to return one row */
+ int regGosub; /* Register used with OP_Gosub(addrGosub) */
+ int regArg; /* First in array of accumulator registers */
+ int eDelete; /* See above */
+
+ WindowCsrAndReg start;
+ WindowCsrAndReg current;
+ WindowCsrAndReg end;
+};
+
+/*
+** Generate VM code to read the window frames peer values from cursor csr into
+** an array of registers starting at reg.
+*/
+static void windowReadPeerValues(
+ WindowCodeArg *p,
+ int csr,
+ int reg
+){
+ Window *pMWin = p->pMWin;
+ ExprList *pOrderBy = pMWin->pOrderBy;
+ if( pOrderBy ){
+ Vdbe *v = sqlite3GetVdbe(p->pParse);
+ ExprList *pPart = pMWin->pPartition;
+ int iColOff = pMWin->nBufferCol + (pPart ? pPart->nExpr : 0);
+ int i;
+ for(i=0; i<pOrderBy->nExpr; i++){
+ sqlite3VdbeAddOp3(v, OP_Column, csr, iColOff+i, reg+i);
+ }
+ }
+}
+
/*
** Generate VM code to invoke either xStep() (if bInverse is 0) or
** xInverse (if bInverse is non-zero) for each window function in the
@@ -147351,20 +148230,27 @@ static int windowArgCount(Window *pWin){
** number of rows in the current partition.
*/
static void windowAggStep(
- Parse *pParse,
+ WindowCodeArg *p,
Window *pMWin, /* Linked list of window functions */
int csr, /* Read arguments from this cursor */
int bInverse, /* True to invoke xInverse instead of xStep */
int reg /* Array of registers */
){
+ Parse *pParse = p->pParse;
Vdbe *v = sqlite3GetVdbe(pParse);
Window *pWin;
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
FuncDef *pFunc = pWin->pFunc;
int regArg;
- int nArg = windowArgCount(pWin);
+ int nArg = pWin->bExprArgs ? 0 : windowArgCount(pWin);
int i;
+ assert( bInverse==0 || pWin->eStart!=TK_UNBOUNDED );
+
+ /* All OVER clauses in the same window function aggregate step must
+ ** be the same. */
+ assert( pWin==pMWin || sqlite3WindowCompare(pParse,pWin,pMWin,0)==0 );
+
for(i=0; i<nArg; i++){
if( i!=1 || pFunc->zName!=nth_valueName ){
sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+i, reg+i);
@@ -147402,14 +148288,30 @@ static void windowAggStep(
int addrIf = 0;
if( pWin->pFilter ){
int regTmp;
- assert( nArg==0 || nArg==pWin->pOwner->x.pList->nExpr );
- assert( nArg || pWin->pOwner->x.pList==0 );
+ assert( pWin->bExprArgs || !nArg ||nArg==pWin->pOwner->x.pList->nExpr );
+ assert( pWin->bExprArgs || nArg ||pWin->pOwner->x.pList==0 );
regTmp = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp);
addrIf = sqlite3VdbeAddOp3(v, OP_IfNot, regTmp, 0, 1);
VdbeCoverage(v);
sqlite3ReleaseTempReg(pParse, regTmp);
}
+
+ if( pWin->bExprArgs ){
+ int iStart = sqlite3VdbeCurrentAddr(v);
+ VdbeOp *pOp, *pEnd;
+
+ nArg = pWin->pOwner->x.pList->nExpr;
+ regArg = sqlite3GetTempRange(pParse, nArg);
+ sqlite3ExprCodeExprList(pParse, pWin->pOwner->x.pList, regArg, 0, 0);
+
+ pEnd = sqlite3VdbeGetOp(v, -1);
+ for(pOp=sqlite3VdbeGetOp(v, iStart); pOp<=pEnd; pOp++){
+ if( pOp->opcode==OP_Column && pOp->p1==pWin->iEphCsr ){
+ pOp->p1 = csr;
+ }
+ }
+ }
if( pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
CollSeq *pColl;
assert( nArg>0 );
@@ -147420,32 +148322,14 @@ static void windowAggStep(
bInverse, regArg, pWin->regAccum);
sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, (u8)nArg);
+ if( pWin->bExprArgs ){
+ sqlite3ReleaseTempRange(pParse, regArg, nArg);
+ }
if( addrIf ) sqlite3VdbeJumpHere(v, addrIf);
}
}
}
-typedef struct WindowCodeArg WindowCodeArg;
-typedef struct WindowCsrAndReg WindowCsrAndReg;
-struct WindowCsrAndReg {
- int csr;
- int reg;
-};
-
-struct WindowCodeArg {
- Parse *pParse;
- Window *pMWin;
- Vdbe *pVdbe;
- int regGosub;
- int addrGosub;
- int regArg;
- int eDelete;
-
- WindowCsrAndReg start;
- WindowCsrAndReg current;
- WindowCsrAndReg end;
-};
-
/*
** Values that may be passed as the second argument to windowCodeOp().
*/
@@ -147454,28 +148338,6 @@ struct WindowCodeArg {
#define WINDOW_AGGSTEP 3
/*
-** Generate VM code to read the window frames peer values from cursor csr into
-** an array of registers starting at reg.
-*/
-static void windowReadPeerValues(
- WindowCodeArg *p,
- int csr,
- int reg
-){
- Window *pMWin = p->pMWin;
- ExprList *pOrderBy = pMWin->pOrderBy;
- if( pOrderBy ){
- Vdbe *v = sqlite3GetVdbe(p->pParse);
- ExprList *pPart = pMWin->pPartition;
- int iColOff = pMWin->nBufferCol + (pPart ? pPart->nExpr : 0);
- int i;
- for(i=0; i<pOrderBy->nExpr; i++){
- sqlite3VdbeAddOp3(v, OP_Column, csr, iColOff+i, reg+i);
- }
- }
-}
-
-/*
** Generate VM code to invoke either xValue() (bFin==0) or xFinalize()
** (bFin==1) for each window function in the linked list starting at
** pMWin. Or, for built-in window-functions that do not use the standard
@@ -147535,8 +148397,12 @@ static void windowFullScan(WindowCodeArg *p){
int lblNext;
int lblBrk;
int addrNext;
- int csr = pMWin->csrApp;
+ int csr;
+
+ VdbeModuleComment((v, "windowFullScan begin"));
+ assert( pMWin!=0 );
+ csr = pMWin->csrApp;
nPeer = (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0);
lblNext = sqlite3VdbeMakeLabel(pParse);
@@ -147591,7 +148457,7 @@ static void windowFullScan(WindowCodeArg *p){
if( addrEq ) sqlite3VdbeJumpHere(v, addrEq);
}
- windowAggStep(pParse, pMWin, csr, 0, p->regArg);
+ windowAggStep(p, pMWin, csr, 0, p->regArg);
sqlite3VdbeResolveLabel(v, lblNext);
sqlite3VdbeAddOp2(v, OP_Next, csr, addrNext);
@@ -147606,6 +148472,7 @@ static void windowFullScan(WindowCodeArg *p){
}
windowAggFinal(p, 1);
+ VdbeModuleComment((v, "windowFullScan end"));
}
/*
@@ -147780,34 +148647,46 @@ static void windowIfNewPeer(
/*
** This function is called as part of generating VM programs for RANGE
** offset PRECEDING/FOLLOWING frame boundaries. Assuming "ASC" order for
-** the ORDER BY term in the window, it generates code equivalent to:
+** the ORDER BY term in the window, and that argument op is OP_Ge, it generates
+** code equivalent to:
**
** if( csr1.peerVal + regVal >= csr2.peerVal ) goto lbl;
**
-** A special type of arithmetic is used such that if csr.peerVal is not
-** a numeric type (real or integer), then the result of the addition is
-** a copy of csr1.peerVal.
+** The value of parameter op may also be OP_Gt or OP_Le. In these cases the
+** operator in the above pseudo-code is replaced with ">" or "<=", respectively.
+**
+** If the sort-order for the ORDER BY term in the window is DESC, then the
+** comparison is reversed. Instead of adding regVal to csr1.peerVal, it is
+** subtracted. And the comparison operator is inverted to - ">=" becomes "<=",
+** ">" becomes "<", and so on. So, with DESC sort order, if the argument op
+** is OP_Ge, the generated code is equivalent to:
+**
+** if( csr1.peerVal - regVal <= csr2.peerVal ) goto lbl;
+**
+** A special type of arithmetic is used such that if csr1.peerVal is not
+** a numeric type (real or integer), then the result of the addition addition
+** or subtraction is a a copy of csr1.peerVal.
*/
static void windowCodeRangeTest(
WindowCodeArg *p,
- int op, /* OP_Ge or OP_Gt */
- int csr1,
- int regVal,
- int csr2,
- int lbl
+ int op, /* OP_Ge, OP_Gt, or OP_Le */
+ int csr1, /* Cursor number for cursor 1 */
+ int regVal, /* Register containing non-negative number */
+ int csr2, /* Cursor number for cursor 2 */
+ int lbl /* Jump destination if condition is true */
){
Parse *pParse = p->pParse;
Vdbe *v = sqlite3GetVdbe(pParse);
- int reg1 = sqlite3GetTempReg(pParse);
- int reg2 = sqlite3GetTempReg(pParse);
- int arith = OP_Add;
- int addrGe;
-
- int regString = ++pParse->nMem;
+ ExprList *pOrderBy = p->pMWin->pOrderBy; /* ORDER BY clause for window */
+ int reg1 = sqlite3GetTempReg(pParse); /* Reg. for csr1.peerVal+regVal */
+ int reg2 = sqlite3GetTempReg(pParse); /* Reg. for csr2.peerVal */
+ int regString = ++pParse->nMem; /* Reg. for constant value '' */
+ int arith = OP_Add; /* OP_Add or OP_Subtract */
+ int addrGe; /* Jump destination */
assert( op==OP_Ge || op==OP_Gt || op==OP_Le );
- assert( p->pMWin->pOrderBy && p->pMWin->pOrderBy->nExpr==1 );
- if( p->pMWin->pOrderBy->a[0].sortOrder ){
+ assert( pOrderBy && pOrderBy->nExpr==1 );
+ if( pOrderBy->a[0].sortFlags & KEYINFO_ORDER_DESC ){
switch( op ){
case OP_Ge: op = OP_Le; break;
case OP_Gt: op = OP_Lt; break;
@@ -147816,27 +148695,95 @@ static void windowCodeRangeTest(
arith = OP_Subtract;
}
+ /* Read the peer-value from each cursor into a register */
windowReadPeerValues(p, csr1, reg1);
windowReadPeerValues(p, csr2, reg2);
- /* Check if the peer value for csr1 value is a text or blob by comparing
- ** it to the smallest possible string - ''. If it is, jump over the
- ** OP_Add or OP_Subtract operation and proceed directly to the comparison. */
+ VdbeModuleComment((v, "CodeRangeTest: if( R%d %s R%d %s R%d ) goto lbl",
+ reg1, (arith==OP_Add ? "+" : "-"), regVal,
+ ((op==OP_Ge) ? ">=" : (op==OP_Le) ? "<=" : (op==OP_Gt) ? ">" : "<"), reg2
+ ));
+
+ /* Register reg1 currently contains csr1.peerVal (the peer-value from csr1).
+ ** This block adds (or subtracts for DESC) the numeric value in regVal
+ ** from it. Or, if reg1 is not numeric (it is a NULL, a text value or a blob),
+ ** then leave reg1 as it is. In pseudo-code, this is implemented as:
+ **
+ ** if( reg1>='' ) goto addrGe;
+ ** reg1 = reg1 +/- regVal
+ ** addrGe:
+ **
+ ** Since all strings and blobs are greater-than-or-equal-to an empty string,
+ ** the add/subtract is skipped for these, as required. If reg1 is a NULL,
+ ** then the arithmetic is performed, but since adding or subtracting from
+ ** NULL is always NULL anyway, this case is handled as required too. */
sqlite3VdbeAddOp4(v, OP_String8, 0, regString, 0, "", P4_STATIC);
addrGe = sqlite3VdbeAddOp3(v, OP_Ge, regString, 0, reg1);
VdbeCoverage(v);
sqlite3VdbeAddOp3(v, arith, regVal, reg1, reg1);
sqlite3VdbeJumpHere(v, addrGe);
+
+ /* If the BIGNULL flag is set for the ORDER BY, then it is required to
+ ** consider NULL values to be larger than all other values, instead of
+ ** the usual smaller. The VDBE opcodes OP_Ge and so on do not handle this
+ ** (and adding that capability causes a performance regression), so
+ ** instead if the BIGNULL flag is set then cases where either reg1 or
+ ** reg2 are NULL are handled separately in the following block. The code
+ ** generated is equivalent to:
+ **
+ ** if( reg1 IS NULL ){
+ ** if( op==OP_Ge ) goto lbl;
+ ** if( op==OP_Gt && reg2 IS NOT NULL ) goto lbl;
+ ** if( op==OP_Le && reg2 IS NULL ) goto lbl;
+ ** }else if( reg2 IS NULL ){
+ ** if( op==OP_Le ) goto lbl;
+ ** }
+ **
+ ** Additionally, if either reg1 or reg2 are NULL but the jump to lbl is
+ ** not taken, control jumps over the comparison operator coded below this
+ ** block. */
+ if( pOrderBy->a[0].sortFlags & KEYINFO_ORDER_BIGNULL ){
+ /* This block runs if reg1 contains a NULL. */
+ int addr = sqlite3VdbeAddOp1(v, OP_NotNull, reg1); VdbeCoverage(v);
+ switch( op ){
+ case OP_Ge:
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, lbl);
+ break;
+ case OP_Gt:
+ sqlite3VdbeAddOp2(v, OP_NotNull, reg2, lbl);
+ VdbeCoverage(v);
+ break;
+ case OP_Le:
+ sqlite3VdbeAddOp2(v, OP_IsNull, reg2, lbl);
+ VdbeCoverage(v);
+ break;
+ default: assert( op==OP_Lt ); /* no-op */ break;
+ }
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+3);
+
+ /* This block runs if reg1 is not NULL, but reg2 is. */
+ sqlite3VdbeJumpHere(v, addr);
+ sqlite3VdbeAddOp2(v, OP_IsNull, reg2, lbl); VdbeCoverage(v);
+ if( op==OP_Gt || op==OP_Ge ){
+ sqlite3VdbeChangeP2(v, -1, sqlite3VdbeCurrentAddr(v)+1);
+ }
+ }
+
+ /* Compare registers reg2 and reg1, taking the jump if required. Note that
+ ** control skips over this test if the BIGNULL flag is set and either
+ ** reg1 or reg2 contain a NULL value. */
sqlite3VdbeAddOp3(v, op, reg2, lbl, reg1); VdbeCoverage(v);
sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
+
assert( op==OP_Ge || op==OP_Gt || op==OP_Lt || op==OP_Le );
testcase(op==OP_Ge); VdbeCoverageIf(v, op==OP_Ge);
testcase(op==OP_Lt); VdbeCoverageIf(v, op==OP_Lt);
testcase(op==OP_Le); VdbeCoverageIf(v, op==OP_Le);
testcase(op==OP_Gt); VdbeCoverageIf(v, op==OP_Gt);
-
sqlite3ReleaseTempReg(pParse, reg1);
sqlite3ReleaseTempReg(pParse, reg2);
+
+ VdbeModuleComment((v, "CodeRangeTest: end"));
}
/*
@@ -147856,9 +148803,7 @@ static int windowCodeOp(
Window *pMWin = p->pMWin;
int ret = 0;
Vdbe *v = p->pVdbe;
- int addrIf = 0;
int addrContinue = 0;
- int addrGoto = 0;
int bPeer = (pMWin->eFrmType!=TK_ROWS);
int lblDone = sqlite3VdbeMakeLabel(pParse);
@@ -147891,7 +148836,7 @@ static int windowCodeOp(
);
}
}else{
- addrIf = sqlite3VdbeAddOp3(v, OP_IfPos, regCountdown, 0, 1);
+ sqlite3VdbeAddOp3(v, OP_IfPos, regCountdown, lblDone, 1);
VdbeCoverage(v);
}
}
@@ -147900,6 +148845,25 @@ static int windowCodeOp(
windowAggFinal(p, 0);
}
addrContinue = sqlite3VdbeCurrentAddr(v);
+
+ /* If this is a (RANGE BETWEEN a FOLLOWING AND b FOLLOWING) or
+ ** (RANGE BETWEEN b PRECEDING AND a PRECEDING) frame, ensure the
+ ** start cursor does not advance past the end cursor within the
+ ** temporary table. It otherwise might, if (a>b). */
+ if( pMWin->eStart==pMWin->eEnd && regCountdown
+ && pMWin->eFrmType==TK_RANGE && op==WINDOW_AGGINVERSE
+ ){
+ int regRowid1 = sqlite3GetTempReg(pParse);
+ int regRowid2 = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp2(v, OP_Rowid, p->start.csr, regRowid1);
+ sqlite3VdbeAddOp2(v, OP_Rowid, p->end.csr, regRowid2);
+ sqlite3VdbeAddOp3(v, OP_Ge, regRowid2, lblDone, regRowid1);
+ VdbeCoverage(v);
+ sqlite3ReleaseTempReg(pParse, regRowid1);
+ sqlite3ReleaseTempReg(pParse, regRowid2);
+ assert( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_FOLLOWING );
+ }
+
switch( op ){
case WINDOW_RETURN_ROW:
csr = p->current.csr;
@@ -147914,7 +148878,7 @@ static int windowCodeOp(
assert( pMWin->regEndRowid );
sqlite3VdbeAddOp2(v, OP_AddImm, pMWin->regStartRowid, 1);
}else{
- windowAggStep(pParse, pMWin, csr, 1, p->regArg);
+ windowAggStep(p, pMWin, csr, 1, p->regArg);
}
break;
@@ -147926,7 +148890,7 @@ static int windowCodeOp(
assert( pMWin->regEndRowid );
sqlite3VdbeAddOp2(v, OP_AddImm, pMWin->regEndRowid, 1);
}else{
- windowAggStep(pParse, pMWin, csr, 0, p->regArg);
+ windowAggStep(p, pMWin, csr, 0, p->regArg);
}
break;
}
@@ -147944,7 +148908,7 @@ static int windowCodeOp(
sqlite3VdbeAddOp2(v, OP_Next, csr, sqlite3VdbeCurrentAddr(v)+1+bPeer);
VdbeCoverage(v);
if( bPeer ){
- addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, lblDone);
}
}
@@ -147960,8 +148924,6 @@ static int windowCodeOp(
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNextRange);
}
sqlite3VdbeResolveLabel(v, lblDone);
- if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto);
- if( addrIf ) sqlite3VdbeJumpHere(v, addrIf);
return ret;
}
@@ -147977,6 +148939,7 @@ SQLITE_PRIVATE Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p){
pNew = sqlite3DbMallocZero(db, sizeof(Window));
if( pNew ){
pNew->zName = sqlite3DbStrDup(db, p->zName);
+ pNew->zBase = sqlite3DbStrDup(db, p->zBase);
pNew->pFilter = sqlite3ExprDup(db, p->pFilter, 0);
pNew->pFunc = p->pFunc;
pNew->pPartition = sqlite3ExprListDup(db, p->pPartition, 0);
@@ -147985,9 +148948,11 @@ SQLITE_PRIVATE Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p){
pNew->eEnd = p->eEnd;
pNew->eStart = p->eStart;
pNew->eExclude = p->eExclude;
+ pNew->regResult = p->regResult;
pNew->pStart = sqlite3ExprDup(db, p->pStart, 0);
pNew->pEnd = sqlite3ExprDup(db, p->pEnd, 0);
pNew->pOwner = pOwner;
+ pNew->bImplicitFrame = p->bImplicitFrame;
}
}
return pNew;
@@ -148311,7 +149276,7 @@ static int windowExprGtZero(Parse *pParse, Expr *pExpr){
** regEnd = <expr2>
** regStart = <expr1>
** }else{
-** if( (csrEnd.key + regEnd) <= csrCurrent.key ){
+** while( (csrEnd.key + regEnd) <= csrCurrent.key ){
** AGGSTEP
** }
** while( (csrStart.key + regStart) < csrCurrent.key ){
@@ -148384,8 +149349,6 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep(
int addrGosubFlush = 0; /* Address of OP_Gosub to flush: */
int addrInteger = 0; /* Address of OP_Integer */
int addrEmpty; /* Address of OP_Rewind in flush: */
- int regStart = 0; /* Value of <expr> PRECEDING */
- int regEnd = 0; /* Value of <expr> FOLLOWING */
int regNew; /* Array of registers holding new input row */
int regRecord; /* regNew array in record form */
int regRowid; /* Rowid for regRecord in eph table */
@@ -148394,6 +149357,8 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep(
int regFlushPart = 0; /* Register for "Gosub flush_partition" */
WindowCodeArg s; /* Context object for sub-routines */
int lblWhereEnd; /* Label just before sqlite3WhereEnd() code */
+ int regStart = 0; /* Value of <expr> PRECEDING */
+ int regEnd = 0; /* Value of <expr> FOLLOWING */
assert( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_CURRENT
|| pMWin->eStart==TK_FOLLOWING || pMWin->eStart==TK_UNBOUNDED
@@ -148524,14 +149489,14 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep(
if( regStart ){
sqlite3ExprCode(pParse, pMWin->pStart, regStart);
- windowCheckValue(pParse, regStart, 0 + (pMWin->eFrmType==TK_RANGE ? 3 : 0));
+ windowCheckValue(pParse, regStart, 0 + (pMWin->eFrmType==TK_RANGE?3:0));
}
if( regEnd ){
sqlite3ExprCode(pParse, pMWin->pEnd, regEnd);
- windowCheckValue(pParse, regEnd, 1 + (pMWin->eFrmType==TK_RANGE ? 3 : 0));
+ windowCheckValue(pParse, regEnd, 1 + (pMWin->eFrmType==TK_RANGE?3:0));
}
- if( pMWin->eStart==pMWin->eEnd && regStart ){
+ if( pMWin->eFrmType!=TK_RANGE && pMWin->eStart==pMWin->eEnd && regStart ){
int op = ((pMWin->eStart==TK_FOLLOWING) ? OP_Ge : OP_Le);
int addrGe = sqlite3VdbeAddOp3(v, op, regStart, 0, regEnd);
VdbeCoverageNeverNullIf(v, op==OP_Ge); /* NeverNull because bound <expr> */
@@ -148791,6 +149756,7 @@ static void disableLookaside(Parse *pParse){
** SQLITE_LIMIT_COMPOUND_SELECT.
*/
static void parserDoubleLinkSelect(Parse *pParse, Select *p){
+ assert( p!=0 );
if( p->pPrior ){
Select *pNext = 0, *pLoop;
int mxSelect, cnt = 0;
@@ -148817,7 +149783,7 @@ static void disableLookaside(Parse *pParse){
if( p ){
/* memset(p, 0, sizeof(Expr)); */
p->op = (u8)op;
- p->affinity = 0;
+ p->affExpr = 0;
p->flags = EP_Leaf;
p->iAgg = -1;
p->pLeft = p->pRight = 0;
@@ -148944,28 +149910,28 @@ static void disableLookaside(Parse *pParse){
#endif
/************* Begin control #defines *****************************************/
#define YYCODETYPE unsigned short int
-#define YYNOCODE 302
+#define YYNOCODE 307
#define YYACTIONTYPE unsigned short int
-#define YYWILDCARD 95
+#define YYWILDCARD 98
#define sqlite3ParserTOKENTYPE Token
typedef union {
int yyinit;
sqlite3ParserTOKENTYPE yy0;
- TriggerStep* yy11;
- IdList* yy76;
- ExprList* yy94;
- Upsert* yy95;
- int yy100;
- Expr* yy102;
- struct {int value; int mask;} yy199;
- u8 yy218;
- With* yy243;
- struct TrigEvent yy298;
- Window* yy379;
- struct FrameBound yy389;
- Select* yy391;
- SrcList* yy407;
- const char* yy528;
+ const char* yy8;
+ Select* yy25;
+ int yy32;
+ Expr* yy46;
+ struct FrameBound yy57;
+ u8 yy118;
+ ExprList* yy138;
+ Upsert* yy288;
+ With* yy297;
+ IdList* yy406;
+ Window* yy455;
+ struct {int value; int mask;} yy495;
+ TriggerStep* yy527;
+ struct TrigEvent yy572;
+ SrcList* yy609;
} YYMINORTYPE;
#ifndef YYSTACKDEPTH
#define YYSTACKDEPTH 100
@@ -148981,17 +149947,17 @@ typedef union {
#define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse;
#define sqlite3ParserCTX_STORE yypParser->pParse=pParse;
#define YYFALLBACK 1
-#define YYNSTATE 540
-#define YYNRULE 376
-#define YYNTOKEN 176
-#define YY_MAX_SHIFT 539
-#define YY_MIN_SHIFTREDUCE 783
-#define YY_MAX_SHIFTREDUCE 1158
-#define YY_ERROR_ACTION 1159
-#define YY_ACCEPT_ACTION 1160
-#define YY_NO_ACTION 1161
-#define YY_MIN_REDUCE 1162
-#define YY_MAX_REDUCE 1537
+#define YYNSTATE 543
+#define YYNRULE 381
+#define YYNTOKEN 179
+#define YY_MAX_SHIFT 542
+#define YY_MIN_SHIFTREDUCE 790
+#define YY_MAX_SHIFTREDUCE 1170
+#define YY_ERROR_ACTION 1171
+#define YY_ACCEPT_ACTION 1172
+#define YY_NO_ACTION 1173
+#define YY_MIN_REDUCE 1174
+#define YY_MAX_REDUCE 1554
/************* End control #defines *******************************************/
#define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])))
@@ -149058,601 +150024,573 @@ typedef union {
** yy_default[] Default action for each state.
**
*********** Begin parsing tables **********************************************/
-#define YY_ACTTAB_COUNT (2142)
+#define YY_ACTTAB_COUNT (1913)
static const YYACTIONTYPE yy_action[] = {
- /* 0 */ 112, 109, 209, 112, 109, 209, 1160, 1, 1, 539,
- /* 10 */ 2, 1164, 490, 1193, 1293, 534, 289, 1196, 134, 383,
- /* 20 */ 1485, 1428, 1164, 1229, 1208, 1242, 1195, 289, 491, 134,
- /* 30 */ 373, 915, 1229, 443, 16, 16, 1242, 70, 70, 916,
- /* 40 */ 242, 1292, 296, 119, 120, 110, 1136, 1136, 981, 984,
- /* 50 */ 974, 974, 117, 117, 118, 118, 118, 118, 264, 264,
- /* 60 */ 190, 264, 264, 264, 264, 112, 109, 209, 362, 264,
- /* 70 */ 264, 531, 376, 497, 531, 1134, 531, 1501, 239, 206,
- /* 80 */ 338, 9, 531, 242, 219, 1203, 118, 118, 118, 118,
- /* 90 */ 111, 439, 112, 109, 209, 219, 116, 116, 116, 116,
- /* 100 */ 115, 115, 114, 114, 114, 113, 414, 115, 115, 114,
- /* 110 */ 114, 114, 113, 414, 418, 12, 383, 400, 1134, 114,
- /* 120 */ 114, 114, 113, 414, 1115, 418, 1134, 1392, 116, 116,
- /* 130 */ 116, 116, 115, 115, 114, 114, 114, 113, 414, 961,
- /* 140 */ 119, 120, 110, 1136, 1136, 981, 984, 974, 974, 117,
- /* 150 */ 117, 118, 118, 118, 118, 952, 534, 414, 941, 951,
- /* 160 */ 1481, 539, 2, 1164, 1505, 534, 160, 175, 289, 1134,
- /* 170 */ 134, 434, 312, 297, 1115, 1116, 1117, 1242, 70, 70,
- /* 180 */ 1089, 338, 1089, 118, 118, 118, 118, 42, 42, 448,
- /* 190 */ 951, 951, 953, 116, 116, 116, 116, 115, 115, 114,
- /* 200 */ 114, 114, 113, 414, 1115, 311, 264, 264, 82, 441,
- /* 210 */ 264, 264, 190, 383, 284, 12, 288, 525, 407, 531,
- /* 220 */ 96, 159, 458, 531, 371, 116, 116, 116, 116, 115,
- /* 230 */ 115, 114, 114, 114, 113, 414, 219, 119, 120, 110,
- /* 240 */ 1136, 1136, 981, 984, 974, 974, 117, 117, 118, 118,
- /* 250 */ 118, 118, 511, 1477, 1115, 1116, 1117, 113, 414, 534,
- /* 260 */ 528, 528, 528, 121, 534, 1427, 418, 116, 116, 116,
- /* 270 */ 116, 115, 115, 114, 114, 114, 113, 414, 1464, 351,
- /* 280 */ 270, 42, 42, 383, 187, 1115, 70, 70, 533, 433,
- /* 290 */ 116, 116, 116, 116, 115, 115, 114, 114, 114, 113,
- /* 300 */ 414, 534, 1339, 405, 159, 411, 410, 119, 120, 110,
- /* 310 */ 1136, 1136, 981, 984, 974, 974, 117, 117, 118, 118,
- /* 320 */ 118, 118, 285, 42, 42, 349, 411, 410, 514, 479,
- /* 330 */ 1458, 79, 1084, 6, 1140, 1115, 1116, 1117, 480, 1142,
- /* 340 */ 501, 1115, 1084, 123, 238, 1084, 136, 1141, 1234, 1234,
- /* 350 */ 1143, 383, 1143, 1115, 167, 426, 80, 447, 512, 1451,
- /* 360 */ 116, 116, 116, 116, 115, 115, 114, 114, 114, 113,
- /* 370 */ 414, 1143, 1466, 1143, 350, 119, 120, 110, 1136, 1136,
- /* 380 */ 981, 984, 974, 974, 117, 117, 118, 118, 118, 118,
- /* 390 */ 402, 1115, 1116, 1117, 500, 534, 250, 267, 336, 474,
- /* 400 */ 331, 473, 236, 1115, 1116, 1117, 231, 1115, 329, 471,
- /* 410 */ 468, 467, 509, 1458, 1464, 505, 6, 70, 70, 466,
- /* 420 */ 181, 380, 379, 534, 971, 971, 982, 985, 116, 116,
- /* 430 */ 116, 116, 115, 115, 114, 114, 114, 113, 414, 1115,
- /* 440 */ 412, 412, 412, 496, 1115, 69, 69, 235, 383, 288,
- /* 450 */ 525, 273, 326, 516, 337, 458, 1084, 1115, 1116, 1117,
- /* 460 */ 1232, 1232, 492, 160, 508, 441, 1084, 1067, 1531, 1084,
- /* 470 */ 207, 1531, 119, 120, 110, 1136, 1136, 981, 984, 974,
- /* 480 */ 974, 117, 117, 118, 118, 118, 118, 881, 534, 1115,
- /* 490 */ 1116, 1117, 975, 534, 1115, 1116, 1117, 534, 421, 534,
- /* 500 */ 141, 534, 176, 356, 517, 1119, 32, 511, 482, 388,
- /* 510 */ 70, 70, 818, 288, 525, 70, 70, 441, 499, 50,
- /* 520 */ 50, 70, 70, 70, 70, 116, 116, 116, 116, 115,
- /* 530 */ 115, 114, 114, 114, 113, 414, 274, 264, 264, 1115,
- /* 540 */ 1065, 264, 264, 1115, 355, 383, 409, 961, 1439, 822,
- /* 550 */ 531, 516, 190, 419, 531, 483, 1119, 516, 337, 516,
- /* 560 */ 518, 1115, 818, 952, 382, 458, 515, 951, 481, 119,
- /* 570 */ 120, 110, 1136, 1136, 981, 984, 974, 974, 117, 117,
- /* 580 */ 118, 118, 118, 118, 1338, 278, 1045, 278, 275, 1115,
- /* 590 */ 1116, 1117, 259, 1115, 1116, 1117, 534, 5, 951, 951,
- /* 600 */ 953, 1046, 231, 3, 143, 471, 468, 467, 1391, 463,
- /* 610 */ 1115, 1115, 1116, 1117, 1452, 466, 1047, 836, 70, 70,
- /* 620 */ 480, 534, 116, 116, 116, 116, 115, 115, 114, 114,
- /* 630 */ 114, 113, 414, 95, 1115, 287, 235, 856, 902, 420,
- /* 640 */ 1115, 534, 383, 13, 13, 381, 815, 857, 472, 112,
- /* 650 */ 109, 209, 1115, 337, 413, 309, 837, 394, 1436, 534,
- /* 660 */ 1115, 1116, 1117, 54, 54, 291, 119, 120, 110, 1136,
- /* 670 */ 1136, 981, 984, 974, 974, 117, 117, 118, 118, 118,
- /* 680 */ 118, 13, 13, 1084, 1115, 1116, 1117, 901, 264, 264,
- /* 690 */ 1115, 1116, 1117, 1084, 292, 399, 1084, 800, 388, 140,
- /* 700 */ 295, 531, 1115, 1116, 1117, 403, 447, 532, 534, 870,
- /* 710 */ 870, 534, 1240, 534, 329, 534, 1185, 389, 534, 116,
- /* 720 */ 116, 116, 116, 115, 115, 114, 114, 114, 113, 414,
- /* 730 */ 13, 13, 1024, 13, 13, 13, 13, 13, 13, 383,
- /* 740 */ 13, 13, 424, 1100, 401, 264, 264, 277, 160, 184,
- /* 750 */ 1182, 185, 1533, 369, 513, 484, 432, 487, 531, 424,
- /* 760 */ 423, 1397, 941, 119, 120, 110, 1136, 1136, 981, 984,
- /* 770 */ 974, 974, 117, 117, 118, 118, 118, 118, 1397, 1399,
- /* 780 */ 425, 519, 392, 264, 264, 1029, 1029, 455, 264, 264,
- /* 790 */ 264, 264, 1004, 304, 261, 1278, 531, 900, 288, 525,
- /* 800 */ 310, 531, 493, 531, 1067, 1532, 458, 387, 1532, 311,
- /* 810 */ 429, 299, 534, 107, 264, 264, 116, 116, 116, 116,
- /* 820 */ 115, 115, 114, 114, 114, 113, 414, 531, 424, 1384,
- /* 830 */ 507, 258, 258, 1246, 55, 55, 383, 1277, 265, 265,
- /* 840 */ 962, 324, 434, 312, 531, 531, 506, 1397, 1026, 1241,
- /* 850 */ 298, 531, 1026, 445, 301, 1095, 303, 534, 368, 1156,
- /* 860 */ 119, 120, 110, 1136, 1136, 981, 984, 974, 974, 117,
- /* 870 */ 117, 118, 118, 118, 118, 1045, 534, 1065, 534, 15,
- /* 880 */ 15, 1084, 208, 1324, 453, 452, 534, 1324, 534, 449,
- /* 890 */ 1046, 1084, 494, 458, 1084, 234, 233, 232, 44, 44,
- /* 900 */ 56, 56, 319, 1095, 322, 1047, 534, 900, 57, 57,
- /* 910 */ 58, 58, 534, 116, 116, 116, 116, 115, 115, 114,
- /* 920 */ 114, 114, 113, 414, 534, 514, 522, 534, 59, 59,
- /* 930 */ 302, 1157, 534, 383, 60, 60, 1237, 946, 788, 789,
- /* 940 */ 790, 1459, 1456, 446, 6, 6, 61, 61, 1212, 45,
- /* 950 */ 45, 534, 396, 383, 46, 46, 397, 119, 120, 110,
- /* 960 */ 1136, 1136, 981, 984, 974, 974, 117, 117, 118, 118,
- /* 970 */ 118, 118, 428, 48, 48, 534, 392, 119, 120, 110,
- /* 980 */ 1136, 1136, 981, 984, 974, 974, 117, 117, 118, 118,
- /* 990 */ 118, 118, 1324, 368, 1066, 447, 825, 49, 49, 534,
- /* 1000 */ 458, 357, 534, 353, 534, 138, 534, 337, 1478, 478,
- /* 1010 */ 116, 116, 116, 116, 115, 115, 114, 114, 114, 113,
- /* 1020 */ 414, 62, 62, 392, 63, 63, 64, 64, 14, 14,
- /* 1030 */ 116, 116, 116, 116, 115, 115, 114, 114, 114, 113,
- /* 1040 */ 414, 534, 810, 317, 271, 534, 1457, 825, 534, 6,
- /* 1050 */ 534, 1324, 534, 142, 534, 1442, 534, 212, 534, 1324,
- /* 1060 */ 534, 398, 305, 65, 65, 534, 1157, 125, 125, 476,
- /* 1070 */ 66, 66, 51, 51, 67, 67, 68, 68, 52, 52,
- /* 1080 */ 147, 147, 148, 148, 534, 98, 534, 75, 75, 276,
- /* 1090 */ 534, 272, 534, 810, 534, 876, 534, 527, 389, 534,
- /* 1100 */ 875, 534, 1151, 202, 534, 383, 53, 53, 71, 71,
- /* 1110 */ 288, 525, 126, 126, 72, 72, 127, 127, 128, 128,
- /* 1120 */ 454, 124, 124, 146, 146, 383, 145, 145, 408, 119,
- /* 1130 */ 120, 110, 1136, 1136, 981, 984, 974, 974, 117, 117,
- /* 1140 */ 118, 118, 118, 118, 534, 900, 534, 95, 534, 119,
- /* 1150 */ 120, 110, 1136, 1136, 981, 984, 974, 974, 117, 117,
- /* 1160 */ 118, 118, 118, 118, 390, 161, 132, 132, 131, 131,
- /* 1170 */ 129, 129, 534, 915, 534, 1455, 534, 1454, 6, 1416,
- /* 1180 */ 6, 916, 116, 116, 116, 116, 115, 115, 114, 114,
- /* 1190 */ 114, 113, 414, 1415, 130, 130, 74, 74, 76, 76,
- /* 1200 */ 534, 30, 116, 116, 116, 116, 115, 115, 114, 114,
- /* 1210 */ 114, 113, 414, 534, 263, 206, 534, 1133, 1504, 93,
- /* 1220 */ 876, 845, 73, 73, 102, 875, 100, 139, 17, 38,
- /* 1230 */ 208, 1062, 31, 450, 370, 43, 43, 101, 47, 47,
- /* 1240 */ 827, 216, 436, 308, 943, 440, 95, 241, 241, 442,
- /* 1250 */ 313, 464, 241, 95, 237, 900, 327, 383, 266, 95,
- /* 1260 */ 835, 834, 193, 335, 938, 314, 1011, 435, 842, 843,
- /* 1270 */ 955, 1007, 909, 334, 237, 241, 873, 383, 1023, 107,
- /* 1280 */ 1023, 119, 120, 110, 1136, 1136, 981, 984, 974, 974,
- /* 1290 */ 117, 117, 118, 118, 118, 118, 1022, 808, 1022, 1274,
- /* 1300 */ 137, 119, 108, 110, 1136, 1136, 981, 984, 974, 974,
- /* 1310 */ 117, 117, 118, 118, 118, 118, 874, 1011, 318, 107,
- /* 1320 */ 321, 955, 323, 325, 1225, 1211, 197, 1210, 1209, 330,
- /* 1330 */ 339, 1265, 340, 283, 116, 116, 116, 116, 115, 115,
- /* 1340 */ 114, 114, 114, 113, 414, 1286, 1323, 1261, 1471, 1272,
- /* 1350 */ 520, 218, 521, 1329, 116, 116, 116, 116, 115, 115,
- /* 1360 */ 114, 114, 114, 113, 414, 1192, 1184, 1173, 1172, 1174,
- /* 1370 */ 1494, 1488, 459, 256, 383, 1258, 342, 199, 367, 344,
- /* 1380 */ 211, 195, 307, 444, 11, 346, 469, 333, 1308, 1316,
- /* 1390 */ 375, 427, 203, 360, 383, 1388, 188, 1387, 189, 120,
- /* 1400 */ 110, 1136, 1136, 981, 984, 974, 974, 117, 117, 118,
- /* 1410 */ 118, 118, 118, 1208, 1151, 300, 348, 1491, 245, 1148,
- /* 1420 */ 110, 1136, 1136, 981, 984, 974, 974, 117, 117, 118,
- /* 1430 */ 118, 118, 118, 198, 1435, 1433, 524, 78, 391, 163,
- /* 1440 */ 82, 1393, 438, 173, 81, 105, 526, 1313, 4, 35,
- /* 1450 */ 157, 116, 116, 116, 116, 115, 115, 114, 114, 114,
- /* 1460 */ 113, 414, 529, 165, 93, 430, 1305, 168, 169, 431,
- /* 1470 */ 462, 116, 116, 116, 116, 115, 115, 114, 114, 114,
- /* 1480 */ 113, 414, 170, 171, 221, 415, 372, 437, 1319, 177,
- /* 1490 */ 374, 36, 451, 225, 1382, 87, 457, 523, 257, 1404,
- /* 1500 */ 316, 105, 526, 227, 4, 182, 460, 160, 320, 228,
- /* 1510 */ 377, 1175, 475, 229, 1228, 404, 1227, 1226, 529, 827,
- /* 1520 */ 961, 1219, 378, 1200, 1199, 406, 103, 103, 1218, 332,
- /* 1530 */ 8, 281, 1198, 104, 1503, 415, 536, 535, 486, 282,
- /* 1540 */ 951, 415, 489, 495, 92, 244, 1269, 341, 243, 122,
- /* 1550 */ 1270, 343, 514, 523, 1268, 1462, 10, 288, 525, 345,
- /* 1560 */ 1461, 354, 99, 352, 503, 94, 1267, 347, 1251, 502,
- /* 1570 */ 498, 951, 951, 953, 954, 27, 961, 1250, 194, 358,
- /* 1580 */ 251, 359, 103, 103, 1181, 34, 537, 1110, 252, 104,
- /* 1590 */ 254, 415, 536, 535, 255, 1368, 951, 1420, 286, 538,
- /* 1600 */ 1170, 1165, 1421, 135, 1419, 1418, 149, 150, 279, 784,
- /* 1610 */ 416, 196, 151, 290, 210, 200, 77, 385, 269, 386,
- /* 1620 */ 133, 162, 935, 1021, 201, 1019, 153, 951, 951, 953,
- /* 1630 */ 954, 27, 1480, 1104, 417, 164, 217, 268, 859, 166,
- /* 1640 */ 306, 1035, 366, 366, 365, 253, 363, 220, 172, 797,
- /* 1650 */ 939, 155, 105, 526, 393, 4, 395, 174, 156, 83,
- /* 1660 */ 1038, 84, 213, 85, 294, 222, 86, 223, 1034, 529,
- /* 1670 */ 144, 18, 293, 224, 315, 456, 241, 1027, 1145, 178,
- /* 1680 */ 226, 179, 37, 799, 334, 461, 230, 465, 470, 838,
- /* 1690 */ 180, 88, 415, 19, 280, 328, 20, 89, 90, 158,
- /* 1700 */ 191, 477, 215, 1097, 523, 204, 192, 987, 91, 1070,
- /* 1710 */ 152, 39, 485, 154, 1071, 503, 40, 488, 205, 260,
- /* 1720 */ 504, 262, 105, 526, 214, 4, 908, 961, 183, 240,
- /* 1730 */ 903, 107, 1086, 103, 103, 21, 22, 1088, 23, 529,
- /* 1740 */ 104, 24, 415, 536, 535, 1090, 1093, 951, 1094, 25,
- /* 1750 */ 1074, 33, 7, 26, 510, 1002, 247, 186, 384, 95,
- /* 1760 */ 988, 986, 415, 288, 525, 990, 1044, 246, 1043, 991,
- /* 1770 */ 28, 41, 530, 956, 523, 809, 106, 29, 951, 951,
- /* 1780 */ 953, 954, 27, 869, 361, 503, 422, 248, 364, 1105,
- /* 1790 */ 502, 249, 1161, 1496, 1495, 1161, 1161, 961, 1161, 1161,
- /* 1800 */ 1161, 1161, 1161, 103, 103, 1161, 1161, 1161, 1161, 1161,
- /* 1810 */ 104, 1161, 415, 536, 535, 1104, 417, 951, 1161, 268,
- /* 1820 */ 1161, 1161, 1161, 1161, 366, 366, 365, 253, 363, 1161,
- /* 1830 */ 1161, 797, 1161, 1161, 1161, 1161, 105, 526, 1161, 4,
- /* 1840 */ 1161, 1161, 1161, 1161, 213, 1161, 294, 1161, 951, 951,
- /* 1850 */ 953, 954, 27, 529, 293, 1161, 1161, 1161, 1161, 1161,
- /* 1860 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161,
- /* 1870 */ 1161, 1161, 1161, 1161, 1161, 1161, 415, 1161, 1161, 1161,
- /* 1880 */ 1161, 1161, 1161, 1161, 215, 1161, 1161, 1161, 523, 1161,
- /* 1890 */ 1161, 1161, 152, 1161, 1161, 154, 105, 526, 1161, 4,
- /* 1900 */ 1161, 1161, 1161, 1161, 1161, 1161, 214, 1161, 1161, 1161,
- /* 1910 */ 1161, 961, 1161, 529, 1161, 1161, 1161, 103, 103, 880,
- /* 1920 */ 1161, 1161, 1161, 1161, 104, 1161, 415, 536, 535, 1161,
- /* 1930 */ 1161, 951, 1161, 1161, 1161, 1161, 415, 1161, 1161, 1161,
- /* 1940 */ 384, 1161, 1161, 1161, 1161, 288, 525, 1161, 523, 1161,
- /* 1950 */ 1161, 1161, 1161, 1161, 1161, 1161, 97, 526, 1161, 4,
- /* 1960 */ 1161, 1161, 951, 951, 953, 954, 27, 1161, 422, 1161,
- /* 1970 */ 1161, 961, 1161, 529, 1161, 1161, 1161, 103, 103, 1161,
- /* 1980 */ 1161, 1161, 1161, 1161, 104, 1161, 415, 536, 535, 1161,
- /* 1990 */ 1161, 951, 268, 1161, 1161, 1161, 415, 366, 366, 365,
- /* 2000 */ 253, 363, 1161, 1161, 797, 1161, 1161, 1161, 523, 1161,
- /* 2010 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 213, 1161, 294,
- /* 2020 */ 1161, 1161, 951, 951, 953, 954, 27, 293, 1161, 1161,
- /* 2030 */ 1161, 961, 1161, 1161, 1161, 1161, 1161, 103, 103, 1161,
- /* 2040 */ 1161, 1161, 1161, 1161, 104, 1161, 415, 536, 535, 1161,
- /* 2050 */ 1161, 951, 1161, 1161, 1161, 1161, 1161, 215, 1161, 1161,
- /* 2060 */ 1161, 1161, 1161, 1161, 1161, 152, 1161, 1161, 154, 1161,
- /* 2070 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 214,
- /* 2080 */ 1161, 1161, 951, 951, 953, 954, 27, 1161, 1161, 1161,
- /* 2090 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161,
- /* 2100 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161,
- /* 2110 */ 1161, 1161, 1161, 384, 1161, 1161, 1161, 1161, 288, 525,
- /* 2120 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161,
- /* 2130 */ 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161, 1161,
- /* 2140 */ 1161, 422,
+ /* 0 */ 537, 339, 537, 1241, 1220, 537, 12, 537, 112, 109,
+ /* 10 */ 209, 537, 1241, 537, 1205, 462, 112, 109, 209, 386,
+ /* 20 */ 338, 462, 42, 42, 42, 42, 445, 42, 42, 70,
+ /* 30 */ 70, 922, 1208, 70, 70, 70, 70, 1443, 403, 923,
+ /* 40 */ 531, 531, 531, 119, 120, 110, 1148, 1148, 991, 994,
+ /* 50 */ 984, 984, 117, 117, 118, 118, 118, 118, 425, 386,
+ /* 60 */ 1498, 542, 2, 1176, 1442, 519, 141, 1518, 289, 519,
+ /* 70 */ 134, 519, 95, 259, 495, 1215, 189, 1254, 518, 494,
+ /* 80 */ 484, 437, 296, 119, 120, 110, 1148, 1148, 991, 994,
+ /* 90 */ 984, 984, 117, 117, 118, 118, 118, 118, 270, 116,
+ /* 100 */ 116, 116, 116, 115, 115, 114, 114, 114, 113, 418,
+ /* 110 */ 264, 264, 264, 264, 423, 1479, 352, 1481, 123, 351,
+ /* 120 */ 1479, 508, 1094, 534, 1034, 534, 1099, 386, 1099, 239,
+ /* 130 */ 206, 112, 109, 209, 96, 1094, 376, 219, 1094, 116,
+ /* 140 */ 116, 116, 116, 115, 115, 114, 114, 114, 113, 418,
+ /* 150 */ 480, 119, 120, 110, 1148, 1148, 991, 994, 984, 984,
+ /* 160 */ 117, 117, 118, 118, 118, 118, 353, 422, 1407, 264,
+ /* 170 */ 264, 114, 114, 114, 113, 418, 883, 121, 416, 416,
+ /* 180 */ 416, 882, 534, 116, 116, 116, 116, 115, 115, 114,
+ /* 190 */ 114, 114, 113, 418, 212, 415, 414, 386, 443, 383,
+ /* 200 */ 382, 118, 118, 118, 118, 111, 177, 116, 116, 116,
+ /* 210 */ 116, 115, 115, 114, 114, 114, 113, 418, 112, 109,
+ /* 220 */ 209, 119, 120, 110, 1148, 1148, 991, 994, 984, 984,
+ /* 230 */ 117, 117, 118, 118, 118, 118, 386, 438, 312, 1163,
+ /* 240 */ 1155, 80, 1155, 1127, 514, 79, 116, 116, 116, 116,
+ /* 250 */ 115, 115, 114, 114, 114, 113, 418, 514, 428, 418,
+ /* 260 */ 119, 120, 110, 1148, 1148, 991, 994, 984, 984, 117,
+ /* 270 */ 117, 118, 118, 118, 118, 428, 427, 116, 116, 116,
+ /* 280 */ 116, 115, 115, 114, 114, 114, 113, 418, 115, 115,
+ /* 290 */ 114, 114, 114, 113, 418, 1127, 1127, 1128, 1129, 1094,
+ /* 300 */ 258, 258, 192, 386, 408, 371, 1168, 326, 118, 118,
+ /* 310 */ 118, 118, 1094, 534, 374, 1094, 116, 116, 116, 116,
+ /* 320 */ 115, 115, 114, 114, 114, 113, 418, 119, 120, 110,
+ /* 330 */ 1148, 1148, 991, 994, 984, 984, 117, 117, 118, 118,
+ /* 340 */ 118, 118, 386, 354, 445, 428, 829, 238, 1127, 1128,
+ /* 350 */ 1129, 515, 1466, 116, 116, 116, 116, 115, 115, 114,
+ /* 360 */ 114, 114, 113, 418, 1127, 1467, 119, 120, 110, 1148,
+ /* 370 */ 1148, 991, 994, 984, 984, 117, 117, 118, 118, 118,
+ /* 380 */ 118, 1169, 82, 116, 116, 116, 116, 115, 115, 114,
+ /* 390 */ 114, 114, 113, 418, 405, 112, 109, 209, 161, 445,
+ /* 400 */ 250, 267, 336, 478, 331, 477, 236, 951, 1127, 386,
+ /* 410 */ 888, 1521, 329, 822, 852, 162, 274, 1127, 1128, 1129,
+ /* 420 */ 338, 169, 116, 116, 116, 116, 115, 115, 114, 114,
+ /* 430 */ 114, 113, 418, 119, 120, 110, 1148, 1148, 991, 994,
+ /* 440 */ 984, 984, 117, 117, 118, 118, 118, 118, 386, 438,
+ /* 450 */ 312, 1502, 1112, 1176, 161, 288, 528, 311, 289, 883,
+ /* 460 */ 134, 1127, 1128, 1129, 882, 537, 143, 1254, 288, 528,
+ /* 470 */ 297, 275, 119, 120, 110, 1148, 1148, 991, 994, 984,
+ /* 480 */ 984, 117, 117, 118, 118, 118, 118, 70, 70, 116,
+ /* 490 */ 116, 116, 116, 115, 115, 114, 114, 114, 113, 418,
+ /* 500 */ 264, 264, 12, 264, 264, 395, 1127, 483, 1473, 1094,
+ /* 510 */ 204, 482, 6, 534, 1258, 386, 534, 1474, 825, 972,
+ /* 520 */ 504, 6, 1094, 500, 95, 1094, 534, 219, 116, 116,
+ /* 530 */ 116, 116, 115, 115, 114, 114, 114, 113, 418, 119,
+ /* 540 */ 120, 110, 1148, 1148, 991, 994, 984, 984, 117, 117,
+ /* 550 */ 118, 118, 118, 118, 386, 1339, 971, 422, 956, 1127,
+ /* 560 */ 1128, 1129, 231, 512, 1473, 475, 472, 471, 6, 113,
+ /* 570 */ 418, 825, 962, 298, 503, 470, 961, 452, 119, 120,
+ /* 580 */ 110, 1148, 1148, 991, 994, 984, 984, 117, 117, 118,
+ /* 590 */ 118, 118, 118, 395, 537, 116, 116, 116, 116, 115,
+ /* 600 */ 115, 114, 114, 114, 113, 418, 202, 961, 961, 963,
+ /* 610 */ 231, 971, 1127, 475, 472, 471, 13, 13, 951, 1127,
+ /* 620 */ 834, 386, 1207, 470, 399, 183, 447, 962, 462, 162,
+ /* 630 */ 397, 961, 1246, 1246, 116, 116, 116, 116, 115, 115,
+ /* 640 */ 114, 114, 114, 113, 418, 119, 120, 110, 1148, 1148,
+ /* 650 */ 991, 994, 984, 984, 117, 117, 118, 118, 118, 118,
+ /* 660 */ 386, 271, 961, 961, 963, 1127, 1128, 1129, 311, 433,
+ /* 670 */ 299, 1406, 1127, 1128, 1129, 178, 1471, 138, 162, 32,
+ /* 680 */ 6, 1127, 288, 528, 119, 120, 110, 1148, 1148, 991,
+ /* 690 */ 994, 984, 984, 117, 117, 118, 118, 118, 118, 909,
+ /* 700 */ 390, 116, 116, 116, 116, 115, 115, 114, 114, 114,
+ /* 710 */ 113, 418, 1127, 429, 817, 537, 1127, 265, 265, 981,
+ /* 720 */ 981, 992, 995, 324, 1055, 93, 520, 5, 338, 537,
+ /* 730 */ 534, 288, 528, 1522, 1127, 1128, 1129, 70, 70, 1056,
+ /* 740 */ 116, 116, 116, 116, 115, 115, 114, 114, 114, 113,
+ /* 750 */ 418, 70, 70, 1495, 1057, 537, 98, 1244, 1244, 264,
+ /* 760 */ 264, 908, 371, 1076, 1127, 1127, 1128, 1129, 817, 1127,
+ /* 770 */ 1128, 1129, 534, 519, 140, 863, 386, 13, 13, 456,
+ /* 780 */ 192, 193, 521, 453, 319, 864, 322, 284, 365, 430,
+ /* 790 */ 985, 402, 379, 1077, 1548, 101, 386, 1548, 3, 395,
+ /* 800 */ 119, 120, 110, 1148, 1148, 991, 994, 984, 984, 117,
+ /* 810 */ 117, 118, 118, 118, 118, 386, 451, 1127, 1128, 1129,
+ /* 820 */ 119, 120, 110, 1148, 1148, 991, 994, 984, 984, 117,
+ /* 830 */ 117, 118, 118, 118, 118, 1127, 1354, 1412, 1169, 119,
+ /* 840 */ 108, 110, 1148, 1148, 991, 994, 984, 984, 117, 117,
+ /* 850 */ 118, 118, 118, 118, 1412, 1414, 116, 116, 116, 116,
+ /* 860 */ 115, 115, 114, 114, 114, 113, 418, 272, 535, 1075,
+ /* 870 */ 877, 877, 337, 1492, 309, 462, 116, 116, 116, 116,
+ /* 880 */ 115, 115, 114, 114, 114, 113, 418, 537, 1127, 1128,
+ /* 890 */ 1129, 537, 360, 537, 356, 116, 116, 116, 116, 115,
+ /* 900 */ 115, 114, 114, 114, 113, 418, 386, 264, 264, 13,
+ /* 910 */ 13, 273, 1127, 13, 13, 13, 13, 304, 1253, 386,
+ /* 920 */ 534, 1077, 1549, 404, 1412, 1549, 496, 277, 451, 186,
+ /* 930 */ 1252, 120, 110, 1148, 1148, 991, 994, 984, 984, 117,
+ /* 940 */ 117, 118, 118, 118, 118, 110, 1148, 1148, 991, 994,
+ /* 950 */ 984, 984, 117, 117, 118, 118, 118, 118, 105, 529,
+ /* 960 */ 537, 4, 1339, 264, 264, 1127, 1128, 1129, 1039, 1039,
+ /* 970 */ 459, 795, 796, 797, 536, 532, 534, 242, 301, 807,
+ /* 980 */ 303, 462, 69, 69, 451, 1353, 116, 116, 116, 116,
+ /* 990 */ 115, 115, 114, 114, 114, 113, 418, 1075, 419, 116,
+ /* 1000 */ 116, 116, 116, 115, 115, 114, 114, 114, 113, 418,
+ /* 1010 */ 526, 537, 1146, 192, 350, 105, 529, 537, 4, 497,
+ /* 1020 */ 162, 337, 1492, 310, 1249, 385, 1550, 372, 9, 462,
+ /* 1030 */ 242, 400, 532, 13, 13, 499, 971, 843, 436, 70,
+ /* 1040 */ 70, 359, 103, 103, 8, 339, 278, 187, 278, 104,
+ /* 1050 */ 1127, 419, 539, 538, 1339, 419, 961, 302, 1339, 1172,
+ /* 1060 */ 1, 1, 542, 2, 1176, 1146, 1146, 526, 476, 289,
+ /* 1070 */ 30, 134, 317, 288, 528, 285, 844, 1014, 1254, 276,
+ /* 1080 */ 1472, 506, 410, 1194, 6, 207, 505, 961, 961, 963,
+ /* 1090 */ 964, 27, 449, 971, 415, 414, 234, 233, 232, 103,
+ /* 1100 */ 103, 31, 1152, 1127, 1128, 1129, 104, 1154, 419, 539,
+ /* 1110 */ 538, 264, 264, 961, 1399, 1153, 264, 264, 1470, 1146,
+ /* 1120 */ 537, 216, 6, 401, 534, 1197, 392, 458, 406, 534,
+ /* 1130 */ 537, 485, 358, 537, 261, 537, 1339, 907, 219, 1155,
+ /* 1140 */ 467, 1155, 50, 50, 961, 961, 963, 964, 27, 1497,
+ /* 1150 */ 1116, 421, 70, 70, 268, 70, 70, 13, 13, 369,
+ /* 1160 */ 369, 368, 253, 366, 264, 264, 804, 235, 422, 105,
+ /* 1170 */ 529, 516, 4, 287, 487, 510, 493, 534, 486, 213,
+ /* 1180 */ 1055, 294, 490, 384, 1127, 450, 532, 338, 413, 293,
+ /* 1190 */ 522, 417, 335, 1036, 509, 1056, 107, 1036, 16, 16,
+ /* 1200 */ 1469, 1094, 334, 1105, 6, 411, 1145, 264, 264, 419,
+ /* 1210 */ 1057, 102, 511, 100, 1094, 264, 264, 1094, 922, 215,
+ /* 1220 */ 534, 526, 907, 264, 264, 208, 923, 154, 534, 457,
+ /* 1230 */ 156, 525, 391, 142, 218, 506, 534, 1127, 1128, 1129,
+ /* 1240 */ 507, 139, 1131, 38, 214, 530, 392, 971, 329, 1454,
+ /* 1250 */ 907, 1105, 537, 103, 103, 105, 529, 537, 4, 537,
+ /* 1260 */ 104, 424, 419, 539, 538, 537, 502, 961, 517, 537,
+ /* 1270 */ 1072, 537, 532, 373, 54, 54, 288, 528, 387, 55,
+ /* 1280 */ 55, 15, 15, 288, 528, 17, 136, 44, 44, 1451,
+ /* 1290 */ 537, 56, 56, 57, 57, 419, 1131, 291, 961, 961,
+ /* 1300 */ 963, 964, 27, 393, 163, 537, 426, 526, 263, 206,
+ /* 1310 */ 208, 517, 58, 58, 235, 440, 842, 841, 197, 105,
+ /* 1320 */ 529, 506, 4, 1033, 439, 1033, 505, 59, 59, 308,
+ /* 1330 */ 849, 850, 95, 971, 537, 907, 532, 948, 832, 103,
+ /* 1340 */ 103, 105, 529, 537, 4, 1021, 104, 537, 419, 539,
+ /* 1350 */ 538, 1116, 421, 961, 537, 268, 60, 60, 532, 419,
+ /* 1360 */ 369, 369, 368, 253, 366, 61, 61, 804, 965, 45,
+ /* 1370 */ 45, 526, 537, 1032, 1277, 1032, 46, 46, 537, 391,
+ /* 1380 */ 213, 419, 294, 266, 961, 961, 963, 964, 27, 292,
+ /* 1390 */ 293, 295, 832, 526, 48, 48, 1290, 971, 1289, 1021,
+ /* 1400 */ 49, 49, 432, 103, 103, 887, 953, 537, 1457, 241,
+ /* 1410 */ 104, 305, 419, 539, 538, 925, 926, 961, 444, 971,
+ /* 1420 */ 215, 241, 965, 1224, 537, 103, 103, 1431, 154, 62,
+ /* 1430 */ 62, 156, 104, 1430, 419, 539, 538, 97, 529, 961,
+ /* 1440 */ 4, 537, 454, 537, 314, 214, 63, 63, 961, 961,
+ /* 1450 */ 963, 964, 27, 537, 532, 446, 1286, 318, 241, 537,
+ /* 1460 */ 321, 323, 325, 64, 64, 14, 14, 1237, 537, 1223,
+ /* 1470 */ 961, 961, 963, 964, 27, 65, 65, 419, 537, 387,
+ /* 1480 */ 537, 125, 125, 537, 288, 528, 537, 1486, 537, 526,
+ /* 1490 */ 66, 66, 313, 524, 537, 95, 468, 1221, 1511, 237,
+ /* 1500 */ 51, 51, 67, 67, 330, 68, 68, 426, 52, 52,
+ /* 1510 */ 149, 149, 1222, 340, 341, 971, 150, 150, 1298, 463,
+ /* 1520 */ 327, 103, 103, 95, 537, 1338, 1273, 537, 104, 537,
+ /* 1530 */ 419, 539, 538, 1284, 537, 961, 268, 283, 523, 1344,
+ /* 1540 */ 1204, 369, 369, 368, 253, 366, 75, 75, 804, 53,
+ /* 1550 */ 53, 71, 71, 537, 1196, 537, 126, 126, 537, 1017,
+ /* 1560 */ 537, 213, 237, 294, 537, 1185, 961, 961, 963, 964,
+ /* 1570 */ 27, 293, 537, 1184, 537, 72, 72, 127, 127, 1186,
+ /* 1580 */ 128, 128, 124, 124, 1505, 537, 148, 148, 537, 256,
+ /* 1590 */ 195, 537, 1270, 537, 147, 147, 132, 132, 537, 11,
+ /* 1600 */ 537, 215, 537, 199, 343, 345, 347, 131, 131, 154,
+ /* 1610 */ 129, 129, 156, 130, 130, 74, 74, 537, 370, 1323,
+ /* 1620 */ 76, 76, 73, 73, 43, 43, 214, 431, 211, 1331,
+ /* 1630 */ 300, 916, 880, 815, 241, 107, 137, 307, 881, 47,
+ /* 1640 */ 47, 107, 473, 378, 203, 448, 333, 1403, 1220, 1402,
+ /* 1650 */ 349, 190, 527, 191, 363, 198, 1508, 1163, 245, 165,
+ /* 1660 */ 387, 1450, 1448, 1160, 78, 288, 528, 1408, 81, 394,
+ /* 1670 */ 82, 442, 175, 159, 167, 93, 1328, 35, 1320, 434,
+ /* 1680 */ 170, 171, 172, 173, 435, 466, 221, 375, 426, 377,
+ /* 1690 */ 1334, 179, 455, 441, 1397, 225, 87, 36, 461, 1419,
+ /* 1700 */ 316, 257, 227, 184, 320, 464, 228, 479, 1187, 229,
+ /* 1710 */ 380, 1240, 1239, 407, 1238, 1212, 834, 332, 1231, 381,
+ /* 1720 */ 409, 1211, 204, 1210, 1491, 498, 1520, 1281, 92, 281,
+ /* 1730 */ 1230, 489, 282, 492, 342, 243, 1282, 344, 244, 1280,
+ /* 1740 */ 346, 412, 1279, 1477, 348, 122, 1476, 517, 10, 357,
+ /* 1750 */ 286, 1305, 1304, 99, 1383, 94, 501, 251, 1193, 34,
+ /* 1760 */ 1263, 355, 540, 194, 1262, 361, 362, 1122, 252, 254,
+ /* 1770 */ 255, 388, 541, 1182, 1177, 151, 1435, 389, 1436, 1434,
+ /* 1780 */ 1433, 791, 152, 135, 279, 200, 201, 420, 196, 77,
+ /* 1790 */ 153, 290, 269, 210, 1031, 133, 1029, 945, 166, 155,
+ /* 1800 */ 217, 168, 866, 306, 220, 1045, 174, 949, 157, 396,
+ /* 1810 */ 83, 398, 176, 84, 85, 164, 86, 158, 1048, 222,
+ /* 1820 */ 223, 1044, 144, 18, 224, 315, 1037, 180, 241, 460,
+ /* 1830 */ 1157, 226, 181, 37, 806, 465, 334, 230, 328, 469,
+ /* 1840 */ 182, 88, 474, 19, 20, 160, 89, 280, 145, 90,
+ /* 1850 */ 481, 845, 1110, 146, 997, 205, 1080, 39, 91, 40,
+ /* 1860 */ 488, 1081, 915, 491, 260, 262, 185, 910, 240, 107,
+ /* 1870 */ 1100, 1096, 1098, 1104, 21, 1084, 33, 513, 247, 22,
+ /* 1880 */ 23, 24, 1103, 25, 188, 95, 1012, 998, 996, 26,
+ /* 1890 */ 1000, 1054, 7, 1053, 1001, 246, 28, 41, 533, 966,
+ /* 1900 */ 816, 106, 29, 367, 248, 249, 1513, 1512, 364, 1117,
+ /* 1910 */ 1173, 1173, 876,
};
static const YYCODETYPE yy_lookahead[] = {
- /* 0 */ 260, 261, 262, 260, 261, 262, 176, 177, 178, 179,
- /* 10 */ 180, 181, 184, 206, 209, 184, 186, 206, 188, 19,
- /* 20 */ 179, 281, 181, 213, 214, 195, 206, 186, 195, 188,
- /* 30 */ 195, 31, 222, 184, 206, 207, 195, 206, 207, 39,
- /* 40 */ 24, 209, 184, 43, 44, 45, 46, 47, 48, 49,
- /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 228, 229,
- /* 60 */ 184, 228, 229, 228, 229, 260, 261, 262, 192, 228,
- /* 70 */ 229, 241, 196, 242, 241, 59, 241, 205, 245, 246,
- /* 80 */ 184, 22, 241, 24, 254, 213, 54, 55, 56, 57,
- /* 90 */ 58, 256, 260, 261, 262, 254, 96, 97, 98, 99,
- /* 100 */ 100, 101, 102, 103, 104, 105, 106, 100, 101, 102,
- /* 110 */ 103, 104, 105, 106, 284, 203, 19, 221, 59, 102,
- /* 120 */ 103, 104, 105, 106, 59, 284, 110, 269, 96, 97,
- /* 130 */ 98, 99, 100, 101, 102, 103, 104, 105, 106, 94,
- /* 140 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
- /* 150 */ 53, 54, 55, 56, 57, 110, 184, 106, 73, 114,
- /* 160 */ 178, 179, 180, 181, 219, 184, 81, 22, 186, 110,
- /* 170 */ 188, 121, 122, 195, 109, 110, 111, 195, 206, 207,
- /* 180 */ 83, 184, 85, 54, 55, 56, 57, 206, 207, 277,
- /* 190 */ 145, 146, 147, 96, 97, 98, 99, 100, 101, 102,
- /* 200 */ 103, 104, 105, 106, 59, 120, 228, 229, 143, 184,
- /* 210 */ 228, 229, 184, 19, 242, 203, 131, 132, 221, 241,
- /* 220 */ 26, 184, 184, 241, 196, 96, 97, 98, 99, 100,
- /* 230 */ 101, 102, 103, 104, 105, 106, 254, 43, 44, 45,
- /* 240 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
- /* 250 */ 56, 57, 184, 184, 109, 110, 111, 105, 106, 184,
- /* 260 */ 200, 201, 202, 69, 184, 227, 284, 96, 97, 98,
- /* 270 */ 99, 100, 101, 102, 103, 104, 105, 106, 297, 298,
- /* 280 */ 255, 206, 207, 19, 272, 59, 206, 207, 184, 277,
- /* 290 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
- /* 300 */ 106, 184, 259, 19, 184, 100, 101, 43, 44, 45,
- /* 310 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
- /* 320 */ 56, 57, 242, 206, 207, 184, 100, 101, 138, 292,
- /* 330 */ 293, 67, 76, 296, 108, 109, 110, 111, 295, 113,
- /* 340 */ 84, 59, 86, 22, 26, 89, 156, 121, 224, 225,
- /* 350 */ 145, 19, 147, 59, 72, 256, 24, 184, 290, 291,
- /* 360 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
- /* 370 */ 106, 145, 297, 147, 299, 43, 44, 45, 46, 47,
- /* 380 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
- /* 390 */ 106, 109, 110, 111, 138, 184, 112, 113, 114, 115,
- /* 400 */ 116, 117, 118, 109, 110, 111, 112, 59, 124, 115,
- /* 410 */ 116, 117, 292, 293, 297, 298, 296, 206, 207, 125,
- /* 420 */ 72, 100, 101, 184, 46, 47, 48, 49, 96, 97,
- /* 430 */ 98, 99, 100, 101, 102, 103, 104, 105, 106, 59,
- /* 440 */ 200, 201, 202, 184, 59, 206, 207, 46, 19, 131,
- /* 450 */ 132, 278, 23, 242, 184, 184, 76, 109, 110, 111,
- /* 460 */ 224, 225, 251, 81, 84, 184, 86, 22, 23, 89,
- /* 470 */ 184, 26, 43, 44, 45, 46, 47, 48, 49, 50,
- /* 480 */ 51, 52, 53, 54, 55, 56, 57, 102, 184, 109,
- /* 490 */ 110, 111, 114, 184, 109, 110, 111, 184, 227, 184,
- /* 500 */ 230, 184, 22, 264, 195, 59, 22, 184, 195, 108,
- /* 510 */ 206, 207, 59, 131, 132, 206, 207, 184, 138, 206,
- /* 520 */ 207, 206, 207, 206, 207, 96, 97, 98, 99, 100,
- /* 530 */ 101, 102, 103, 104, 105, 106, 255, 228, 229, 59,
- /* 540 */ 95, 228, 229, 59, 184, 19, 242, 94, 184, 23,
- /* 550 */ 241, 242, 184, 282, 241, 242, 110, 242, 184, 242,
- /* 560 */ 251, 59, 109, 110, 196, 184, 251, 114, 251, 43,
- /* 570 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
- /* 580 */ 54, 55, 56, 57, 259, 217, 12, 219, 255, 109,
- /* 590 */ 110, 111, 203, 109, 110, 111, 184, 22, 145, 146,
- /* 600 */ 147, 27, 112, 22, 230, 115, 116, 117, 227, 19,
- /* 610 */ 59, 109, 110, 111, 291, 125, 42, 35, 206, 207,
- /* 620 */ 295, 184, 96, 97, 98, 99, 100, 101, 102, 103,
- /* 630 */ 104, 105, 106, 26, 59, 233, 46, 63, 136, 184,
- /* 640 */ 59, 184, 19, 206, 207, 243, 23, 73, 66, 260,
- /* 650 */ 261, 262, 59, 184, 242, 195, 74, 220, 184, 184,
- /* 660 */ 109, 110, 111, 206, 207, 184, 43, 44, 45, 46,
- /* 670 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 680 */ 57, 206, 207, 76, 109, 110, 111, 136, 228, 229,
- /* 690 */ 109, 110, 111, 86, 184, 220, 89, 21, 108, 230,
- /* 700 */ 184, 241, 109, 110, 111, 123, 184, 127, 184, 129,
- /* 710 */ 130, 184, 195, 184, 124, 184, 198, 199, 184, 96,
- /* 720 */ 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
- /* 730 */ 206, 207, 11, 206, 207, 206, 207, 206, 207, 19,
- /* 740 */ 206, 207, 184, 23, 220, 228, 229, 220, 81, 220,
- /* 750 */ 195, 220, 287, 288, 220, 195, 80, 195, 241, 201,
- /* 760 */ 202, 184, 73, 43, 44, 45, 46, 47, 48, 49,
- /* 770 */ 50, 51, 52, 53, 54, 55, 56, 57, 201, 202,
- /* 780 */ 113, 195, 184, 228, 229, 120, 121, 122, 228, 229,
- /* 790 */ 228, 229, 116, 16, 23, 184, 241, 26, 131, 132,
- /* 800 */ 278, 241, 19, 241, 22, 23, 184, 189, 26, 120,
- /* 810 */ 121, 122, 184, 26, 228, 229, 96, 97, 98, 99,
- /* 820 */ 100, 101, 102, 103, 104, 105, 106, 241, 270, 153,
- /* 830 */ 66, 228, 229, 229, 206, 207, 19, 184, 228, 229,
- /* 840 */ 23, 16, 121, 122, 241, 241, 82, 270, 29, 227,
- /* 850 */ 252, 241, 33, 19, 77, 91, 79, 184, 22, 23,
- /* 860 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
- /* 870 */ 53, 54, 55, 56, 57, 12, 184, 95, 184, 206,
- /* 880 */ 207, 76, 111, 184, 65, 267, 184, 184, 184, 271,
- /* 890 */ 27, 86, 109, 184, 89, 120, 121, 122, 206, 207,
- /* 900 */ 206, 207, 77, 139, 79, 42, 184, 136, 206, 207,
- /* 910 */ 206, 207, 184, 96, 97, 98, 99, 100, 101, 102,
- /* 920 */ 103, 104, 105, 106, 184, 138, 63, 184, 206, 207,
- /* 930 */ 153, 95, 184, 19, 206, 207, 227, 23, 7, 8,
- /* 940 */ 9, 293, 293, 109, 296, 296, 206, 207, 215, 206,
- /* 950 */ 207, 184, 253, 19, 206, 207, 253, 43, 44, 45,
- /* 960 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
- /* 970 */ 56, 57, 184, 206, 207, 184, 184, 43, 44, 45,
- /* 980 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
- /* 990 */ 56, 57, 184, 22, 23, 184, 59, 206, 207, 184,
- /* 1000 */ 184, 238, 184, 240, 184, 22, 184, 184, 157, 158,
- /* 1010 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
- /* 1020 */ 106, 206, 207, 184, 206, 207, 206, 207, 206, 207,
- /* 1030 */ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105,
- /* 1040 */ 106, 184, 59, 227, 252, 184, 293, 110, 184, 296,
- /* 1050 */ 184, 184, 184, 230, 184, 184, 184, 15, 184, 184,
- /* 1060 */ 184, 253, 184, 206, 207, 184, 95, 206, 207, 102,
- /* 1070 */ 206, 207, 206, 207, 206, 207, 206, 207, 206, 207,
- /* 1080 */ 206, 207, 206, 207, 184, 151, 184, 206, 207, 278,
- /* 1090 */ 184, 252, 184, 110, 184, 128, 184, 198, 199, 184,
- /* 1100 */ 133, 184, 60, 26, 184, 19, 206, 207, 206, 207,
- /* 1110 */ 131, 132, 206, 207, 206, 207, 206, 207, 206, 207,
- /* 1120 */ 253, 206, 207, 206, 207, 19, 206, 207, 253, 43,
- /* 1130 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
- /* 1140 */ 54, 55, 56, 57, 184, 26, 184, 26, 184, 43,
- /* 1150 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
- /* 1160 */ 54, 55, 56, 57, 285, 286, 206, 207, 206, 207,
- /* 1170 */ 206, 207, 184, 31, 184, 293, 184, 293, 296, 184,
- /* 1180 */ 296, 39, 96, 97, 98, 99, 100, 101, 102, 103,
- /* 1190 */ 104, 105, 106, 184, 206, 207, 206, 207, 206, 207,
- /* 1200 */ 184, 22, 96, 97, 98, 99, 100, 101, 102, 103,
- /* 1210 */ 104, 105, 106, 184, 245, 246, 184, 26, 23, 142,
- /* 1220 */ 128, 26, 206, 207, 150, 133, 152, 22, 22, 24,
- /* 1230 */ 111, 23, 53, 184, 26, 206, 207, 151, 206, 207,
- /* 1240 */ 119, 24, 122, 23, 23, 23, 26, 26, 26, 23,
- /* 1250 */ 23, 23, 26, 26, 26, 136, 23, 19, 22, 26,
- /* 1260 */ 113, 114, 24, 114, 144, 184, 59, 61, 7, 8,
- /* 1270 */ 59, 23, 23, 124, 26, 26, 23, 19, 145, 26,
- /* 1280 */ 147, 43, 44, 45, 46, 47, 48, 49, 50, 51,
- /* 1290 */ 52, 53, 54, 55, 56, 57, 145, 23, 147, 184,
- /* 1300 */ 26, 43, 44, 45, 46, 47, 48, 49, 50, 51,
- /* 1310 */ 52, 53, 54, 55, 56, 57, 23, 110, 184, 26,
- /* 1320 */ 184, 110, 184, 184, 184, 215, 135, 215, 184, 184,
- /* 1330 */ 184, 247, 184, 244, 96, 97, 98, 99, 100, 101,
- /* 1340 */ 102, 103, 104, 105, 106, 184, 184, 184, 301, 184,
- /* 1350 */ 184, 134, 225, 184, 96, 97, 98, 99, 100, 101,
- /* 1360 */ 102, 103, 104, 105, 106, 184, 184, 184, 184, 184,
- /* 1370 */ 134, 184, 274, 273, 19, 244, 244, 204, 182, 244,
- /* 1380 */ 283, 231, 279, 279, 232, 244, 210, 209, 235, 235,
- /* 1390 */ 235, 248, 218, 234, 19, 209, 238, 209, 238, 44,
- /* 1400 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
- /* 1410 */ 55, 56, 57, 214, 60, 248, 248, 187, 134, 38,
- /* 1420 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
- /* 1430 */ 55, 56, 57, 232, 191, 191, 266, 280, 191, 283,
- /* 1440 */ 143, 269, 108, 22, 280, 19, 20, 258, 22, 257,
- /* 1450 */ 43, 96, 97, 98, 99, 100, 101, 102, 103, 104,
- /* 1460 */ 105, 106, 36, 223, 142, 18, 235, 226, 226, 191,
- /* 1470 */ 18, 96, 97, 98, 99, 100, 101, 102, 103, 104,
- /* 1480 */ 105, 106, 226, 226, 190, 59, 235, 235, 223, 223,
- /* 1490 */ 258, 257, 191, 190, 235, 150, 62, 71, 191, 276,
- /* 1500 */ 275, 19, 20, 190, 22, 22, 211, 81, 191, 190,
- /* 1510 */ 211, 191, 108, 190, 208, 64, 208, 208, 36, 119,
- /* 1520 */ 94, 216, 211, 208, 210, 106, 100, 101, 216, 208,
- /* 1530 */ 48, 268, 208, 107, 208, 109, 110, 111, 211, 268,
- /* 1540 */ 114, 59, 211, 137, 108, 88, 250, 249, 191, 141,
- /* 1550 */ 250, 249, 138, 71, 250, 300, 22, 131, 132, 249,
- /* 1560 */ 300, 191, 150, 238, 82, 140, 250, 249, 239, 87,
- /* 1570 */ 139, 145, 146, 147, 148, 149, 94, 239, 237, 236,
- /* 1580 */ 25, 235, 100, 101, 194, 26, 193, 13, 185, 107,
- /* 1590 */ 185, 109, 110, 111, 6, 263, 114, 203, 265, 183,
- /* 1600 */ 183, 183, 203, 212, 203, 203, 197, 197, 212, 4,
- /* 1610 */ 3, 22, 197, 155, 15, 204, 203, 289, 93, 289,
- /* 1620 */ 16, 286, 132, 23, 204, 23, 123, 145, 146, 147,
- /* 1630 */ 148, 149, 0, 1, 2, 143, 24, 5, 20, 135,
- /* 1640 */ 16, 1, 10, 11, 12, 13, 14, 137, 135, 17,
- /* 1650 */ 144, 123, 19, 20, 61, 22, 37, 143, 123, 53,
- /* 1660 */ 109, 53, 30, 53, 32, 34, 53, 134, 1, 36,
- /* 1670 */ 5, 22, 40, 108, 153, 41, 26, 68, 75, 68,
- /* 1680 */ 134, 108, 24, 20, 124, 19, 118, 67, 67, 28,
- /* 1690 */ 22, 22, 59, 22, 67, 23, 22, 22, 142, 37,
- /* 1700 */ 23, 22, 70, 23, 71, 157, 23, 23, 26, 23,
- /* 1710 */ 78, 22, 24, 81, 23, 82, 22, 24, 134, 23,
- /* 1720 */ 87, 23, 19, 20, 92, 22, 109, 94, 22, 34,
- /* 1730 */ 136, 26, 85, 100, 101, 34, 34, 83, 34, 36,
- /* 1740 */ 107, 34, 109, 110, 111, 75, 90, 114, 75, 34,
- /* 1750 */ 23, 22, 44, 34, 24, 23, 22, 26, 126, 26,
- /* 1760 */ 23, 23, 59, 131, 132, 23, 23, 26, 23, 11,
- /* 1770 */ 22, 22, 26, 23, 71, 23, 22, 22, 145, 146,
- /* 1780 */ 147, 148, 149, 128, 23, 82, 154, 134, 15, 1,
- /* 1790 */ 87, 134, 302, 134, 134, 302, 302, 94, 302, 302,
- /* 1800 */ 302, 302, 302, 100, 101, 302, 302, 302, 302, 302,
- /* 1810 */ 107, 302, 109, 110, 111, 1, 2, 114, 302, 5,
- /* 1820 */ 302, 302, 302, 302, 10, 11, 12, 13, 14, 302,
- /* 1830 */ 302, 17, 302, 302, 302, 302, 19, 20, 302, 22,
- /* 1840 */ 302, 302, 302, 302, 30, 302, 32, 302, 145, 146,
- /* 1850 */ 147, 148, 149, 36, 40, 302, 302, 302, 302, 302,
- /* 1860 */ 302, 302, 302, 302, 302, 302, 302, 302, 302, 302,
- /* 1870 */ 302, 302, 302, 302, 302, 302, 59, 302, 302, 302,
- /* 1880 */ 302, 302, 302, 302, 70, 302, 302, 302, 71, 302,
- /* 1890 */ 302, 302, 78, 302, 302, 81, 19, 20, 302, 22,
- /* 1900 */ 302, 302, 302, 302, 302, 302, 92, 302, 302, 302,
- /* 1910 */ 302, 94, 302, 36, 302, 302, 302, 100, 101, 102,
- /* 1920 */ 302, 302, 302, 302, 107, 302, 109, 110, 111, 302,
- /* 1930 */ 302, 114, 302, 302, 302, 302, 59, 302, 302, 302,
- /* 1940 */ 126, 302, 302, 302, 302, 131, 132, 302, 71, 302,
- /* 1950 */ 302, 302, 302, 302, 302, 302, 19, 20, 302, 22,
- /* 1960 */ 302, 302, 145, 146, 147, 148, 149, 302, 154, 302,
- /* 1970 */ 302, 94, 302, 36, 302, 302, 302, 100, 101, 302,
- /* 1980 */ 302, 302, 302, 302, 107, 302, 109, 110, 111, 302,
- /* 1990 */ 302, 114, 5, 302, 302, 302, 59, 10, 11, 12,
- /* 2000 */ 13, 14, 302, 302, 17, 302, 302, 302, 71, 302,
- /* 2010 */ 302, 302, 302, 302, 302, 302, 302, 30, 302, 32,
- /* 2020 */ 302, 302, 145, 146, 147, 148, 149, 40, 302, 302,
- /* 2030 */ 302, 94, 302, 302, 302, 302, 302, 100, 101, 302,
- /* 2040 */ 302, 302, 302, 302, 107, 302, 109, 110, 111, 302,
- /* 2050 */ 302, 114, 302, 302, 302, 302, 302, 70, 302, 302,
- /* 2060 */ 302, 302, 302, 302, 302, 78, 302, 302, 81, 302,
- /* 2070 */ 302, 302, 302, 302, 302, 302, 302, 302, 302, 92,
- /* 2080 */ 302, 302, 145, 146, 147, 148, 149, 302, 302, 302,
- /* 2090 */ 302, 302, 302, 302, 302, 302, 302, 302, 302, 302,
- /* 2100 */ 302, 302, 302, 302, 302, 302, 302, 302, 302, 302,
- /* 2110 */ 302, 302, 302, 126, 302, 302, 302, 302, 131, 132,
- /* 2120 */ 302, 302, 302, 302, 302, 302, 302, 302, 302, 302,
- /* 2130 */ 302, 302, 302, 302, 302, 302, 302, 302, 302, 302,
- /* 2140 */ 302, 154, 302, 302, 302, 302, 302, 302, 302, 302,
- /* 2150 */ 302, 302, 302, 302, 302, 302, 302, 302, 302, 302,
- /* 2160 */ 302, 302, 302, 302, 302, 302, 302, 302, 302,
+ /* 0 */ 187, 187, 187, 216, 217, 187, 206, 187, 264, 265,
+ /* 10 */ 266, 187, 225, 187, 209, 187, 264, 265, 266, 19,
+ /* 20 */ 187, 187, 209, 210, 209, 210, 187, 209, 210, 209,
+ /* 30 */ 210, 31, 209, 209, 210, 209, 210, 285, 224, 39,
+ /* 40 */ 203, 204, 205, 43, 44, 45, 46, 47, 48, 49,
+ /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 230, 19,
+ /* 60 */ 181, 182, 183, 184, 230, 245, 233, 208, 189, 245,
+ /* 70 */ 191, 245, 26, 206, 254, 216, 276, 198, 254, 198,
+ /* 80 */ 254, 281, 187, 43, 44, 45, 46, 47, 48, 49,
+ /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 259, 99,
+ /* 100 */ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+ /* 110 */ 231, 232, 231, 232, 286, 302, 303, 302, 22, 304,
+ /* 120 */ 302, 303, 76, 244, 11, 244, 86, 19, 88, 248,
+ /* 130 */ 249, 264, 265, 266, 26, 89, 198, 258, 92, 99,
+ /* 140 */ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+ /* 150 */ 105, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+ /* 160 */ 52, 53, 54, 55, 56, 57, 212, 288, 273, 231,
+ /* 170 */ 232, 105, 106, 107, 108, 109, 131, 69, 203, 204,
+ /* 180 */ 205, 136, 244, 99, 100, 101, 102, 103, 104, 105,
+ /* 190 */ 106, 107, 108, 109, 15, 103, 104, 19, 260, 103,
+ /* 200 */ 104, 54, 55, 56, 57, 58, 22, 99, 100, 101,
+ /* 210 */ 102, 103, 104, 105, 106, 107, 108, 109, 264, 265,
+ /* 220 */ 266, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+ /* 230 */ 52, 53, 54, 55, 56, 57, 19, 124, 125, 60,
+ /* 240 */ 148, 24, 150, 59, 187, 67, 99, 100, 101, 102,
+ /* 250 */ 103, 104, 105, 106, 107, 108, 109, 187, 187, 109,
+ /* 260 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ /* 270 */ 53, 54, 55, 56, 57, 204, 205, 99, 100, 101,
+ /* 280 */ 102, 103, 104, 105, 106, 107, 108, 109, 103, 104,
+ /* 290 */ 105, 106, 107, 108, 109, 59, 112, 113, 114, 76,
+ /* 300 */ 231, 232, 187, 19, 19, 22, 23, 23, 54, 55,
+ /* 310 */ 56, 57, 89, 244, 199, 92, 99, 100, 101, 102,
+ /* 320 */ 103, 104, 105, 106, 107, 108, 109, 43, 44, 45,
+ /* 330 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+ /* 340 */ 56, 57, 19, 212, 187, 274, 23, 26, 112, 113,
+ /* 350 */ 114, 294, 295, 99, 100, 101, 102, 103, 104, 105,
+ /* 360 */ 106, 107, 108, 109, 59, 295, 43, 44, 45, 46,
+ /* 370 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ /* 380 */ 57, 98, 146, 99, 100, 101, 102, 103, 104, 105,
+ /* 390 */ 106, 107, 108, 109, 109, 264, 265, 266, 187, 187,
+ /* 400 */ 115, 116, 117, 118, 119, 120, 121, 73, 59, 19,
+ /* 410 */ 105, 23, 127, 23, 26, 81, 259, 112, 113, 114,
+ /* 420 */ 187, 72, 99, 100, 101, 102, 103, 104, 105, 106,
+ /* 430 */ 107, 108, 109, 43, 44, 45, 46, 47, 48, 49,
+ /* 440 */ 50, 51, 52, 53, 54, 55, 56, 57, 19, 124,
+ /* 450 */ 125, 182, 23, 184, 187, 134, 135, 123, 189, 131,
+ /* 460 */ 191, 112, 113, 114, 136, 187, 233, 198, 134, 135,
+ /* 470 */ 198, 259, 43, 44, 45, 46, 47, 48, 49, 50,
+ /* 480 */ 51, 52, 53, 54, 55, 56, 57, 209, 210, 99,
+ /* 490 */ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+ /* 500 */ 231, 232, 206, 231, 232, 187, 59, 296, 297, 76,
+ /* 510 */ 160, 161, 301, 244, 232, 19, 244, 297, 59, 23,
+ /* 520 */ 87, 301, 89, 245, 26, 92, 244, 258, 99, 100,
+ /* 530 */ 101, 102, 103, 104, 105, 106, 107, 108, 109, 43,
+ /* 540 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+ /* 550 */ 54, 55, 56, 57, 19, 187, 97, 288, 23, 112,
+ /* 560 */ 113, 114, 115, 296, 297, 118, 119, 120, 301, 108,
+ /* 570 */ 109, 112, 113, 255, 141, 128, 117, 281, 43, 44,
+ /* 580 */ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ /* 590 */ 55, 56, 57, 187, 187, 99, 100, 101, 102, 103,
+ /* 600 */ 104, 105, 106, 107, 108, 109, 26, 148, 149, 150,
+ /* 610 */ 115, 97, 59, 118, 119, 120, 209, 210, 73, 59,
+ /* 620 */ 122, 19, 209, 128, 256, 72, 187, 113, 187, 81,
+ /* 630 */ 223, 117, 227, 228, 99, 100, 101, 102, 103, 104,
+ /* 640 */ 105, 106, 107, 108, 109, 43, 44, 45, 46, 47,
+ /* 650 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ /* 660 */ 19, 255, 148, 149, 150, 112, 113, 114, 123, 124,
+ /* 670 */ 125, 230, 112, 113, 114, 22, 297, 22, 81, 22,
+ /* 680 */ 301, 59, 134, 135, 43, 44, 45, 46, 47, 48,
+ /* 690 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 139,
+ /* 700 */ 192, 99, 100, 101, 102, 103, 104, 105, 106, 107,
+ /* 710 */ 108, 109, 59, 116, 59, 187, 59, 231, 232, 46,
+ /* 720 */ 47, 48, 49, 16, 12, 145, 198, 22, 187, 187,
+ /* 730 */ 244, 134, 135, 222, 112, 113, 114, 209, 210, 27,
+ /* 740 */ 99, 100, 101, 102, 103, 104, 105, 106, 107, 108,
+ /* 750 */ 109, 209, 210, 187, 42, 187, 154, 227, 228, 231,
+ /* 760 */ 232, 139, 22, 23, 59, 112, 113, 114, 113, 112,
+ /* 770 */ 113, 114, 244, 245, 233, 63, 19, 209, 210, 271,
+ /* 780 */ 187, 24, 254, 275, 77, 73, 79, 245, 195, 260,
+ /* 790 */ 117, 223, 199, 22, 23, 154, 19, 26, 22, 187,
+ /* 800 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ /* 810 */ 53, 54, 55, 56, 57, 19, 187, 112, 113, 114,
+ /* 820 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ /* 830 */ 53, 54, 55, 56, 57, 59, 263, 187, 98, 43,
+ /* 840 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+ /* 850 */ 54, 55, 56, 57, 204, 205, 99, 100, 101, 102,
+ /* 860 */ 103, 104, 105, 106, 107, 108, 109, 255, 130, 98,
+ /* 870 */ 132, 133, 299, 300, 198, 187, 99, 100, 101, 102,
+ /* 880 */ 103, 104, 105, 106, 107, 108, 109, 187, 112, 113,
+ /* 890 */ 114, 187, 241, 187, 243, 99, 100, 101, 102, 103,
+ /* 900 */ 104, 105, 106, 107, 108, 109, 19, 231, 232, 209,
+ /* 910 */ 210, 282, 59, 209, 210, 209, 210, 16, 230, 19,
+ /* 920 */ 244, 22, 23, 223, 274, 26, 19, 223, 187, 223,
+ /* 930 */ 198, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ /* 940 */ 53, 54, 55, 56, 57, 45, 46, 47, 48, 49,
+ /* 950 */ 50, 51, 52, 53, 54, 55, 56, 57, 19, 20,
+ /* 960 */ 187, 22, 187, 231, 232, 112, 113, 114, 123, 124,
+ /* 970 */ 125, 7, 8, 9, 187, 36, 244, 24, 77, 21,
+ /* 980 */ 79, 187, 209, 210, 187, 263, 99, 100, 101, 102,
+ /* 990 */ 103, 104, 105, 106, 107, 108, 109, 98, 59, 99,
+ /* 1000 */ 100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
+ /* 1010 */ 71, 187, 59, 187, 187, 19, 20, 187, 22, 112,
+ /* 1020 */ 81, 299, 300, 282, 230, 199, 291, 292, 22, 187,
+ /* 1030 */ 24, 256, 36, 209, 210, 187, 97, 35, 80, 209,
+ /* 1040 */ 210, 268, 103, 104, 48, 187, 220, 223, 222, 110,
+ /* 1050 */ 59, 112, 113, 114, 187, 59, 117, 156, 187, 179,
+ /* 1060 */ 180, 181, 182, 183, 184, 59, 113, 71, 66, 189,
+ /* 1070 */ 22, 191, 230, 134, 135, 245, 74, 119, 198, 282,
+ /* 1080 */ 297, 85, 224, 198, 301, 187, 90, 148, 149, 150,
+ /* 1090 */ 151, 152, 19, 97, 103, 104, 123, 124, 125, 103,
+ /* 1100 */ 104, 53, 111, 112, 113, 114, 110, 116, 112, 113,
+ /* 1110 */ 114, 231, 232, 117, 156, 124, 231, 232, 297, 113,
+ /* 1120 */ 187, 24, 301, 256, 244, 201, 202, 256, 126, 244,
+ /* 1130 */ 187, 198, 187, 187, 23, 187, 187, 26, 258, 148,
+ /* 1140 */ 19, 150, 209, 210, 148, 149, 150, 151, 152, 0,
+ /* 1150 */ 1, 2, 209, 210, 5, 209, 210, 209, 210, 10,
+ /* 1160 */ 11, 12, 13, 14, 231, 232, 17, 46, 288, 19,
+ /* 1170 */ 20, 223, 22, 236, 198, 66, 187, 244, 245, 30,
+ /* 1180 */ 12, 32, 198, 246, 59, 112, 36, 187, 245, 40,
+ /* 1190 */ 198, 245, 117, 29, 85, 27, 26, 33, 209, 210,
+ /* 1200 */ 297, 76, 127, 94, 301, 256, 26, 231, 232, 59,
+ /* 1210 */ 42, 153, 87, 155, 89, 231, 232, 92, 31, 70,
+ /* 1220 */ 244, 71, 26, 231, 232, 114, 39, 78, 244, 65,
+ /* 1230 */ 81, 63, 111, 233, 137, 85, 244, 112, 113, 114,
+ /* 1240 */ 90, 22, 59, 24, 95, 201, 202, 97, 127, 187,
+ /* 1250 */ 139, 142, 187, 103, 104, 19, 20, 187, 22, 187,
+ /* 1260 */ 110, 187, 112, 113, 114, 187, 141, 117, 141, 187,
+ /* 1270 */ 23, 187, 36, 26, 209, 210, 134, 135, 129, 209,
+ /* 1280 */ 210, 209, 210, 134, 135, 22, 159, 209, 210, 187,
+ /* 1290 */ 187, 209, 210, 209, 210, 59, 113, 187, 148, 149,
+ /* 1300 */ 150, 151, 152, 289, 290, 187, 157, 71, 248, 249,
+ /* 1310 */ 114, 141, 209, 210, 46, 125, 116, 117, 138, 19,
+ /* 1320 */ 20, 85, 22, 148, 61, 150, 90, 209, 210, 23,
+ /* 1330 */ 7, 8, 26, 97, 187, 139, 36, 147, 59, 103,
+ /* 1340 */ 104, 19, 20, 187, 22, 59, 110, 187, 112, 113,
+ /* 1350 */ 114, 1, 2, 117, 187, 5, 209, 210, 36, 59,
+ /* 1360 */ 10, 11, 12, 13, 14, 209, 210, 17, 59, 209,
+ /* 1370 */ 210, 71, 187, 148, 250, 150, 209, 210, 187, 111,
+ /* 1380 */ 30, 59, 32, 22, 148, 149, 150, 151, 152, 187,
+ /* 1390 */ 40, 187, 113, 71, 209, 210, 187, 97, 187, 113,
+ /* 1400 */ 209, 210, 187, 103, 104, 105, 23, 187, 187, 26,
+ /* 1410 */ 110, 187, 112, 113, 114, 83, 84, 117, 23, 97,
+ /* 1420 */ 70, 26, 113, 218, 187, 103, 104, 187, 78, 209,
+ /* 1430 */ 210, 81, 110, 187, 112, 113, 114, 19, 20, 117,
+ /* 1440 */ 22, 187, 187, 187, 187, 95, 209, 210, 148, 149,
+ /* 1450 */ 150, 151, 152, 187, 36, 23, 187, 187, 26, 187,
+ /* 1460 */ 187, 187, 187, 209, 210, 209, 210, 187, 187, 218,
+ /* 1470 */ 148, 149, 150, 151, 152, 209, 210, 59, 187, 129,
+ /* 1480 */ 187, 209, 210, 187, 134, 135, 187, 306, 187, 71,
+ /* 1490 */ 209, 210, 23, 228, 187, 26, 23, 187, 137, 26,
+ /* 1500 */ 209, 210, 209, 210, 187, 209, 210, 157, 209, 210,
+ /* 1510 */ 209, 210, 218, 187, 187, 97, 209, 210, 187, 278,
+ /* 1520 */ 23, 103, 104, 26, 187, 187, 187, 187, 110, 187,
+ /* 1530 */ 112, 113, 114, 187, 187, 117, 5, 247, 187, 187,
+ /* 1540 */ 187, 10, 11, 12, 13, 14, 209, 210, 17, 209,
+ /* 1550 */ 210, 209, 210, 187, 187, 187, 209, 210, 187, 23,
+ /* 1560 */ 187, 30, 26, 32, 187, 187, 148, 149, 150, 151,
+ /* 1570 */ 152, 40, 187, 187, 187, 209, 210, 209, 210, 187,
+ /* 1580 */ 209, 210, 209, 210, 187, 187, 209, 210, 187, 277,
+ /* 1590 */ 234, 187, 247, 187, 209, 210, 209, 210, 187, 235,
+ /* 1600 */ 187, 70, 187, 207, 247, 247, 247, 209, 210, 78,
+ /* 1610 */ 209, 210, 81, 209, 210, 209, 210, 187, 185, 238,
+ /* 1620 */ 209, 210, 209, 210, 209, 210, 95, 251, 287, 238,
+ /* 1630 */ 251, 23, 23, 23, 26, 26, 26, 283, 23, 209,
+ /* 1640 */ 210, 26, 213, 238, 221, 283, 212, 212, 217, 212,
+ /* 1650 */ 251, 241, 270, 241, 237, 235, 190, 60, 137, 287,
+ /* 1660 */ 129, 194, 194, 38, 284, 134, 135, 273, 284, 194,
+ /* 1670 */ 146, 111, 22, 43, 226, 145, 262, 261, 238, 18,
+ /* 1680 */ 229, 229, 229, 229, 194, 18, 193, 238, 157, 262,
+ /* 1690 */ 226, 226, 194, 238, 238, 193, 153, 261, 62, 280,
+ /* 1700 */ 279, 194, 193, 22, 194, 214, 193, 111, 194, 193,
+ /* 1710 */ 214, 211, 211, 64, 211, 211, 122, 211, 219, 214,
+ /* 1720 */ 109, 213, 160, 211, 300, 140, 211, 253, 111, 272,
+ /* 1730 */ 219, 214, 272, 214, 252, 194, 253, 252, 91, 253,
+ /* 1740 */ 252, 82, 253, 305, 252, 144, 305, 141, 22, 194,
+ /* 1750 */ 269, 257, 257, 153, 267, 143, 142, 25, 197, 26,
+ /* 1760 */ 242, 241, 196, 240, 242, 239, 238, 13, 188, 188,
+ /* 1770 */ 6, 293, 186, 186, 186, 200, 206, 293, 206, 206,
+ /* 1780 */ 206, 4, 200, 215, 215, 207, 207, 3, 22, 206,
+ /* 1790 */ 200, 158, 96, 15, 23, 16, 23, 135, 146, 126,
+ /* 1800 */ 24, 138, 20, 16, 140, 1, 138, 147, 126, 61,
+ /* 1810 */ 53, 37, 146, 53, 53, 290, 53, 126, 112, 34,
+ /* 1820 */ 137, 1, 5, 22, 111, 156, 68, 68, 26, 41,
+ /* 1830 */ 75, 137, 111, 24, 20, 19, 127, 121, 23, 67,
+ /* 1840 */ 22, 22, 67, 22, 22, 37, 22, 67, 23, 145,
+ /* 1850 */ 22, 28, 23, 23, 23, 137, 23, 22, 26, 22,
+ /* 1860 */ 24, 23, 112, 24, 23, 23, 22, 139, 34, 26,
+ /* 1870 */ 75, 88, 86, 75, 34, 23, 22, 24, 22, 34,
+ /* 1880 */ 34, 34, 93, 34, 26, 26, 23, 23, 23, 34,
+ /* 1890 */ 23, 23, 44, 23, 11, 26, 22, 22, 26, 23,
+ /* 1900 */ 23, 22, 22, 15, 137, 137, 137, 137, 23, 1,
+ /* 1910 */ 307, 307, 131, 307, 307, 307, 307, 307, 307, 307,
+ /* 1920 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 1930 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 1940 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 1950 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 1960 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 1970 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 1980 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 1990 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 2000 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 2010 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 2020 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 2030 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 2040 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 2050 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 2060 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 2070 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 2080 */ 307, 307, 307, 307, 307, 307, 307, 307, 307, 307,
+ /* 2090 */ 307, 307,
};
-#define YY_SHIFT_COUNT (539)
+#define YY_SHIFT_COUNT (542)
#define YY_SHIFT_MIN (0)
-#define YY_SHIFT_MAX (1987)
+#define YY_SHIFT_MAX (1908)
static const unsigned short int yy_shift_ofst[] = {
- /* 0 */ 1814, 1632, 1987, 1426, 1426, 382, 1482, 1633, 1703, 1877,
- /* 10 */ 1877, 1877, 85, 0, 0, 264, 1106, 1877, 1877, 1877,
- /* 20 */ 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877,
- /* 30 */ 226, 226, 380, 380, 294, 667, 382, 382, 382, 382,
- /* 40 */ 382, 382, 97, 194, 332, 429, 526, 623, 720, 817,
- /* 50 */ 914, 934, 1086, 1238, 1106, 1106, 1106, 1106, 1106, 1106,
- /* 60 */ 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106, 1106,
- /* 70 */ 1106, 1106, 1258, 1106, 1355, 1375, 1375, 1817, 1877, 1877,
- /* 80 */ 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877,
- /* 90 */ 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877,
- /* 100 */ 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877,
- /* 110 */ 1937, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877,
- /* 120 */ 1877, 1877, 1877, 1877, 32, 129, 129, 129, 129, 129,
- /* 130 */ 171, 7, 17, 593, 676, 590, 593, 205, 205, 593,
- /* 140 */ 318, 318, 318, 318, 50, 152, 51, 2142, 2142, 284,
- /* 150 */ 284, 284, 65, 145, 282, 145, 145, 574, 574, 256,
- /* 160 */ 348, 445, 782, 593, 593, 593, 593, 593, 593, 593,
- /* 170 */ 593, 593, 593, 593, 593, 593, 593, 593, 593, 593,
- /* 180 */ 593, 593, 593, 593, 607, 607, 593, 721, 805, 805,
- /* 190 */ 446, 851, 851, 446, 190, 979, 2142, 2142, 2142, 453,
- /* 200 */ 45, 45, 480, 490, 484, 385, 575, 502, 551, 581,
- /* 210 */ 593, 593, 593, 593, 593, 593, 593, 593, 593, 689,
- /* 220 */ 593, 593, 593, 593, 593, 593, 593, 593, 593, 593,
- /* 230 */ 593, 593, 582, 582, 582, 593, 593, 593, 593, 771,
- /* 240 */ 593, 593, 593, 59, 764, 593, 593, 863, 593, 593,
- /* 250 */ 593, 593, 593, 593, 593, 593, 665, 819, 580, 16,
- /* 260 */ 16, 16, 16, 1119, 580, 580, 967, 321, 931, 1042,
- /* 270 */ 1077, 783, 783, 834, 1077, 1077, 834, 1121, 1195, 401,
- /* 280 */ 1142, 1142, 1142, 783, 787, 787, 1074, 1191, 1092, 1205,
- /* 290 */ 1354, 1284, 1284, 1381, 1381, 1284, 1297, 1334, 1421, 1407,
- /* 300 */ 1322, 1447, 1447, 1447, 1447, 1284, 1452, 1322, 1322, 1334,
- /* 310 */ 1421, 1407, 1407, 1322, 1284, 1452, 1345, 1434, 1284, 1452,
- /* 320 */ 1483, 1284, 1452, 1284, 1452, 1483, 1404, 1404, 1404, 1451,
- /* 330 */ 1483, 1404, 1400, 1404, 1451, 1404, 1404, 1483, 1419, 1419,
- /* 340 */ 1483, 1406, 1436, 1406, 1436, 1406, 1436, 1406, 1436, 1284,
- /* 350 */ 1457, 1457, 1408, 1414, 1534, 1284, 1412, 1408, 1425, 1431,
- /* 360 */ 1322, 1555, 1559, 1574, 1574, 1588, 1588, 1588, 2142, 2142,
- /* 370 */ 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142,
- /* 380 */ 2142, 2142, 2142, 378, 777, 836, 971, 825, 775, 983,
- /* 390 */ 1208, 1179, 1217, 1120, 1220, 1206, 1221, 1222, 1226, 1227,
- /* 400 */ 1228, 1233, 937, 1147, 1261, 1149, 1207, 1248, 1249, 1253,
- /* 410 */ 1133, 1151, 1274, 1293, 1211, 1236, 1605, 1607, 1589, 1458,
- /* 420 */ 1599, 1525, 1604, 1600, 1602, 1490, 1492, 1503, 1612, 1504,
- /* 430 */ 1618, 1510, 1624, 1640, 1513, 1506, 1528, 1593, 1619, 1514,
- /* 440 */ 1606, 1608, 1610, 1613, 1535, 1551, 1631, 1533, 1667, 1665,
- /* 450 */ 1649, 1565, 1521, 1609, 1650, 1611, 1603, 1634, 1546, 1573,
- /* 460 */ 1658, 1663, 1666, 1560, 1568, 1668, 1620, 1669, 1671, 1672,
- /* 470 */ 1674, 1621, 1661, 1675, 1627, 1662, 1677, 1556, 1679, 1680,
- /* 480 */ 1548, 1683, 1684, 1682, 1686, 1689, 1688, 1691, 1694, 1693,
- /* 490 */ 1584, 1696, 1698, 1617, 1695, 1706, 1594, 1705, 1701, 1702,
- /* 500 */ 1704, 1707, 1647, 1670, 1654, 1708, 1673, 1656, 1715, 1727,
- /* 510 */ 1729, 1730, 1731, 1733, 1719, 1732, 1705, 1737, 1738, 1742,
- /* 520 */ 1743, 1741, 1745, 1734, 1758, 1748, 1749, 1750, 1752, 1754,
- /* 530 */ 1755, 1746, 1655, 1653, 1657, 1659, 1660, 1761, 1773, 1788,
+ /* 0 */ 1350, 1149, 1531, 939, 939, 548, 996, 1150, 1236, 1322,
+ /* 10 */ 1322, 1322, 334, 0, 0, 178, 777, 1322, 1322, 1322,
+ /* 20 */ 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
+ /* 30 */ 991, 991, 1125, 1125, 447, 597, 548, 548, 548, 548,
+ /* 40 */ 548, 548, 40, 108, 217, 284, 323, 390, 429, 496,
+ /* 50 */ 535, 602, 641, 757, 777, 777, 777, 777, 777, 777,
+ /* 60 */ 777, 777, 777, 777, 777, 777, 777, 777, 777, 777,
+ /* 70 */ 777, 777, 796, 777, 887, 900, 900, 1300, 1322, 1322,
+ /* 80 */ 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
+ /* 90 */ 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
+ /* 100 */ 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
+ /* 110 */ 1418, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
+ /* 120 */ 1322, 1322, 1322, 1322, 147, 254, 254, 254, 254, 254,
+ /* 130 */ 84, 185, 66, 853, 958, 1121, 853, 92, 92, 853,
+ /* 140 */ 321, 321, 321, 321, 325, 350, 350, 461, 150, 1913,
+ /* 150 */ 1913, 285, 285, 285, 236, 184, 349, 184, 184, 712,
+ /* 160 */ 712, 433, 553, 771, 899, 853, 853, 853, 853, 853,
+ /* 170 */ 853, 853, 853, 853, 853, 853, 853, 853, 853, 853,
+ /* 180 */ 853, 853, 853, 853, 853, 853, 46, 46, 853, 113,
+ /* 190 */ 223, 223, 1183, 1183, 1127, 1142, 1913, 1913, 1913, 459,
+ /* 200 */ 514, 514, 653, 495, 657, 305, 705, 560, 622, 776,
+ /* 210 */ 853, 853, 853, 853, 853, 853, 853, 853, 853, 545,
+ /* 220 */ 853, 853, 853, 853, 853, 853, 853, 853, 853, 853,
+ /* 230 */ 853, 853, 1002, 1002, 1002, 853, 853, 853, 853, 1111,
+ /* 240 */ 853, 853, 853, 1006, 1109, 853, 853, 1168, 853, 853,
+ /* 250 */ 853, 853, 853, 853, 853, 853, 845, 1164, 738, 953,
+ /* 260 */ 953, 953, 953, 1196, 738, 738, 45, 96, 964, 179,
+ /* 270 */ 580, 907, 907, 1073, 580, 580, 1073, 498, 388, 1268,
+ /* 280 */ 1187, 1187, 1187, 907, 1170, 1170, 1058, 1180, 328, 1219,
+ /* 290 */ 1597, 1521, 1521, 1625, 1625, 1521, 1524, 1560, 1650, 1630,
+ /* 300 */ 1530, 1661, 1661, 1661, 1661, 1521, 1667, 1530, 1530, 1560,
+ /* 310 */ 1650, 1630, 1630, 1530, 1521, 1667, 1543, 1636, 1521, 1667,
+ /* 320 */ 1681, 1521, 1667, 1521, 1667, 1681, 1596, 1596, 1596, 1649,
+ /* 330 */ 1681, 1596, 1594, 1596, 1649, 1596, 1596, 1562, 1681, 1611,
+ /* 340 */ 1611, 1681, 1585, 1617, 1585, 1617, 1585, 1617, 1585, 1617,
+ /* 350 */ 1521, 1647, 1647, 1659, 1659, 1601, 1606, 1726, 1521, 1600,
+ /* 360 */ 1601, 1612, 1614, 1530, 1732, 1733, 1754, 1754, 1764, 1764,
+ /* 370 */ 1764, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913,
+ /* 380 */ 1913, 1913, 1913, 1913, 1913, 1913, 673, 901, 283, 740,
+ /* 390 */ 707, 973, 655, 1247, 1048, 1097, 1190, 1306, 1263, 1383,
+ /* 400 */ 1395, 1432, 1469, 1473, 1497, 1279, 1200, 1323, 1075, 1286,
+ /* 410 */ 1536, 1608, 1332, 1609, 1175, 1225, 1610, 1615, 1309, 1361,
+ /* 420 */ 1777, 1784, 1766, 1633, 1778, 1696, 1779, 1771, 1773, 1662,
+ /* 430 */ 1652, 1673, 1776, 1663, 1782, 1664, 1787, 1804, 1668, 1660,
+ /* 440 */ 1682, 1748, 1774, 1666, 1757, 1760, 1761, 1763, 1691, 1706,
+ /* 450 */ 1785, 1683, 1820, 1817, 1801, 1713, 1669, 1758, 1802, 1759,
+ /* 460 */ 1755, 1788, 1694, 1721, 1809, 1814, 1816, 1709, 1716, 1818,
+ /* 470 */ 1772, 1819, 1821, 1815, 1822, 1775, 1823, 1824, 1780, 1808,
+ /* 480 */ 1825, 1704, 1828, 1829, 1830, 1831, 1832, 1833, 1835, 1836,
+ /* 490 */ 1838, 1837, 1839, 1718, 1841, 1842, 1750, 1834, 1844, 1728,
+ /* 500 */ 1843, 1840, 1845, 1846, 1847, 1783, 1795, 1786, 1848, 1798,
+ /* 510 */ 1789, 1849, 1852, 1854, 1853, 1858, 1859, 1855, 1863, 1843,
+ /* 520 */ 1864, 1865, 1867, 1868, 1869, 1870, 1856, 1883, 1874, 1875,
+ /* 530 */ 1876, 1877, 1879, 1880, 1872, 1781, 1767, 1768, 1769, 1770,
+ /* 540 */ 1885, 1888, 1908,
};
-#define YY_REDUCE_COUNT (382)
-#define YY_REDUCE_MIN (-260)
-#define YY_REDUCE_MAX (1420)
+#define YY_REDUCE_COUNT (385)
+#define YY_REDUCE_MIN (-256)
+#define YY_REDUCE_MAX (1590)
static const short yy_reduce_ofst[] = {
- /* 0 */ -170, -18, -159, 309, 313, -167, -19, 75, 117, 211,
- /* 10 */ 315, 317, -165, -195, -168, -260, 389, 437, 475, 524,
- /* 20 */ 527, -169, 529, 531, -28, 80, 534, 239, 304, 412,
- /* 30 */ 558, 577, 37, 120, 368, -22, 460, 517, 555, 560,
- /* 40 */ 562, 586, -257, -257, -257, -257, -257, -257, -257, -257,
- /* 50 */ -257, -257, -257, -257, -257, -257, -257, -257, -257, -257,
- /* 60 */ -257, -257, -257, -257, -257, -257, -257, -257, -257, -257,
- /* 70 */ -257, -257, -257, -257, -257, -257, -257, -172, 457, 628,
- /* 80 */ 673, 692, 694, 702, 704, 722, 728, 740, 743, 748,
- /* 90 */ 767, 791, 815, 818, 820, 822, 857, 861, 864, 866,
- /* 100 */ 868, 870, 872, 874, 876, 881, 900, 902, 906, 908,
- /* 110 */ 910, 912, 915, 917, 920, 960, 962, 964, 988, 990,
- /* 120 */ 992, 1016, 1029, 1032, -257, -257, -257, -257, -257, -257,
- /* 130 */ -257, -257, -257, 271, 618, -190, 68, 60, 240, -124,
- /* 140 */ 603, 610, 603, 610, 12, -257, -257, -257, -257, -128,
- /* 150 */ -128, -128, -142, 25, 270, 281, 333, 124, 236, 648,
- /* 160 */ 374, 465, 465, 28, 598, 792, 839, 469, 38, 381,
- /* 170 */ 622, 709, 173, 699, 522, 703, 808, 811, 867, 816,
- /* 180 */ -104, 823, -3, 875, 649, 753, 323, -88, 882, 884,
- /* 190 */ 518, 43, 325, 899, 763, 604, 879, 969, 402, -193,
- /* 200 */ -189, -180, -151, -55, 69, 104, 141, 259, 286, 360,
- /* 210 */ 364, 455, 474, 481, 510, 516, 611, 653, 788, 99,
- /* 220 */ 871, 878, 995, 1009, 1049, 1081, 1115, 1134, 1136, 1138,
- /* 230 */ 1139, 1140, 733, 1110, 1112, 1144, 1145, 1146, 1148, 1084,
- /* 240 */ 1161, 1162, 1163, 1089, 1047, 1165, 1166, 1127, 1169, 104,
- /* 250 */ 1181, 1182, 1183, 1184, 1185, 1187, 1098, 1100, 1150, 1131,
- /* 260 */ 1132, 1135, 1141, 1084, 1150, 1150, 1152, 1173, 1196, 1097,
- /* 270 */ 1153, 1143, 1167, 1103, 1154, 1155, 1104, 1176, 1174, 1199,
- /* 280 */ 1178, 1186, 1188, 1168, 1158, 1160, 1170, 1159, 1201, 1230,
- /* 290 */ 1156, 1243, 1244, 1157, 1164, 1247, 1172, 1189, 1192, 1240,
- /* 300 */ 1231, 1241, 1242, 1256, 1257, 1278, 1294, 1251, 1252, 1232,
- /* 310 */ 1234, 1265, 1266, 1259, 1301, 1303, 1223, 1225, 1307, 1313,
- /* 320 */ 1295, 1317, 1319, 1320, 1323, 1299, 1306, 1308, 1309, 1305,
- /* 330 */ 1311, 1315, 1314, 1321, 1312, 1324, 1326, 1327, 1263, 1271,
- /* 340 */ 1331, 1296, 1298, 1300, 1302, 1304, 1310, 1316, 1318, 1357,
- /* 350 */ 1255, 1260, 1329, 1325, 1332, 1370, 1333, 1338, 1341, 1343,
- /* 360 */ 1346, 1390, 1393, 1403, 1405, 1416, 1417, 1418, 1328, 1330,
- /* 370 */ 1335, 1409, 1394, 1399, 1401, 1402, 1410, 1391, 1396, 1411,
- /* 380 */ 1420, 1413, 1415,
+ /* 0 */ 880, -121, 269, 528, 933, -119, -187, -185, -182, -180,
+ /* 10 */ -176, -174, -62, -46, 131, -248, -133, 407, 568, 700,
+ /* 20 */ 704, 278, 706, 824, 542, 830, 948, 773, 943, 946,
+ /* 30 */ 71, 650, 211, 267, 826, 272, 676, 732, 885, 976,
+ /* 40 */ 984, 992, -256, -256, -256, -256, -256, -256, -256, -256,
+ /* 50 */ -256, -256, -256, -256, -256, -256, -256, -256, -256, -256,
+ /* 60 */ -256, -256, -256, -256, -256, -256, -256, -256, -256, -256,
+ /* 70 */ -256, -256, -256, -256, -256, -256, -256, 989, 1065, 1070,
+ /* 80 */ 1072, 1078, 1082, 1084, 1103, 1118, 1147, 1156, 1160, 1167,
+ /* 90 */ 1185, 1191, 1220, 1237, 1254, 1256, 1266, 1272, 1281, 1291,
+ /* 100 */ 1293, 1296, 1299, 1301, 1307, 1337, 1340, 1342, 1347, 1366,
+ /* 110 */ 1368, 1371, 1373, 1377, 1385, 1387, 1398, 1401, 1404, 1406,
+ /* 120 */ 1411, 1413, 1415, 1430, -256, -256, -256, -256, -256, -256,
+ /* 130 */ -256, -256, -256, -172, 508, -213, 57, -163, -25, 593,
+ /* 140 */ 69, 486, 69, 486, -200, 573, 722, -256, -256, -256,
+ /* 150 */ -256, -141, -141, -141, -105, -161, -167, 157, 212, 405,
+ /* 160 */ 530, 220, 233, 735, 735, 115, 318, 406, 612, 541,
+ /* 170 */ -166, 441, 688, 794, 629, 368, 741, 775, 867, 797,
+ /* 180 */ 871, 842, -186, 1000, 858, 949, 379, 783, 70, 296,
+ /* 190 */ 821, 903, 924, 1044, 651, 282, 1014, 1060, 937, -195,
+ /* 200 */ -177, 413, 439, 511, 566, 787, 827, 848, 898, 945,
+ /* 210 */ 1062, 1074, 1102, 1110, 1202, 1204, 1209, 1211, 1215, 529,
+ /* 220 */ 1221, 1224, 1240, 1246, 1255, 1257, 1269, 1270, 1273, 1274,
+ /* 230 */ 1275, 1280, 1205, 1251, 1294, 1310, 1317, 1326, 1327, 1124,
+ /* 240 */ 1331, 1338, 1339, 1290, 1181, 1346, 1351, 1265, 1352, 787,
+ /* 250 */ 1353, 1367, 1378, 1386, 1392, 1397, 1241, 1312, 1356, 1345,
+ /* 260 */ 1357, 1358, 1359, 1124, 1356, 1356, 1364, 1396, 1433, 1341,
+ /* 270 */ 1381, 1376, 1379, 1354, 1391, 1405, 1362, 1429, 1423, 1431,
+ /* 280 */ 1434, 1435, 1437, 1399, 1410, 1412, 1382, 1417, 1420, 1466,
+ /* 290 */ 1372, 1467, 1468, 1380, 1384, 1475, 1394, 1414, 1416, 1448,
+ /* 300 */ 1440, 1451, 1452, 1453, 1454, 1490, 1493, 1449, 1455, 1427,
+ /* 310 */ 1436, 1464, 1465, 1456, 1498, 1502, 1419, 1421, 1507, 1509,
+ /* 320 */ 1491, 1510, 1513, 1514, 1516, 1496, 1500, 1501, 1503, 1499,
+ /* 330 */ 1505, 1504, 1508, 1506, 1511, 1512, 1515, 1424, 1517, 1457,
+ /* 340 */ 1460, 1519, 1474, 1482, 1483, 1485, 1486, 1488, 1489, 1492,
+ /* 350 */ 1541, 1438, 1441, 1494, 1495, 1518, 1520, 1487, 1555, 1481,
+ /* 360 */ 1522, 1523, 1526, 1528, 1561, 1566, 1580, 1581, 1586, 1587,
+ /* 370 */ 1588, 1478, 1484, 1525, 1575, 1570, 1572, 1573, 1574, 1582,
+ /* 380 */ 1568, 1569, 1578, 1579, 1583, 1590,
};
static const YYACTIONTYPE yy_default[] = {
- /* 0 */ 1537, 1537, 1537, 1377, 1159, 1266, 1159, 1159, 1159, 1377,
- /* 10 */ 1377, 1377, 1159, 1296, 1296, 1430, 1190, 1159, 1159, 1159,
- /* 20 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1376, 1159, 1159,
- /* 30 */ 1159, 1159, 1460, 1460, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 40 */ 1159, 1159, 1159, 1302, 1159, 1159, 1159, 1159, 1159, 1378,
- /* 50 */ 1379, 1159, 1159, 1159, 1429, 1431, 1394, 1312, 1311, 1310,
- /* 60 */ 1309, 1412, 1283, 1307, 1300, 1304, 1372, 1373, 1371, 1375,
- /* 70 */ 1379, 1378, 1159, 1303, 1343, 1357, 1342, 1159, 1159, 1159,
- /* 80 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 90 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 100 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 110 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 120 */ 1159, 1159, 1159, 1159, 1351, 1356, 1362, 1355, 1352, 1345,
- /* 130 */ 1344, 1346, 1347, 1159, 1180, 1230, 1159, 1159, 1159, 1159,
- /* 140 */ 1448, 1447, 1159, 1159, 1190, 1348, 1349, 1359, 1358, 1437,
- /* 150 */ 1493, 1492, 1395, 1159, 1159, 1159, 1159, 1159, 1159, 1460,
- /* 160 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 170 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 180 */ 1159, 1159, 1159, 1159, 1460, 1460, 1159, 1190, 1460, 1460,
- /* 190 */ 1186, 1337, 1336, 1186, 1290, 1159, 1443, 1266, 1257, 1159,
- /* 200 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 210 */ 1159, 1159, 1159, 1434, 1432, 1159, 1159, 1159, 1159, 1159,
- /* 220 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 230 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 240 */ 1159, 1159, 1159, 1262, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 250 */ 1159, 1159, 1159, 1159, 1159, 1487, 1159, 1407, 1244, 1262,
- /* 260 */ 1262, 1262, 1262, 1264, 1245, 1243, 1256, 1191, 1166, 1529,
- /* 270 */ 1306, 1285, 1285, 1526, 1306, 1306, 1526, 1205, 1507, 1202,
- /* 280 */ 1296, 1296, 1296, 1285, 1290, 1290, 1374, 1263, 1256, 1159,
- /* 290 */ 1529, 1271, 1271, 1528, 1528, 1271, 1395, 1315, 1321, 1233,
- /* 300 */ 1306, 1239, 1239, 1239, 1239, 1271, 1177, 1306, 1306, 1315,
- /* 310 */ 1321, 1233, 1233, 1306, 1271, 1177, 1411, 1523, 1271, 1177,
- /* 320 */ 1385, 1271, 1177, 1271, 1177, 1385, 1231, 1231, 1231, 1220,
- /* 330 */ 1385, 1231, 1205, 1231, 1220, 1231, 1231, 1385, 1389, 1389,
- /* 340 */ 1385, 1289, 1284, 1289, 1284, 1289, 1284, 1289, 1284, 1271,
- /* 350 */ 1470, 1470, 1301, 1290, 1380, 1271, 1159, 1301, 1299, 1297,
- /* 360 */ 1306, 1183, 1223, 1490, 1490, 1486, 1486, 1486, 1534, 1534,
- /* 370 */ 1443, 1502, 1190, 1190, 1190, 1190, 1502, 1207, 1207, 1191,
- /* 380 */ 1191, 1190, 1502, 1159, 1159, 1159, 1159, 1159, 1159, 1497,
- /* 390 */ 1159, 1396, 1275, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 400 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 410 */ 1159, 1159, 1159, 1159, 1159, 1326, 1159, 1162, 1440, 1159,
- /* 420 */ 1159, 1438, 1159, 1159, 1159, 1159, 1159, 1159, 1276, 1159,
- /* 430 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 440 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1525, 1159, 1159,
- /* 450 */ 1159, 1159, 1159, 1159, 1410, 1409, 1159, 1159, 1273, 1159,
- /* 460 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 470 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 480 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 490 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1298, 1159, 1159,
- /* 500 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 510 */ 1159, 1159, 1475, 1291, 1159, 1159, 1516, 1159, 1159, 1159,
- /* 520 */ 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159, 1159,
- /* 530 */ 1159, 1511, 1247, 1328, 1159, 1327, 1331, 1159, 1171, 1159,
+ /* 0 */ 1554, 1554, 1554, 1392, 1171, 1278, 1171, 1171, 1171, 1392,
+ /* 10 */ 1392, 1392, 1171, 1308, 1308, 1445, 1202, 1171, 1171, 1171,
+ /* 20 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1391, 1171, 1171,
+ /* 30 */ 1171, 1171, 1475, 1475, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 40 */ 1171, 1171, 1171, 1317, 1171, 1171, 1171, 1171, 1171, 1393,
+ /* 50 */ 1394, 1171, 1171, 1171, 1444, 1446, 1409, 1327, 1326, 1325,
+ /* 60 */ 1324, 1427, 1295, 1322, 1315, 1319, 1387, 1388, 1386, 1390,
+ /* 70 */ 1394, 1393, 1171, 1318, 1358, 1372, 1357, 1171, 1171, 1171,
+ /* 80 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 90 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 100 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 110 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 120 */ 1171, 1171, 1171, 1171, 1366, 1371, 1377, 1370, 1367, 1360,
+ /* 130 */ 1359, 1361, 1362, 1171, 1192, 1242, 1171, 1171, 1171, 1171,
+ /* 140 */ 1463, 1462, 1171, 1171, 1202, 1352, 1351, 1363, 1364, 1374,
+ /* 150 */ 1373, 1452, 1510, 1509, 1410, 1171, 1171, 1171, 1171, 1171,
+ /* 160 */ 1171, 1475, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 170 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 180 */ 1171, 1171, 1171, 1171, 1171, 1171, 1475, 1475, 1171, 1202,
+ /* 190 */ 1475, 1475, 1198, 1198, 1302, 1171, 1458, 1278, 1269, 1171,
+ /* 200 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 210 */ 1171, 1171, 1171, 1449, 1447, 1171, 1171, 1171, 1171, 1171,
+ /* 220 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 230 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 240 */ 1171, 1171, 1171, 1274, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 250 */ 1171, 1171, 1171, 1171, 1171, 1504, 1171, 1422, 1256, 1274,
+ /* 260 */ 1274, 1274, 1274, 1276, 1257, 1255, 1268, 1203, 1178, 1546,
+ /* 270 */ 1321, 1297, 1297, 1543, 1321, 1321, 1543, 1217, 1524, 1214,
+ /* 280 */ 1308, 1308, 1308, 1297, 1302, 1302, 1389, 1275, 1268, 1171,
+ /* 290 */ 1546, 1283, 1283, 1545, 1545, 1283, 1410, 1330, 1336, 1245,
+ /* 300 */ 1321, 1251, 1251, 1251, 1251, 1283, 1189, 1321, 1321, 1330,
+ /* 310 */ 1336, 1245, 1245, 1321, 1283, 1189, 1426, 1540, 1283, 1189,
+ /* 320 */ 1400, 1283, 1189, 1283, 1189, 1400, 1243, 1243, 1243, 1232,
+ /* 330 */ 1400, 1243, 1217, 1243, 1232, 1243, 1243, 1493, 1400, 1404,
+ /* 340 */ 1404, 1400, 1301, 1296, 1301, 1296, 1301, 1296, 1301, 1296,
+ /* 350 */ 1283, 1485, 1485, 1311, 1311, 1316, 1302, 1395, 1283, 1171,
+ /* 360 */ 1316, 1314, 1312, 1321, 1195, 1235, 1507, 1507, 1503, 1503,
+ /* 370 */ 1503, 1551, 1551, 1458, 1519, 1202, 1202, 1202, 1202, 1519,
+ /* 380 */ 1219, 1219, 1203, 1203, 1202, 1519, 1171, 1171, 1171, 1171,
+ /* 390 */ 1171, 1171, 1514, 1171, 1411, 1287, 1171, 1171, 1171, 1171,
+ /* 400 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 410 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1341,
+ /* 420 */ 1171, 1174, 1455, 1171, 1171, 1453, 1171, 1171, 1171, 1171,
+ /* 430 */ 1171, 1171, 1288, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 440 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 450 */ 1171, 1542, 1171, 1171, 1171, 1171, 1171, 1171, 1425, 1424,
+ /* 460 */ 1171, 1171, 1285, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 470 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 480 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 490 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 500 */ 1313, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 510 */ 1171, 1171, 1171, 1171, 1171, 1490, 1303, 1171, 1171, 1533,
+ /* 520 */ 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171, 1171,
+ /* 530 */ 1171, 1171, 1171, 1171, 1528, 1259, 1343, 1171, 1342, 1346,
+ /* 540 */ 1171, 1183, 1171,
};
/********** End of lemon-generated parsing tables *****************************/
@@ -149754,6 +150692,9 @@ static const YYCODETYPE yyFallback[] = {
59, /* VIEW => ID */
59, /* VIRTUAL => ID */
59, /* WITH => ID */
+ 59, /* NULLS => ID */
+ 59, /* FIRST => ID */
+ 59, /* LAST => ID */
59, /* CURRENT => ID */
59, /* FOLLOWING => ID */
59, /* PARTITION => ID */
@@ -149767,6 +150708,87 @@ static const YYCODETYPE yyFallback[] = {
59, /* REINDEX => ID */
59, /* RENAME => ID */
59, /* CTIME_KW => ID */
+ 0, /* ANY => nothing */
+ 0, /* BITAND => nothing */
+ 0, /* BITOR => nothing */
+ 0, /* LSHIFT => nothing */
+ 0, /* RSHIFT => nothing */
+ 0, /* PLUS => nothing */
+ 0, /* MINUS => nothing */
+ 0, /* STAR => nothing */
+ 0, /* SLASH => nothing */
+ 0, /* REM => nothing */
+ 0, /* CONCAT => nothing */
+ 0, /* COLLATE => nothing */
+ 0, /* BITNOT => nothing */
+ 0, /* ON => nothing */
+ 0, /* INDEXED => nothing */
+ 0, /* STRING => nothing */
+ 0, /* JOIN_KW => nothing */
+ 0, /* CONSTRAINT => nothing */
+ 0, /* DEFAULT => nothing */
+ 0, /* NULL => nothing */
+ 0, /* PRIMARY => nothing */
+ 0, /* UNIQUE => nothing */
+ 0, /* CHECK => nothing */
+ 0, /* REFERENCES => nothing */
+ 0, /* AUTOINCR => nothing */
+ 0, /* INSERT => nothing */
+ 0, /* DELETE => nothing */
+ 0, /* UPDATE => nothing */
+ 0, /* SET => nothing */
+ 0, /* DEFERRABLE => nothing */
+ 0, /* FOREIGN => nothing */
+ 0, /* DROP => nothing */
+ 0, /* UNION => nothing */
+ 0, /* ALL => nothing */
+ 0, /* EXCEPT => nothing */
+ 0, /* INTERSECT => nothing */
+ 0, /* SELECT => nothing */
+ 0, /* VALUES => nothing */
+ 0, /* DISTINCT => nothing */
+ 0, /* DOT => nothing */
+ 0, /* FROM => nothing */
+ 0, /* JOIN => nothing */
+ 0, /* USING => nothing */
+ 0, /* ORDER => nothing */
+ 0, /* GROUP => nothing */
+ 0, /* HAVING => nothing */
+ 0, /* LIMIT => nothing */
+ 0, /* WHERE => nothing */
+ 0, /* INTO => nothing */
+ 0, /* NOTHING => nothing */
+ 0, /* FLOAT => nothing */
+ 0, /* BLOB => nothing */
+ 0, /* INTEGER => nothing */
+ 0, /* VARIABLE => nothing */
+ 0, /* CASE => nothing */
+ 0, /* WHEN => nothing */
+ 0, /* THEN => nothing */
+ 0, /* ELSE => nothing */
+ 0, /* INDEX => nothing */
+ 0, /* ALTER => nothing */
+ 0, /* ADD => nothing */
+ 0, /* WINDOW => nothing */
+ 0, /* OVER => nothing */
+ 0, /* FILTER => nothing */
+ 0, /* COLUMN => nothing */
+ 0, /* AGG_FUNCTION => nothing */
+ 0, /* AGG_COLUMN => nothing */
+ 0, /* TRUEFALSE => nothing */
+ 0, /* ISNOT => nothing */
+ 0, /* FUNCTION => nothing */
+ 0, /* UMINUS => nothing */
+ 0, /* UPLUS => nothing */
+ 0, /* TRUTH => nothing */
+ 0, /* REGISTER => nothing */
+ 0, /* VECTOR => nothing */
+ 0, /* SELECT_COLUMN => nothing */
+ 0, /* IF_NULL_ROW => nothing */
+ 0, /* ASTERISK => nothing */
+ 0, /* SPAN => nothing */
+ 0, /* SPACE => nothing */
+ 0, /* ILLEGAL => nothing */
};
#endif /* YYFALLBACK */
@@ -149936,226 +150958,231 @@ static const char *const yyTokenName[] = {
/* 79 */ "VIEW",
/* 80 */ "VIRTUAL",
/* 81 */ "WITH",
- /* 82 */ "CURRENT",
- /* 83 */ "FOLLOWING",
- /* 84 */ "PARTITION",
- /* 85 */ "PRECEDING",
- /* 86 */ "RANGE",
- /* 87 */ "UNBOUNDED",
- /* 88 */ "EXCLUDE",
- /* 89 */ "GROUPS",
- /* 90 */ "OTHERS",
- /* 91 */ "TIES",
- /* 92 */ "REINDEX",
- /* 93 */ "RENAME",
- /* 94 */ "CTIME_KW",
- /* 95 */ "ANY",
- /* 96 */ "BITAND",
- /* 97 */ "BITOR",
- /* 98 */ "LSHIFT",
- /* 99 */ "RSHIFT",
- /* 100 */ "PLUS",
- /* 101 */ "MINUS",
- /* 102 */ "STAR",
- /* 103 */ "SLASH",
- /* 104 */ "REM",
- /* 105 */ "CONCAT",
- /* 106 */ "COLLATE",
- /* 107 */ "BITNOT",
- /* 108 */ "ON",
- /* 109 */ "INDEXED",
- /* 110 */ "STRING",
- /* 111 */ "JOIN_KW",
- /* 112 */ "CONSTRAINT",
- /* 113 */ "DEFAULT",
- /* 114 */ "NULL",
- /* 115 */ "PRIMARY",
- /* 116 */ "UNIQUE",
- /* 117 */ "CHECK",
- /* 118 */ "REFERENCES",
- /* 119 */ "AUTOINCR",
- /* 120 */ "INSERT",
- /* 121 */ "DELETE",
- /* 122 */ "UPDATE",
- /* 123 */ "SET",
- /* 124 */ "DEFERRABLE",
- /* 125 */ "FOREIGN",
- /* 126 */ "DROP",
- /* 127 */ "UNION",
- /* 128 */ "ALL",
- /* 129 */ "EXCEPT",
- /* 130 */ "INTERSECT",
- /* 131 */ "SELECT",
- /* 132 */ "VALUES",
- /* 133 */ "DISTINCT",
- /* 134 */ "DOT",
- /* 135 */ "FROM",
- /* 136 */ "JOIN",
- /* 137 */ "USING",
- /* 138 */ "ORDER",
- /* 139 */ "GROUP",
- /* 140 */ "HAVING",
- /* 141 */ "LIMIT",
- /* 142 */ "WHERE",
- /* 143 */ "INTO",
- /* 144 */ "NOTHING",
- /* 145 */ "FLOAT",
- /* 146 */ "BLOB",
- /* 147 */ "INTEGER",
- /* 148 */ "VARIABLE",
- /* 149 */ "CASE",
- /* 150 */ "WHEN",
- /* 151 */ "THEN",
- /* 152 */ "ELSE",
- /* 153 */ "INDEX",
- /* 154 */ "ALTER",
- /* 155 */ "ADD",
- /* 156 */ "WINDOW",
- /* 157 */ "OVER",
- /* 158 */ "FILTER",
- /* 159 */ "TRUEFALSE",
- /* 160 */ "ISNOT",
- /* 161 */ "FUNCTION",
+ /* 82 */ "NULLS",
+ /* 83 */ "FIRST",
+ /* 84 */ "LAST",
+ /* 85 */ "CURRENT",
+ /* 86 */ "FOLLOWING",
+ /* 87 */ "PARTITION",
+ /* 88 */ "PRECEDING",
+ /* 89 */ "RANGE",
+ /* 90 */ "UNBOUNDED",
+ /* 91 */ "EXCLUDE",
+ /* 92 */ "GROUPS",
+ /* 93 */ "OTHERS",
+ /* 94 */ "TIES",
+ /* 95 */ "REINDEX",
+ /* 96 */ "RENAME",
+ /* 97 */ "CTIME_KW",
+ /* 98 */ "ANY",
+ /* 99 */ "BITAND",
+ /* 100 */ "BITOR",
+ /* 101 */ "LSHIFT",
+ /* 102 */ "RSHIFT",
+ /* 103 */ "PLUS",
+ /* 104 */ "MINUS",
+ /* 105 */ "STAR",
+ /* 106 */ "SLASH",
+ /* 107 */ "REM",
+ /* 108 */ "CONCAT",
+ /* 109 */ "COLLATE",
+ /* 110 */ "BITNOT",
+ /* 111 */ "ON",
+ /* 112 */ "INDEXED",
+ /* 113 */ "STRING",
+ /* 114 */ "JOIN_KW",
+ /* 115 */ "CONSTRAINT",
+ /* 116 */ "DEFAULT",
+ /* 117 */ "NULL",
+ /* 118 */ "PRIMARY",
+ /* 119 */ "UNIQUE",
+ /* 120 */ "CHECK",
+ /* 121 */ "REFERENCES",
+ /* 122 */ "AUTOINCR",
+ /* 123 */ "INSERT",
+ /* 124 */ "DELETE",
+ /* 125 */ "UPDATE",
+ /* 126 */ "SET",
+ /* 127 */ "DEFERRABLE",
+ /* 128 */ "FOREIGN",
+ /* 129 */ "DROP",
+ /* 130 */ "UNION",
+ /* 131 */ "ALL",
+ /* 132 */ "EXCEPT",
+ /* 133 */ "INTERSECT",
+ /* 134 */ "SELECT",
+ /* 135 */ "VALUES",
+ /* 136 */ "DISTINCT",
+ /* 137 */ "DOT",
+ /* 138 */ "FROM",
+ /* 139 */ "JOIN",
+ /* 140 */ "USING",
+ /* 141 */ "ORDER",
+ /* 142 */ "GROUP",
+ /* 143 */ "HAVING",
+ /* 144 */ "LIMIT",
+ /* 145 */ "WHERE",
+ /* 146 */ "INTO",
+ /* 147 */ "NOTHING",
+ /* 148 */ "FLOAT",
+ /* 149 */ "BLOB",
+ /* 150 */ "INTEGER",
+ /* 151 */ "VARIABLE",
+ /* 152 */ "CASE",
+ /* 153 */ "WHEN",
+ /* 154 */ "THEN",
+ /* 155 */ "ELSE",
+ /* 156 */ "INDEX",
+ /* 157 */ "ALTER",
+ /* 158 */ "ADD",
+ /* 159 */ "WINDOW",
+ /* 160 */ "OVER",
+ /* 161 */ "FILTER",
/* 162 */ "COLUMN",
/* 163 */ "AGG_FUNCTION",
/* 164 */ "AGG_COLUMN",
- /* 165 */ "UMINUS",
- /* 166 */ "UPLUS",
- /* 167 */ "TRUTH",
- /* 168 */ "REGISTER",
- /* 169 */ "VECTOR",
- /* 170 */ "SELECT_COLUMN",
- /* 171 */ "IF_NULL_ROW",
- /* 172 */ "ASTERISK",
- /* 173 */ "SPAN",
- /* 174 */ "SPACE",
- /* 175 */ "ILLEGAL",
- /* 176 */ "input",
- /* 177 */ "cmdlist",
- /* 178 */ "ecmd",
- /* 179 */ "cmdx",
- /* 180 */ "explain",
- /* 181 */ "cmd",
- /* 182 */ "transtype",
- /* 183 */ "trans_opt",
- /* 184 */ "nm",
- /* 185 */ "savepoint_opt",
- /* 186 */ "create_table",
- /* 187 */ "create_table_args",
- /* 188 */ "createkw",
- /* 189 */ "temp",
- /* 190 */ "ifnotexists",
- /* 191 */ "dbnm",
- /* 192 */ "columnlist",
- /* 193 */ "conslist_opt",
- /* 194 */ "table_options",
- /* 195 */ "select",
- /* 196 */ "columnname",
- /* 197 */ "carglist",
- /* 198 */ "typetoken",
- /* 199 */ "typename",
- /* 200 */ "signed",
- /* 201 */ "plus_num",
- /* 202 */ "minus_num",
- /* 203 */ "scanpt",
- /* 204 */ "scantok",
- /* 205 */ "ccons",
- /* 206 */ "term",
- /* 207 */ "expr",
- /* 208 */ "onconf",
- /* 209 */ "sortorder",
- /* 210 */ "autoinc",
- /* 211 */ "eidlist_opt",
- /* 212 */ "refargs",
- /* 213 */ "defer_subclause",
- /* 214 */ "refarg",
- /* 215 */ "refact",
- /* 216 */ "init_deferred_pred_opt",
- /* 217 */ "conslist",
- /* 218 */ "tconscomma",
- /* 219 */ "tcons",
- /* 220 */ "sortlist",
- /* 221 */ "eidlist",
- /* 222 */ "defer_subclause_opt",
- /* 223 */ "orconf",
- /* 224 */ "resolvetype",
- /* 225 */ "raisetype",
- /* 226 */ "ifexists",
- /* 227 */ "fullname",
- /* 228 */ "selectnowith",
- /* 229 */ "oneselect",
- /* 230 */ "wqlist",
- /* 231 */ "multiselect_op",
- /* 232 */ "distinct",
- /* 233 */ "selcollist",
- /* 234 */ "from",
- /* 235 */ "where_opt",
- /* 236 */ "groupby_opt",
- /* 237 */ "having_opt",
- /* 238 */ "orderby_opt",
- /* 239 */ "limit_opt",
- /* 240 */ "window_clause",
- /* 241 */ "values",
- /* 242 */ "nexprlist",
- /* 243 */ "sclp",
- /* 244 */ "as",
- /* 245 */ "seltablist",
- /* 246 */ "stl_prefix",
- /* 247 */ "joinop",
- /* 248 */ "indexed_opt",
- /* 249 */ "on_opt",
- /* 250 */ "using_opt",
- /* 251 */ "exprlist",
- /* 252 */ "xfullname",
- /* 253 */ "idlist",
- /* 254 */ "with",
- /* 255 */ "setlist",
- /* 256 */ "insert_cmd",
- /* 257 */ "idlist_opt",
- /* 258 */ "upsert",
- /* 259 */ "over_clause",
- /* 260 */ "likeop",
- /* 261 */ "between_op",
- /* 262 */ "in_op",
- /* 263 */ "paren_exprlist",
- /* 264 */ "case_operand",
- /* 265 */ "case_exprlist",
- /* 266 */ "case_else",
- /* 267 */ "uniqueflag",
- /* 268 */ "collate",
- /* 269 */ "vinto",
- /* 270 */ "nmnum",
- /* 271 */ "trigger_decl",
- /* 272 */ "trigger_cmd_list",
- /* 273 */ "trigger_time",
- /* 274 */ "trigger_event",
- /* 275 */ "foreach_clause",
- /* 276 */ "when_clause",
- /* 277 */ "trigger_cmd",
- /* 278 */ "trnm",
- /* 279 */ "tridxby",
- /* 280 */ "database_kw_opt",
- /* 281 */ "key_opt",
- /* 282 */ "add_column_fullname",
- /* 283 */ "kwcolumn_opt",
- /* 284 */ "create_vtab",
- /* 285 */ "vtabarglist",
- /* 286 */ "vtabarg",
- /* 287 */ "vtabargtoken",
- /* 288 */ "lp",
- /* 289 */ "anylist",
- /* 290 */ "windowdefn_list",
- /* 291 */ "windowdefn",
- /* 292 */ "window",
- /* 293 */ "frame_opt",
- /* 294 */ "part_opt",
- /* 295 */ "filter_opt",
- /* 296 */ "range_or_rows",
- /* 297 */ "frame_bound",
- /* 298 */ "frame_bound_s",
- /* 299 */ "frame_bound_e",
- /* 300 */ "frame_exclude_opt",
- /* 301 */ "frame_exclude",
+ /* 165 */ "TRUEFALSE",
+ /* 166 */ "ISNOT",
+ /* 167 */ "FUNCTION",
+ /* 168 */ "UMINUS",
+ /* 169 */ "UPLUS",
+ /* 170 */ "TRUTH",
+ /* 171 */ "REGISTER",
+ /* 172 */ "VECTOR",
+ /* 173 */ "SELECT_COLUMN",
+ /* 174 */ "IF_NULL_ROW",
+ /* 175 */ "ASTERISK",
+ /* 176 */ "SPAN",
+ /* 177 */ "SPACE",
+ /* 178 */ "ILLEGAL",
+ /* 179 */ "input",
+ /* 180 */ "cmdlist",
+ /* 181 */ "ecmd",
+ /* 182 */ "cmdx",
+ /* 183 */ "explain",
+ /* 184 */ "cmd",
+ /* 185 */ "transtype",
+ /* 186 */ "trans_opt",
+ /* 187 */ "nm",
+ /* 188 */ "savepoint_opt",
+ /* 189 */ "create_table",
+ /* 190 */ "create_table_args",
+ /* 191 */ "createkw",
+ /* 192 */ "temp",
+ /* 193 */ "ifnotexists",
+ /* 194 */ "dbnm",
+ /* 195 */ "columnlist",
+ /* 196 */ "conslist_opt",
+ /* 197 */ "table_options",
+ /* 198 */ "select",
+ /* 199 */ "columnname",
+ /* 200 */ "carglist",
+ /* 201 */ "typetoken",
+ /* 202 */ "typename",
+ /* 203 */ "signed",
+ /* 204 */ "plus_num",
+ /* 205 */ "minus_num",
+ /* 206 */ "scanpt",
+ /* 207 */ "scantok",
+ /* 208 */ "ccons",
+ /* 209 */ "term",
+ /* 210 */ "expr",
+ /* 211 */ "onconf",
+ /* 212 */ "sortorder",
+ /* 213 */ "autoinc",
+ /* 214 */ "eidlist_opt",
+ /* 215 */ "refargs",
+ /* 216 */ "defer_subclause",
+ /* 217 */ "refarg",
+ /* 218 */ "refact",
+ /* 219 */ "init_deferred_pred_opt",
+ /* 220 */ "conslist",
+ /* 221 */ "tconscomma",
+ /* 222 */ "tcons",
+ /* 223 */ "sortlist",
+ /* 224 */ "eidlist",
+ /* 225 */ "defer_subclause_opt",
+ /* 226 */ "orconf",
+ /* 227 */ "resolvetype",
+ /* 228 */ "raisetype",
+ /* 229 */ "ifexists",
+ /* 230 */ "fullname",
+ /* 231 */ "selectnowith",
+ /* 232 */ "oneselect",
+ /* 233 */ "wqlist",
+ /* 234 */ "multiselect_op",
+ /* 235 */ "distinct",
+ /* 236 */ "selcollist",
+ /* 237 */ "from",
+ /* 238 */ "where_opt",
+ /* 239 */ "groupby_opt",
+ /* 240 */ "having_opt",
+ /* 241 */ "orderby_opt",
+ /* 242 */ "limit_opt",
+ /* 243 */ "window_clause",
+ /* 244 */ "values",
+ /* 245 */ "nexprlist",
+ /* 246 */ "sclp",
+ /* 247 */ "as",
+ /* 248 */ "seltablist",
+ /* 249 */ "stl_prefix",
+ /* 250 */ "joinop",
+ /* 251 */ "indexed_opt",
+ /* 252 */ "on_opt",
+ /* 253 */ "using_opt",
+ /* 254 */ "exprlist",
+ /* 255 */ "xfullname",
+ /* 256 */ "idlist",
+ /* 257 */ "nulls",
+ /* 258 */ "with",
+ /* 259 */ "setlist",
+ /* 260 */ "insert_cmd",
+ /* 261 */ "idlist_opt",
+ /* 262 */ "upsert",
+ /* 263 */ "filter_over",
+ /* 264 */ "likeop",
+ /* 265 */ "between_op",
+ /* 266 */ "in_op",
+ /* 267 */ "paren_exprlist",
+ /* 268 */ "case_operand",
+ /* 269 */ "case_exprlist",
+ /* 270 */ "case_else",
+ /* 271 */ "uniqueflag",
+ /* 272 */ "collate",
+ /* 273 */ "vinto",
+ /* 274 */ "nmnum",
+ /* 275 */ "trigger_decl",
+ /* 276 */ "trigger_cmd_list",
+ /* 277 */ "trigger_time",
+ /* 278 */ "trigger_event",
+ /* 279 */ "foreach_clause",
+ /* 280 */ "when_clause",
+ /* 281 */ "trigger_cmd",
+ /* 282 */ "trnm",
+ /* 283 */ "tridxby",
+ /* 284 */ "database_kw_opt",
+ /* 285 */ "key_opt",
+ /* 286 */ "add_column_fullname",
+ /* 287 */ "kwcolumn_opt",
+ /* 288 */ "create_vtab",
+ /* 289 */ "vtabarglist",
+ /* 290 */ "vtabarg",
+ /* 291 */ "vtabargtoken",
+ /* 292 */ "lp",
+ /* 293 */ "anylist",
+ /* 294 */ "windowdefn_list",
+ /* 295 */ "windowdefn",
+ /* 296 */ "window",
+ /* 297 */ "frame_opt",
+ /* 298 */ "part_opt",
+ /* 299 */ "filter_clause",
+ /* 300 */ "over_clause",
+ /* 301 */ "range_or_rows",
+ /* 302 */ "frame_bound",
+ /* 303 */ "frame_bound_s",
+ /* 304 */ "frame_bound_e",
+ /* 305 */ "frame_exclude_opt",
+ /* 306 */ "frame_exclude",
};
#endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */
@@ -150293,252 +151320,257 @@ static const char *const yyRuleName[] = {
/* 127 */ "using_opt ::=",
/* 128 */ "orderby_opt ::=",
/* 129 */ "orderby_opt ::= ORDER BY sortlist",
- /* 130 */ "sortlist ::= sortlist COMMA expr sortorder",
- /* 131 */ "sortlist ::= expr sortorder",
+ /* 130 */ "sortlist ::= sortlist COMMA expr sortorder nulls",
+ /* 131 */ "sortlist ::= expr sortorder nulls",
/* 132 */ "sortorder ::= ASC",
/* 133 */ "sortorder ::= DESC",
/* 134 */ "sortorder ::=",
- /* 135 */ "groupby_opt ::=",
- /* 136 */ "groupby_opt ::= GROUP BY nexprlist",
- /* 137 */ "having_opt ::=",
- /* 138 */ "having_opt ::= HAVING expr",
- /* 139 */ "limit_opt ::=",
- /* 140 */ "limit_opt ::= LIMIT expr",
- /* 141 */ "limit_opt ::= LIMIT expr OFFSET expr",
- /* 142 */ "limit_opt ::= LIMIT expr COMMA expr",
- /* 143 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt",
- /* 144 */ "where_opt ::=",
- /* 145 */ "where_opt ::= WHERE expr",
- /* 146 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt",
- /* 147 */ "setlist ::= setlist COMMA nm EQ expr",
- /* 148 */ "setlist ::= setlist COMMA LP idlist RP EQ expr",
- /* 149 */ "setlist ::= nm EQ expr",
- /* 150 */ "setlist ::= LP idlist RP EQ expr",
- /* 151 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert",
- /* 152 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES",
- /* 153 */ "upsert ::=",
- /* 154 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt",
- /* 155 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING",
- /* 156 */ "upsert ::= ON CONFLICT DO NOTHING",
- /* 157 */ "insert_cmd ::= INSERT orconf",
- /* 158 */ "insert_cmd ::= REPLACE",
- /* 159 */ "idlist_opt ::=",
- /* 160 */ "idlist_opt ::= LP idlist RP",
- /* 161 */ "idlist ::= idlist COMMA nm",
- /* 162 */ "idlist ::= nm",
- /* 163 */ "expr ::= LP expr RP",
- /* 164 */ "expr ::= ID|INDEXED",
- /* 165 */ "expr ::= JOIN_KW",
- /* 166 */ "expr ::= nm DOT nm",
- /* 167 */ "expr ::= nm DOT nm DOT nm",
- /* 168 */ "term ::= NULL|FLOAT|BLOB",
- /* 169 */ "term ::= STRING",
- /* 170 */ "term ::= INTEGER",
- /* 171 */ "expr ::= VARIABLE",
- /* 172 */ "expr ::= expr COLLATE ID|STRING",
- /* 173 */ "expr ::= CAST LP expr AS typetoken RP",
- /* 174 */ "expr ::= ID|INDEXED LP distinct exprlist RP",
- /* 175 */ "expr ::= ID|INDEXED LP STAR RP",
- /* 176 */ "expr ::= ID|INDEXED LP distinct exprlist RP over_clause",
- /* 177 */ "expr ::= ID|INDEXED LP STAR RP over_clause",
- /* 178 */ "term ::= CTIME_KW",
- /* 179 */ "expr ::= LP nexprlist COMMA expr RP",
- /* 180 */ "expr ::= expr AND expr",
- /* 181 */ "expr ::= expr OR expr",
- /* 182 */ "expr ::= expr LT|GT|GE|LE expr",
- /* 183 */ "expr ::= expr EQ|NE expr",
- /* 184 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr",
- /* 185 */ "expr ::= expr PLUS|MINUS expr",
- /* 186 */ "expr ::= expr STAR|SLASH|REM expr",
- /* 187 */ "expr ::= expr CONCAT expr",
- /* 188 */ "likeop ::= NOT LIKE_KW|MATCH",
- /* 189 */ "expr ::= expr likeop expr",
- /* 190 */ "expr ::= expr likeop expr ESCAPE expr",
- /* 191 */ "expr ::= expr ISNULL|NOTNULL",
- /* 192 */ "expr ::= expr NOT NULL",
- /* 193 */ "expr ::= expr IS expr",
- /* 194 */ "expr ::= expr IS NOT expr",
- /* 195 */ "expr ::= NOT expr",
- /* 196 */ "expr ::= BITNOT expr",
- /* 197 */ "expr ::= PLUS|MINUS expr",
- /* 198 */ "between_op ::= BETWEEN",
- /* 199 */ "between_op ::= NOT BETWEEN",
- /* 200 */ "expr ::= expr between_op expr AND expr",
- /* 201 */ "in_op ::= IN",
- /* 202 */ "in_op ::= NOT IN",
- /* 203 */ "expr ::= expr in_op LP exprlist RP",
- /* 204 */ "expr ::= LP select RP",
- /* 205 */ "expr ::= expr in_op LP select RP",
- /* 206 */ "expr ::= expr in_op nm dbnm paren_exprlist",
- /* 207 */ "expr ::= EXISTS LP select RP",
- /* 208 */ "expr ::= CASE case_operand case_exprlist case_else END",
- /* 209 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr",
- /* 210 */ "case_exprlist ::= WHEN expr THEN expr",
- /* 211 */ "case_else ::= ELSE expr",
- /* 212 */ "case_else ::=",
- /* 213 */ "case_operand ::= expr",
- /* 214 */ "case_operand ::=",
- /* 215 */ "exprlist ::=",
- /* 216 */ "nexprlist ::= nexprlist COMMA expr",
- /* 217 */ "nexprlist ::= expr",
- /* 218 */ "paren_exprlist ::=",
- /* 219 */ "paren_exprlist ::= LP exprlist RP",
- /* 220 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt",
- /* 221 */ "uniqueflag ::= UNIQUE",
- /* 222 */ "uniqueflag ::=",
- /* 223 */ "eidlist_opt ::=",
- /* 224 */ "eidlist_opt ::= LP eidlist RP",
- /* 225 */ "eidlist ::= eidlist COMMA nm collate sortorder",
- /* 226 */ "eidlist ::= nm collate sortorder",
- /* 227 */ "collate ::=",
- /* 228 */ "collate ::= COLLATE ID|STRING",
- /* 229 */ "cmd ::= DROP INDEX ifexists fullname",
- /* 230 */ "cmd ::= VACUUM vinto",
- /* 231 */ "cmd ::= VACUUM nm vinto",
- /* 232 */ "vinto ::= INTO expr",
- /* 233 */ "vinto ::=",
- /* 234 */ "cmd ::= PRAGMA nm dbnm",
- /* 235 */ "cmd ::= PRAGMA nm dbnm EQ nmnum",
- /* 236 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP",
- /* 237 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
- /* 238 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP",
- /* 239 */ "plus_num ::= PLUS INTEGER|FLOAT",
- /* 240 */ "minus_num ::= MINUS INTEGER|FLOAT",
- /* 241 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END",
- /* 242 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",
- /* 243 */ "trigger_time ::= BEFORE|AFTER",
- /* 244 */ "trigger_time ::= INSTEAD OF",
- /* 245 */ "trigger_time ::=",
- /* 246 */ "trigger_event ::= DELETE|INSERT",
- /* 247 */ "trigger_event ::= UPDATE",
- /* 248 */ "trigger_event ::= UPDATE OF idlist",
- /* 249 */ "when_clause ::=",
- /* 250 */ "when_clause ::= WHEN expr",
- /* 251 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
- /* 252 */ "trigger_cmd_list ::= trigger_cmd SEMI",
- /* 253 */ "trnm ::= nm DOT nm",
- /* 254 */ "tridxby ::= INDEXED BY nm",
- /* 255 */ "tridxby ::= NOT INDEXED",
- /* 256 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt",
- /* 257 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt",
- /* 258 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt",
- /* 259 */ "trigger_cmd ::= scanpt select scanpt",
- /* 260 */ "expr ::= RAISE LP IGNORE RP",
- /* 261 */ "expr ::= RAISE LP raisetype COMMA nm RP",
- /* 262 */ "raisetype ::= ROLLBACK",
- /* 263 */ "raisetype ::= ABORT",
- /* 264 */ "raisetype ::= FAIL",
- /* 265 */ "cmd ::= DROP TRIGGER ifexists fullname",
- /* 266 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
- /* 267 */ "cmd ::= DETACH database_kw_opt expr",
- /* 268 */ "key_opt ::=",
- /* 269 */ "key_opt ::= KEY expr",
- /* 270 */ "cmd ::= REINDEX",
- /* 271 */ "cmd ::= REINDEX nm dbnm",
- /* 272 */ "cmd ::= ANALYZE",
- /* 273 */ "cmd ::= ANALYZE nm dbnm",
- /* 274 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
- /* 275 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist",
- /* 276 */ "add_column_fullname ::= fullname",
- /* 277 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm",
- /* 278 */ "cmd ::= create_vtab",
- /* 279 */ "cmd ::= create_vtab LP vtabarglist RP",
- /* 280 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm",
- /* 281 */ "vtabarg ::=",
- /* 282 */ "vtabargtoken ::= ANY",
- /* 283 */ "vtabargtoken ::= lp anylist RP",
- /* 284 */ "lp ::= LP",
- /* 285 */ "with ::= WITH wqlist",
- /* 286 */ "with ::= WITH RECURSIVE wqlist",
- /* 287 */ "wqlist ::= nm eidlist_opt AS LP select RP",
- /* 288 */ "wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP",
- /* 289 */ "windowdefn_list ::= windowdefn",
- /* 290 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn",
- /* 291 */ "windowdefn ::= nm AS LP window RP",
- /* 292 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt",
- /* 293 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt",
- /* 294 */ "window ::= ORDER BY sortlist frame_opt",
- /* 295 */ "window ::= nm ORDER BY sortlist frame_opt",
- /* 296 */ "window ::= frame_opt",
- /* 297 */ "window ::= nm frame_opt",
- /* 298 */ "frame_opt ::=",
- /* 299 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt",
- /* 300 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt",
- /* 301 */ "range_or_rows ::= RANGE|ROWS|GROUPS",
- /* 302 */ "frame_bound_s ::= frame_bound",
- /* 303 */ "frame_bound_s ::= UNBOUNDED PRECEDING",
- /* 304 */ "frame_bound_e ::= frame_bound",
- /* 305 */ "frame_bound_e ::= UNBOUNDED FOLLOWING",
- /* 306 */ "frame_bound ::= expr PRECEDING|FOLLOWING",
- /* 307 */ "frame_bound ::= CURRENT ROW",
- /* 308 */ "frame_exclude_opt ::=",
- /* 309 */ "frame_exclude_opt ::= EXCLUDE frame_exclude",
- /* 310 */ "frame_exclude ::= NO OTHERS",
- /* 311 */ "frame_exclude ::= CURRENT ROW",
- /* 312 */ "frame_exclude ::= GROUP|TIES",
- /* 313 */ "window_clause ::= WINDOW windowdefn_list",
- /* 314 */ "over_clause ::= filter_opt OVER LP window RP",
- /* 315 */ "over_clause ::= filter_opt OVER nm",
- /* 316 */ "filter_opt ::=",
- /* 317 */ "filter_opt ::= FILTER LP WHERE expr RP",
- /* 318 */ "input ::= cmdlist",
- /* 319 */ "cmdlist ::= cmdlist ecmd",
- /* 320 */ "cmdlist ::= ecmd",
- /* 321 */ "ecmd ::= SEMI",
- /* 322 */ "ecmd ::= cmdx SEMI",
- /* 323 */ "ecmd ::= explain cmdx",
- /* 324 */ "trans_opt ::=",
- /* 325 */ "trans_opt ::= TRANSACTION",
- /* 326 */ "trans_opt ::= TRANSACTION nm",
- /* 327 */ "savepoint_opt ::= SAVEPOINT",
- /* 328 */ "savepoint_opt ::=",
- /* 329 */ "cmd ::= create_table create_table_args",
- /* 330 */ "columnlist ::= columnlist COMMA columnname carglist",
- /* 331 */ "columnlist ::= columnname carglist",
- /* 332 */ "nm ::= ID|INDEXED",
- /* 333 */ "nm ::= STRING",
- /* 334 */ "nm ::= JOIN_KW",
- /* 335 */ "typetoken ::= typename",
- /* 336 */ "typename ::= ID|STRING",
- /* 337 */ "signed ::= plus_num",
- /* 338 */ "signed ::= minus_num",
- /* 339 */ "carglist ::= carglist ccons",
- /* 340 */ "carglist ::=",
- /* 341 */ "ccons ::= NULL onconf",
- /* 342 */ "conslist_opt ::= COMMA conslist",
- /* 343 */ "conslist ::= conslist tconscomma tcons",
- /* 344 */ "conslist ::= tcons",
- /* 345 */ "tconscomma ::=",
- /* 346 */ "defer_subclause_opt ::= defer_subclause",
- /* 347 */ "resolvetype ::= raisetype",
- /* 348 */ "selectnowith ::= oneselect",
- /* 349 */ "oneselect ::= values",
- /* 350 */ "sclp ::= selcollist COMMA",
- /* 351 */ "as ::= ID|STRING",
- /* 352 */ "expr ::= term",
- /* 353 */ "likeop ::= LIKE_KW|MATCH",
- /* 354 */ "exprlist ::= nexprlist",
- /* 355 */ "nmnum ::= plus_num",
- /* 356 */ "nmnum ::= nm",
- /* 357 */ "nmnum ::= ON",
- /* 358 */ "nmnum ::= DELETE",
- /* 359 */ "nmnum ::= DEFAULT",
- /* 360 */ "plus_num ::= INTEGER|FLOAT",
- /* 361 */ "foreach_clause ::=",
- /* 362 */ "foreach_clause ::= FOR EACH ROW",
- /* 363 */ "trnm ::= nm",
- /* 364 */ "tridxby ::=",
- /* 365 */ "database_kw_opt ::= DATABASE",
- /* 366 */ "database_kw_opt ::=",
- /* 367 */ "kwcolumn_opt ::=",
- /* 368 */ "kwcolumn_opt ::= COLUMNKW",
- /* 369 */ "vtabarglist ::= vtabarg",
- /* 370 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
- /* 371 */ "vtabarg ::= vtabarg vtabargtoken",
- /* 372 */ "anylist ::=",
- /* 373 */ "anylist ::= anylist LP anylist RP",
- /* 374 */ "anylist ::= anylist ANY",
- /* 375 */ "with ::=",
+ /* 135 */ "nulls ::= NULLS FIRST",
+ /* 136 */ "nulls ::= NULLS LAST",
+ /* 137 */ "nulls ::=",
+ /* 138 */ "groupby_opt ::=",
+ /* 139 */ "groupby_opt ::= GROUP BY nexprlist",
+ /* 140 */ "having_opt ::=",
+ /* 141 */ "having_opt ::= HAVING expr",
+ /* 142 */ "limit_opt ::=",
+ /* 143 */ "limit_opt ::= LIMIT expr",
+ /* 144 */ "limit_opt ::= LIMIT expr OFFSET expr",
+ /* 145 */ "limit_opt ::= LIMIT expr COMMA expr",
+ /* 146 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt",
+ /* 147 */ "where_opt ::=",
+ /* 148 */ "where_opt ::= WHERE expr",
+ /* 149 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt",
+ /* 150 */ "setlist ::= setlist COMMA nm EQ expr",
+ /* 151 */ "setlist ::= setlist COMMA LP idlist RP EQ expr",
+ /* 152 */ "setlist ::= nm EQ expr",
+ /* 153 */ "setlist ::= LP idlist RP EQ expr",
+ /* 154 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert",
+ /* 155 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES",
+ /* 156 */ "upsert ::=",
+ /* 157 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt",
+ /* 158 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING",
+ /* 159 */ "upsert ::= ON CONFLICT DO NOTHING",
+ /* 160 */ "insert_cmd ::= INSERT orconf",
+ /* 161 */ "insert_cmd ::= REPLACE",
+ /* 162 */ "idlist_opt ::=",
+ /* 163 */ "idlist_opt ::= LP idlist RP",
+ /* 164 */ "idlist ::= idlist COMMA nm",
+ /* 165 */ "idlist ::= nm",
+ /* 166 */ "expr ::= LP expr RP",
+ /* 167 */ "expr ::= ID|INDEXED",
+ /* 168 */ "expr ::= JOIN_KW",
+ /* 169 */ "expr ::= nm DOT nm",
+ /* 170 */ "expr ::= nm DOT nm DOT nm",
+ /* 171 */ "term ::= NULL|FLOAT|BLOB",
+ /* 172 */ "term ::= STRING",
+ /* 173 */ "term ::= INTEGER",
+ /* 174 */ "expr ::= VARIABLE",
+ /* 175 */ "expr ::= expr COLLATE ID|STRING",
+ /* 176 */ "expr ::= CAST LP expr AS typetoken RP",
+ /* 177 */ "expr ::= ID|INDEXED LP distinct exprlist RP",
+ /* 178 */ "expr ::= ID|INDEXED LP STAR RP",
+ /* 179 */ "expr ::= ID|INDEXED LP distinct exprlist RP filter_over",
+ /* 180 */ "expr ::= ID|INDEXED LP STAR RP filter_over",
+ /* 181 */ "term ::= CTIME_KW",
+ /* 182 */ "expr ::= LP nexprlist COMMA expr RP",
+ /* 183 */ "expr ::= expr AND expr",
+ /* 184 */ "expr ::= expr OR expr",
+ /* 185 */ "expr ::= expr LT|GT|GE|LE expr",
+ /* 186 */ "expr ::= expr EQ|NE expr",
+ /* 187 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr",
+ /* 188 */ "expr ::= expr PLUS|MINUS expr",
+ /* 189 */ "expr ::= expr STAR|SLASH|REM expr",
+ /* 190 */ "expr ::= expr CONCAT expr",
+ /* 191 */ "likeop ::= NOT LIKE_KW|MATCH",
+ /* 192 */ "expr ::= expr likeop expr",
+ /* 193 */ "expr ::= expr likeop expr ESCAPE expr",
+ /* 194 */ "expr ::= expr ISNULL|NOTNULL",
+ /* 195 */ "expr ::= expr NOT NULL",
+ /* 196 */ "expr ::= expr IS expr",
+ /* 197 */ "expr ::= expr IS NOT expr",
+ /* 198 */ "expr ::= NOT expr",
+ /* 199 */ "expr ::= BITNOT expr",
+ /* 200 */ "expr ::= PLUS|MINUS expr",
+ /* 201 */ "between_op ::= BETWEEN",
+ /* 202 */ "between_op ::= NOT BETWEEN",
+ /* 203 */ "expr ::= expr between_op expr AND expr",
+ /* 204 */ "in_op ::= IN",
+ /* 205 */ "in_op ::= NOT IN",
+ /* 206 */ "expr ::= expr in_op LP exprlist RP",
+ /* 207 */ "expr ::= LP select RP",
+ /* 208 */ "expr ::= expr in_op LP select RP",
+ /* 209 */ "expr ::= expr in_op nm dbnm paren_exprlist",
+ /* 210 */ "expr ::= EXISTS LP select RP",
+ /* 211 */ "expr ::= CASE case_operand case_exprlist case_else END",
+ /* 212 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr",
+ /* 213 */ "case_exprlist ::= WHEN expr THEN expr",
+ /* 214 */ "case_else ::= ELSE expr",
+ /* 215 */ "case_else ::=",
+ /* 216 */ "case_operand ::= expr",
+ /* 217 */ "case_operand ::=",
+ /* 218 */ "exprlist ::=",
+ /* 219 */ "nexprlist ::= nexprlist COMMA expr",
+ /* 220 */ "nexprlist ::= expr",
+ /* 221 */ "paren_exprlist ::=",
+ /* 222 */ "paren_exprlist ::= LP exprlist RP",
+ /* 223 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt",
+ /* 224 */ "uniqueflag ::= UNIQUE",
+ /* 225 */ "uniqueflag ::=",
+ /* 226 */ "eidlist_opt ::=",
+ /* 227 */ "eidlist_opt ::= LP eidlist RP",
+ /* 228 */ "eidlist ::= eidlist COMMA nm collate sortorder",
+ /* 229 */ "eidlist ::= nm collate sortorder",
+ /* 230 */ "collate ::=",
+ /* 231 */ "collate ::= COLLATE ID|STRING",
+ /* 232 */ "cmd ::= DROP INDEX ifexists fullname",
+ /* 233 */ "cmd ::= VACUUM vinto",
+ /* 234 */ "cmd ::= VACUUM nm vinto",
+ /* 235 */ "vinto ::= INTO expr",
+ /* 236 */ "vinto ::=",
+ /* 237 */ "cmd ::= PRAGMA nm dbnm",
+ /* 238 */ "cmd ::= PRAGMA nm dbnm EQ nmnum",
+ /* 239 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP",
+ /* 240 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
+ /* 241 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP",
+ /* 242 */ "plus_num ::= PLUS INTEGER|FLOAT",
+ /* 243 */ "minus_num ::= MINUS INTEGER|FLOAT",
+ /* 244 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END",
+ /* 245 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",
+ /* 246 */ "trigger_time ::= BEFORE|AFTER",
+ /* 247 */ "trigger_time ::= INSTEAD OF",
+ /* 248 */ "trigger_time ::=",
+ /* 249 */ "trigger_event ::= DELETE|INSERT",
+ /* 250 */ "trigger_event ::= UPDATE",
+ /* 251 */ "trigger_event ::= UPDATE OF idlist",
+ /* 252 */ "when_clause ::=",
+ /* 253 */ "when_clause ::= WHEN expr",
+ /* 254 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
+ /* 255 */ "trigger_cmd_list ::= trigger_cmd SEMI",
+ /* 256 */ "trnm ::= nm DOT nm",
+ /* 257 */ "tridxby ::= INDEXED BY nm",
+ /* 258 */ "tridxby ::= NOT INDEXED",
+ /* 259 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt",
+ /* 260 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt",
+ /* 261 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt",
+ /* 262 */ "trigger_cmd ::= scanpt select scanpt",
+ /* 263 */ "expr ::= RAISE LP IGNORE RP",
+ /* 264 */ "expr ::= RAISE LP raisetype COMMA nm RP",
+ /* 265 */ "raisetype ::= ROLLBACK",
+ /* 266 */ "raisetype ::= ABORT",
+ /* 267 */ "raisetype ::= FAIL",
+ /* 268 */ "cmd ::= DROP TRIGGER ifexists fullname",
+ /* 269 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
+ /* 270 */ "cmd ::= DETACH database_kw_opt expr",
+ /* 271 */ "key_opt ::=",
+ /* 272 */ "key_opt ::= KEY expr",
+ /* 273 */ "cmd ::= REINDEX",
+ /* 274 */ "cmd ::= REINDEX nm dbnm",
+ /* 275 */ "cmd ::= ANALYZE",
+ /* 276 */ "cmd ::= ANALYZE nm dbnm",
+ /* 277 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
+ /* 278 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist",
+ /* 279 */ "add_column_fullname ::= fullname",
+ /* 280 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm",
+ /* 281 */ "cmd ::= create_vtab",
+ /* 282 */ "cmd ::= create_vtab LP vtabarglist RP",
+ /* 283 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm",
+ /* 284 */ "vtabarg ::=",
+ /* 285 */ "vtabargtoken ::= ANY",
+ /* 286 */ "vtabargtoken ::= lp anylist RP",
+ /* 287 */ "lp ::= LP",
+ /* 288 */ "with ::= WITH wqlist",
+ /* 289 */ "with ::= WITH RECURSIVE wqlist",
+ /* 290 */ "wqlist ::= nm eidlist_opt AS LP select RP",
+ /* 291 */ "wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP",
+ /* 292 */ "windowdefn_list ::= windowdefn",
+ /* 293 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn",
+ /* 294 */ "windowdefn ::= nm AS LP window RP",
+ /* 295 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt",
+ /* 296 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt",
+ /* 297 */ "window ::= ORDER BY sortlist frame_opt",
+ /* 298 */ "window ::= nm ORDER BY sortlist frame_opt",
+ /* 299 */ "window ::= frame_opt",
+ /* 300 */ "window ::= nm frame_opt",
+ /* 301 */ "frame_opt ::=",
+ /* 302 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt",
+ /* 303 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt",
+ /* 304 */ "range_or_rows ::= RANGE|ROWS|GROUPS",
+ /* 305 */ "frame_bound_s ::= frame_bound",
+ /* 306 */ "frame_bound_s ::= UNBOUNDED PRECEDING",
+ /* 307 */ "frame_bound_e ::= frame_bound",
+ /* 308 */ "frame_bound_e ::= UNBOUNDED FOLLOWING",
+ /* 309 */ "frame_bound ::= expr PRECEDING|FOLLOWING",
+ /* 310 */ "frame_bound ::= CURRENT ROW",
+ /* 311 */ "frame_exclude_opt ::=",
+ /* 312 */ "frame_exclude_opt ::= EXCLUDE frame_exclude",
+ /* 313 */ "frame_exclude ::= NO OTHERS",
+ /* 314 */ "frame_exclude ::= CURRENT ROW",
+ /* 315 */ "frame_exclude ::= GROUP|TIES",
+ /* 316 */ "window_clause ::= WINDOW windowdefn_list",
+ /* 317 */ "filter_over ::= filter_clause over_clause",
+ /* 318 */ "filter_over ::= over_clause",
+ /* 319 */ "filter_over ::= filter_clause",
+ /* 320 */ "over_clause ::= OVER LP window RP",
+ /* 321 */ "over_clause ::= OVER nm",
+ /* 322 */ "filter_clause ::= FILTER LP WHERE expr RP",
+ /* 323 */ "input ::= cmdlist",
+ /* 324 */ "cmdlist ::= cmdlist ecmd",
+ /* 325 */ "cmdlist ::= ecmd",
+ /* 326 */ "ecmd ::= SEMI",
+ /* 327 */ "ecmd ::= cmdx SEMI",
+ /* 328 */ "ecmd ::= explain cmdx",
+ /* 329 */ "trans_opt ::=",
+ /* 330 */ "trans_opt ::= TRANSACTION",
+ /* 331 */ "trans_opt ::= TRANSACTION nm",
+ /* 332 */ "savepoint_opt ::= SAVEPOINT",
+ /* 333 */ "savepoint_opt ::=",
+ /* 334 */ "cmd ::= create_table create_table_args",
+ /* 335 */ "columnlist ::= columnlist COMMA columnname carglist",
+ /* 336 */ "columnlist ::= columnname carglist",
+ /* 337 */ "nm ::= ID|INDEXED",
+ /* 338 */ "nm ::= STRING",
+ /* 339 */ "nm ::= JOIN_KW",
+ /* 340 */ "typetoken ::= typename",
+ /* 341 */ "typename ::= ID|STRING",
+ /* 342 */ "signed ::= plus_num",
+ /* 343 */ "signed ::= minus_num",
+ /* 344 */ "carglist ::= carglist ccons",
+ /* 345 */ "carglist ::=",
+ /* 346 */ "ccons ::= NULL onconf",
+ /* 347 */ "conslist_opt ::= COMMA conslist",
+ /* 348 */ "conslist ::= conslist tconscomma tcons",
+ /* 349 */ "conslist ::= tcons",
+ /* 350 */ "tconscomma ::=",
+ /* 351 */ "defer_subclause_opt ::= defer_subclause",
+ /* 352 */ "resolvetype ::= raisetype",
+ /* 353 */ "selectnowith ::= oneselect",
+ /* 354 */ "oneselect ::= values",
+ /* 355 */ "sclp ::= selcollist COMMA",
+ /* 356 */ "as ::= ID|STRING",
+ /* 357 */ "expr ::= term",
+ /* 358 */ "likeop ::= LIKE_KW|MATCH",
+ /* 359 */ "exprlist ::= nexprlist",
+ /* 360 */ "nmnum ::= plus_num",
+ /* 361 */ "nmnum ::= nm",
+ /* 362 */ "nmnum ::= ON",
+ /* 363 */ "nmnum ::= DELETE",
+ /* 364 */ "nmnum ::= DEFAULT",
+ /* 365 */ "plus_num ::= INTEGER|FLOAT",
+ /* 366 */ "foreach_clause ::=",
+ /* 367 */ "foreach_clause ::= FOR EACH ROW",
+ /* 368 */ "trnm ::= nm",
+ /* 369 */ "tridxby ::=",
+ /* 370 */ "database_kw_opt ::= DATABASE",
+ /* 371 */ "database_kw_opt ::=",
+ /* 372 */ "kwcolumn_opt ::=",
+ /* 373 */ "kwcolumn_opt ::= COLUMNKW",
+ /* 374 */ "vtabarglist ::= vtabarg",
+ /* 375 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
+ /* 376 */ "vtabarg ::= vtabarg vtabargtoken",
+ /* 377 */ "anylist ::=",
+ /* 378 */ "anylist ::= anylist LP anylist RP",
+ /* 379 */ "anylist ::= anylist ANY",
+ /* 380 */ "with ::=",
};
#endif /* NDEBUG */
@@ -150664,97 +151696,98 @@ static void yy_destructor(
** inside the C code.
*/
/********* Begin destructor definitions ***************************************/
- case 195: /* select */
- case 228: /* selectnowith */
- case 229: /* oneselect */
- case 241: /* values */
+ case 198: /* select */
+ case 231: /* selectnowith */
+ case 232: /* oneselect */
+ case 244: /* values */
{
-sqlite3SelectDelete(pParse->db, (yypminor->yy391));
+sqlite3SelectDelete(pParse->db, (yypminor->yy25));
}
break;
- case 206: /* term */
- case 207: /* expr */
- case 235: /* where_opt */
- case 237: /* having_opt */
- case 249: /* on_opt */
- case 264: /* case_operand */
- case 266: /* case_else */
- case 269: /* vinto */
- case 276: /* when_clause */
- case 281: /* key_opt */
- case 295: /* filter_opt */
+ case 209: /* term */
+ case 210: /* expr */
+ case 238: /* where_opt */
+ case 240: /* having_opt */
+ case 252: /* on_opt */
+ case 268: /* case_operand */
+ case 270: /* case_else */
+ case 273: /* vinto */
+ case 280: /* when_clause */
+ case 285: /* key_opt */
+ case 299: /* filter_clause */
{
-sqlite3ExprDelete(pParse->db, (yypminor->yy102));
+sqlite3ExprDelete(pParse->db, (yypminor->yy46));
}
break;
- case 211: /* eidlist_opt */
- case 220: /* sortlist */
- case 221: /* eidlist */
- case 233: /* selcollist */
- case 236: /* groupby_opt */
- case 238: /* orderby_opt */
- case 242: /* nexprlist */
- case 243: /* sclp */
- case 251: /* exprlist */
- case 255: /* setlist */
- case 263: /* paren_exprlist */
- case 265: /* case_exprlist */
- case 294: /* part_opt */
+ case 214: /* eidlist_opt */
+ case 223: /* sortlist */
+ case 224: /* eidlist */
+ case 236: /* selcollist */
+ case 239: /* groupby_opt */
+ case 241: /* orderby_opt */
+ case 245: /* nexprlist */
+ case 246: /* sclp */
+ case 254: /* exprlist */
+ case 259: /* setlist */
+ case 267: /* paren_exprlist */
+ case 269: /* case_exprlist */
+ case 298: /* part_opt */
{
-sqlite3ExprListDelete(pParse->db, (yypminor->yy94));
+sqlite3ExprListDelete(pParse->db, (yypminor->yy138));
}
break;
- case 227: /* fullname */
- case 234: /* from */
- case 245: /* seltablist */
- case 246: /* stl_prefix */
- case 252: /* xfullname */
+ case 230: /* fullname */
+ case 237: /* from */
+ case 248: /* seltablist */
+ case 249: /* stl_prefix */
+ case 255: /* xfullname */
{
-sqlite3SrcListDelete(pParse->db, (yypminor->yy407));
+sqlite3SrcListDelete(pParse->db, (yypminor->yy609));
}
break;
- case 230: /* wqlist */
+ case 233: /* wqlist */
{
-sqlite3WithDelete(pParse->db, (yypminor->yy243));
+sqlite3WithDelete(pParse->db, (yypminor->yy297));
}
break;
- case 240: /* window_clause */
- case 290: /* windowdefn_list */
+ case 243: /* window_clause */
+ case 294: /* windowdefn_list */
{
-sqlite3WindowListDelete(pParse->db, (yypminor->yy379));
+sqlite3WindowListDelete(pParse->db, (yypminor->yy455));
}
break;
- case 250: /* using_opt */
- case 253: /* idlist */
- case 257: /* idlist_opt */
+ case 253: /* using_opt */
+ case 256: /* idlist */
+ case 261: /* idlist_opt */
{
-sqlite3IdListDelete(pParse->db, (yypminor->yy76));
+sqlite3IdListDelete(pParse->db, (yypminor->yy406));
}
break;
- case 259: /* over_clause */
- case 291: /* windowdefn */
- case 292: /* window */
- case 293: /* frame_opt */
+ case 263: /* filter_over */
+ case 295: /* windowdefn */
+ case 296: /* window */
+ case 297: /* frame_opt */
+ case 300: /* over_clause */
{
-sqlite3WindowDelete(pParse->db, (yypminor->yy379));
+sqlite3WindowDelete(pParse->db, (yypminor->yy455));
}
break;
- case 272: /* trigger_cmd_list */
- case 277: /* trigger_cmd */
+ case 276: /* trigger_cmd_list */
+ case 281: /* trigger_cmd */
{
-sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy11));
+sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy527));
}
break;
- case 274: /* trigger_event */
+ case 278: /* trigger_event */
{
-sqlite3IdListDelete(pParse->db, (yypminor->yy298).b);
+sqlite3IdListDelete(pParse->db, (yypminor->yy572).b);
}
break;
- case 297: /* frame_bound */
- case 298: /* frame_bound_s */
- case 299: /* frame_bound_e */
+ case 302: /* frame_bound */
+ case 303: /* frame_bound_s */
+ case 304: /* frame_bound_e */
{
-sqlite3ExprDelete(pParse->db, (yypminor->yy389).pExpr);
+sqlite3ExprDelete(pParse->db, (yypminor->yy57).pExpr);
}
break;
/********* End destructor definitions *****************************************/
@@ -150880,15 +151913,18 @@ static YYACTIONTYPE yy_find_shift_action(
do{
i = yy_shift_ofst[stateno];
assert( i>=0 );
- /* assert( i+YYNTOKEN<=(int)YY_NLOOKAHEAD ); */
+ assert( i<=YY_ACTTAB_COUNT );
+ assert( i+YYNTOKEN<=(int)YY_NLOOKAHEAD );
assert( iLookAhead!=YYNOCODE );
assert( iLookAhead < YYNTOKEN );
i += iLookAhead;
- if( i>=YY_NLOOKAHEAD || yy_lookahead[i]!=iLookAhead ){
+ assert( i<(int)YY_NLOOKAHEAD );
+ if( yy_lookahead[i]!=iLookAhead ){
#ifdef YYFALLBACK
YYCODETYPE iFallback; /* Fallback token */
- if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
- && (iFallback = yyFallback[iLookAhead])!=0 ){
+ assert( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0]) );
+ iFallback = yyFallback[iLookAhead];
+ if( iFallback!=0 ){
#ifndef NDEBUG
if( yyTraceFILE ){
fprintf(yyTraceFILE, "%sFALLBACK %s => %s\n",
@@ -150903,16 +151939,8 @@ static YYACTIONTYPE yy_find_shift_action(
#ifdef YYWILDCARD
{
int j = i - iLookAhead + YYWILDCARD;
- if(
-#if YY_SHIFT_MIN+YYWILDCARD<0
- j>=0 &&
-#endif
-#if YY_SHIFT_MAX+YYWILDCARD>=YY_ACTTAB_COUNT
- j<YY_ACTTAB_COUNT &&
-#endif
- j<(int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])) &&
- yy_lookahead[j]==YYWILDCARD && iLookAhead>0
- ){
+ assert( j<(int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])) );
+ if( yy_lookahead[j]==YYWILDCARD && iLookAhead>0 ){
#ifndef NDEBUG
if( yyTraceFILE ){
fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n",
@@ -150926,6 +151954,7 @@ static YYACTIONTYPE yy_find_shift_action(
#endif /* YYWILDCARD */
return yy_default[stateno];
}else{
+ assert( i>=0 && i<sizeof(yy_action)/sizeof(yy_action[0]) );
return yy_action[i];
}
}while(1);
@@ -151049,382 +152078,387 @@ static void yy_shift(
/* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side
** of that rule */
static const YYCODETYPE yyRuleInfoLhs[] = {
- 180, /* (0) explain ::= EXPLAIN */
- 180, /* (1) explain ::= EXPLAIN QUERY PLAN */
- 179, /* (2) cmdx ::= cmd */
- 181, /* (3) cmd ::= BEGIN transtype trans_opt */
- 182, /* (4) transtype ::= */
- 182, /* (5) transtype ::= DEFERRED */
- 182, /* (6) transtype ::= IMMEDIATE */
- 182, /* (7) transtype ::= EXCLUSIVE */
- 181, /* (8) cmd ::= COMMIT|END trans_opt */
- 181, /* (9) cmd ::= ROLLBACK trans_opt */
- 181, /* (10) cmd ::= SAVEPOINT nm */
- 181, /* (11) cmd ::= RELEASE savepoint_opt nm */
- 181, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */
- 186, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */
- 188, /* (14) createkw ::= CREATE */
- 190, /* (15) ifnotexists ::= */
- 190, /* (16) ifnotexists ::= IF NOT EXISTS */
- 189, /* (17) temp ::= TEMP */
- 189, /* (18) temp ::= */
- 187, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_options */
- 187, /* (20) create_table_args ::= AS select */
- 194, /* (21) table_options ::= */
- 194, /* (22) table_options ::= WITHOUT nm */
- 196, /* (23) columnname ::= nm typetoken */
- 198, /* (24) typetoken ::= */
- 198, /* (25) typetoken ::= typename LP signed RP */
- 198, /* (26) typetoken ::= typename LP signed COMMA signed RP */
- 199, /* (27) typename ::= typename ID|STRING */
- 203, /* (28) scanpt ::= */
- 204, /* (29) scantok ::= */
- 205, /* (30) ccons ::= CONSTRAINT nm */
- 205, /* (31) ccons ::= DEFAULT scantok term */
- 205, /* (32) ccons ::= DEFAULT LP expr RP */
- 205, /* (33) ccons ::= DEFAULT PLUS scantok term */
- 205, /* (34) ccons ::= DEFAULT MINUS scantok term */
- 205, /* (35) ccons ::= DEFAULT scantok ID|INDEXED */
- 205, /* (36) ccons ::= NOT NULL onconf */
- 205, /* (37) ccons ::= PRIMARY KEY sortorder onconf autoinc */
- 205, /* (38) ccons ::= UNIQUE onconf */
- 205, /* (39) ccons ::= CHECK LP expr RP */
- 205, /* (40) ccons ::= REFERENCES nm eidlist_opt refargs */
- 205, /* (41) ccons ::= defer_subclause */
- 205, /* (42) ccons ::= COLLATE ID|STRING */
- 210, /* (43) autoinc ::= */
- 210, /* (44) autoinc ::= AUTOINCR */
- 212, /* (45) refargs ::= */
- 212, /* (46) refargs ::= refargs refarg */
- 214, /* (47) refarg ::= MATCH nm */
- 214, /* (48) refarg ::= ON INSERT refact */
- 214, /* (49) refarg ::= ON DELETE refact */
- 214, /* (50) refarg ::= ON UPDATE refact */
- 215, /* (51) refact ::= SET NULL */
- 215, /* (52) refact ::= SET DEFAULT */
- 215, /* (53) refact ::= CASCADE */
- 215, /* (54) refact ::= RESTRICT */
- 215, /* (55) refact ::= NO ACTION */
- 213, /* (56) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
- 213, /* (57) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
- 216, /* (58) init_deferred_pred_opt ::= */
- 216, /* (59) init_deferred_pred_opt ::= INITIALLY DEFERRED */
- 216, /* (60) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
- 193, /* (61) conslist_opt ::= */
- 218, /* (62) tconscomma ::= COMMA */
- 219, /* (63) tcons ::= CONSTRAINT nm */
- 219, /* (64) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
- 219, /* (65) tcons ::= UNIQUE LP sortlist RP onconf */
- 219, /* (66) tcons ::= CHECK LP expr RP onconf */
- 219, /* (67) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
- 222, /* (68) defer_subclause_opt ::= */
- 208, /* (69) onconf ::= */
- 208, /* (70) onconf ::= ON CONFLICT resolvetype */
- 223, /* (71) orconf ::= */
- 223, /* (72) orconf ::= OR resolvetype */
- 224, /* (73) resolvetype ::= IGNORE */
- 224, /* (74) resolvetype ::= REPLACE */
- 181, /* (75) cmd ::= DROP TABLE ifexists fullname */
- 226, /* (76) ifexists ::= IF EXISTS */
- 226, /* (77) ifexists ::= */
- 181, /* (78) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
- 181, /* (79) cmd ::= DROP VIEW ifexists fullname */
- 181, /* (80) cmd ::= select */
- 195, /* (81) select ::= WITH wqlist selectnowith */
- 195, /* (82) select ::= WITH RECURSIVE wqlist selectnowith */
- 195, /* (83) select ::= selectnowith */
- 228, /* (84) selectnowith ::= selectnowith multiselect_op oneselect */
- 231, /* (85) multiselect_op ::= UNION */
- 231, /* (86) multiselect_op ::= UNION ALL */
- 231, /* (87) multiselect_op ::= EXCEPT|INTERSECT */
- 229, /* (88) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
- 229, /* (89) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
- 241, /* (90) values ::= VALUES LP nexprlist RP */
- 241, /* (91) values ::= values COMMA LP nexprlist RP */
- 232, /* (92) distinct ::= DISTINCT */
- 232, /* (93) distinct ::= ALL */
- 232, /* (94) distinct ::= */
- 243, /* (95) sclp ::= */
- 233, /* (96) selcollist ::= sclp scanpt expr scanpt as */
- 233, /* (97) selcollist ::= sclp scanpt STAR */
- 233, /* (98) selcollist ::= sclp scanpt nm DOT STAR */
- 244, /* (99) as ::= AS nm */
- 244, /* (100) as ::= */
- 234, /* (101) from ::= */
- 234, /* (102) from ::= FROM seltablist */
- 246, /* (103) stl_prefix ::= seltablist joinop */
- 246, /* (104) stl_prefix ::= */
- 245, /* (105) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */
- 245, /* (106) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */
- 245, /* (107) seltablist ::= stl_prefix LP select RP as on_opt using_opt */
- 245, /* (108) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */
- 191, /* (109) dbnm ::= */
- 191, /* (110) dbnm ::= DOT nm */
- 227, /* (111) fullname ::= nm */
- 227, /* (112) fullname ::= nm DOT nm */
- 252, /* (113) xfullname ::= nm */
- 252, /* (114) xfullname ::= nm DOT nm */
- 252, /* (115) xfullname ::= nm DOT nm AS nm */
- 252, /* (116) xfullname ::= nm AS nm */
- 247, /* (117) joinop ::= COMMA|JOIN */
- 247, /* (118) joinop ::= JOIN_KW JOIN */
- 247, /* (119) joinop ::= JOIN_KW nm JOIN */
- 247, /* (120) joinop ::= JOIN_KW nm nm JOIN */
- 249, /* (121) on_opt ::= ON expr */
- 249, /* (122) on_opt ::= */
- 248, /* (123) indexed_opt ::= */
- 248, /* (124) indexed_opt ::= INDEXED BY nm */
- 248, /* (125) indexed_opt ::= NOT INDEXED */
- 250, /* (126) using_opt ::= USING LP idlist RP */
- 250, /* (127) using_opt ::= */
- 238, /* (128) orderby_opt ::= */
- 238, /* (129) orderby_opt ::= ORDER BY sortlist */
- 220, /* (130) sortlist ::= sortlist COMMA expr sortorder */
- 220, /* (131) sortlist ::= expr sortorder */
- 209, /* (132) sortorder ::= ASC */
- 209, /* (133) sortorder ::= DESC */
- 209, /* (134) sortorder ::= */
- 236, /* (135) groupby_opt ::= */
- 236, /* (136) groupby_opt ::= GROUP BY nexprlist */
- 237, /* (137) having_opt ::= */
- 237, /* (138) having_opt ::= HAVING expr */
- 239, /* (139) limit_opt ::= */
- 239, /* (140) limit_opt ::= LIMIT expr */
- 239, /* (141) limit_opt ::= LIMIT expr OFFSET expr */
- 239, /* (142) limit_opt ::= LIMIT expr COMMA expr */
- 181, /* (143) cmd ::= with DELETE FROM xfullname indexed_opt where_opt */
- 235, /* (144) where_opt ::= */
- 235, /* (145) where_opt ::= WHERE expr */
- 181, /* (146) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */
- 255, /* (147) setlist ::= setlist COMMA nm EQ expr */
- 255, /* (148) setlist ::= setlist COMMA LP idlist RP EQ expr */
- 255, /* (149) setlist ::= nm EQ expr */
- 255, /* (150) setlist ::= LP idlist RP EQ expr */
- 181, /* (151) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
- 181, /* (152) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */
- 258, /* (153) upsert ::= */
- 258, /* (154) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */
- 258, /* (155) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */
- 258, /* (156) upsert ::= ON CONFLICT DO NOTHING */
- 256, /* (157) insert_cmd ::= INSERT orconf */
- 256, /* (158) insert_cmd ::= REPLACE */
- 257, /* (159) idlist_opt ::= */
- 257, /* (160) idlist_opt ::= LP idlist RP */
- 253, /* (161) idlist ::= idlist COMMA nm */
- 253, /* (162) idlist ::= nm */
- 207, /* (163) expr ::= LP expr RP */
- 207, /* (164) expr ::= ID|INDEXED */
- 207, /* (165) expr ::= JOIN_KW */
- 207, /* (166) expr ::= nm DOT nm */
- 207, /* (167) expr ::= nm DOT nm DOT nm */
- 206, /* (168) term ::= NULL|FLOAT|BLOB */
- 206, /* (169) term ::= STRING */
- 206, /* (170) term ::= INTEGER */
- 207, /* (171) expr ::= VARIABLE */
- 207, /* (172) expr ::= expr COLLATE ID|STRING */
- 207, /* (173) expr ::= CAST LP expr AS typetoken RP */
- 207, /* (174) expr ::= ID|INDEXED LP distinct exprlist RP */
- 207, /* (175) expr ::= ID|INDEXED LP STAR RP */
- 207, /* (176) expr ::= ID|INDEXED LP distinct exprlist RP over_clause */
- 207, /* (177) expr ::= ID|INDEXED LP STAR RP over_clause */
- 206, /* (178) term ::= CTIME_KW */
- 207, /* (179) expr ::= LP nexprlist COMMA expr RP */
- 207, /* (180) expr ::= expr AND expr */
- 207, /* (181) expr ::= expr OR expr */
- 207, /* (182) expr ::= expr LT|GT|GE|LE expr */
- 207, /* (183) expr ::= expr EQ|NE expr */
- 207, /* (184) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
- 207, /* (185) expr ::= expr PLUS|MINUS expr */
- 207, /* (186) expr ::= expr STAR|SLASH|REM expr */
- 207, /* (187) expr ::= expr CONCAT expr */
- 260, /* (188) likeop ::= NOT LIKE_KW|MATCH */
- 207, /* (189) expr ::= expr likeop expr */
- 207, /* (190) expr ::= expr likeop expr ESCAPE expr */
- 207, /* (191) expr ::= expr ISNULL|NOTNULL */
- 207, /* (192) expr ::= expr NOT NULL */
- 207, /* (193) expr ::= expr IS expr */
- 207, /* (194) expr ::= expr IS NOT expr */
- 207, /* (195) expr ::= NOT expr */
- 207, /* (196) expr ::= BITNOT expr */
- 207, /* (197) expr ::= PLUS|MINUS expr */
- 261, /* (198) between_op ::= BETWEEN */
- 261, /* (199) between_op ::= NOT BETWEEN */
- 207, /* (200) expr ::= expr between_op expr AND expr */
- 262, /* (201) in_op ::= IN */
- 262, /* (202) in_op ::= NOT IN */
- 207, /* (203) expr ::= expr in_op LP exprlist RP */
- 207, /* (204) expr ::= LP select RP */
- 207, /* (205) expr ::= expr in_op LP select RP */
- 207, /* (206) expr ::= expr in_op nm dbnm paren_exprlist */
- 207, /* (207) expr ::= EXISTS LP select RP */
- 207, /* (208) expr ::= CASE case_operand case_exprlist case_else END */
- 265, /* (209) case_exprlist ::= case_exprlist WHEN expr THEN expr */
- 265, /* (210) case_exprlist ::= WHEN expr THEN expr */
- 266, /* (211) case_else ::= ELSE expr */
- 266, /* (212) case_else ::= */
- 264, /* (213) case_operand ::= expr */
- 264, /* (214) case_operand ::= */
- 251, /* (215) exprlist ::= */
- 242, /* (216) nexprlist ::= nexprlist COMMA expr */
- 242, /* (217) nexprlist ::= expr */
- 263, /* (218) paren_exprlist ::= */
- 263, /* (219) paren_exprlist ::= LP exprlist RP */
- 181, /* (220) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
- 267, /* (221) uniqueflag ::= UNIQUE */
- 267, /* (222) uniqueflag ::= */
- 211, /* (223) eidlist_opt ::= */
- 211, /* (224) eidlist_opt ::= LP eidlist RP */
- 221, /* (225) eidlist ::= eidlist COMMA nm collate sortorder */
- 221, /* (226) eidlist ::= nm collate sortorder */
- 268, /* (227) collate ::= */
- 268, /* (228) collate ::= COLLATE ID|STRING */
- 181, /* (229) cmd ::= DROP INDEX ifexists fullname */
- 181, /* (230) cmd ::= VACUUM vinto */
- 181, /* (231) cmd ::= VACUUM nm vinto */
- 269, /* (232) vinto ::= INTO expr */
- 269, /* (233) vinto ::= */
- 181, /* (234) cmd ::= PRAGMA nm dbnm */
- 181, /* (235) cmd ::= PRAGMA nm dbnm EQ nmnum */
- 181, /* (236) cmd ::= PRAGMA nm dbnm LP nmnum RP */
- 181, /* (237) cmd ::= PRAGMA nm dbnm EQ minus_num */
- 181, /* (238) cmd ::= PRAGMA nm dbnm LP minus_num RP */
- 201, /* (239) plus_num ::= PLUS INTEGER|FLOAT */
- 202, /* (240) minus_num ::= MINUS INTEGER|FLOAT */
- 181, /* (241) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
- 271, /* (242) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
- 273, /* (243) trigger_time ::= BEFORE|AFTER */
- 273, /* (244) trigger_time ::= INSTEAD OF */
- 273, /* (245) trigger_time ::= */
- 274, /* (246) trigger_event ::= DELETE|INSERT */
- 274, /* (247) trigger_event ::= UPDATE */
- 274, /* (248) trigger_event ::= UPDATE OF idlist */
- 276, /* (249) when_clause ::= */
- 276, /* (250) when_clause ::= WHEN expr */
- 272, /* (251) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
- 272, /* (252) trigger_cmd_list ::= trigger_cmd SEMI */
- 278, /* (253) trnm ::= nm DOT nm */
- 279, /* (254) tridxby ::= INDEXED BY nm */
- 279, /* (255) tridxby ::= NOT INDEXED */
- 277, /* (256) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */
- 277, /* (257) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
- 277, /* (258) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
- 277, /* (259) trigger_cmd ::= scanpt select scanpt */
- 207, /* (260) expr ::= RAISE LP IGNORE RP */
- 207, /* (261) expr ::= RAISE LP raisetype COMMA nm RP */
- 225, /* (262) raisetype ::= ROLLBACK */
- 225, /* (263) raisetype ::= ABORT */
- 225, /* (264) raisetype ::= FAIL */
- 181, /* (265) cmd ::= DROP TRIGGER ifexists fullname */
- 181, /* (266) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
- 181, /* (267) cmd ::= DETACH database_kw_opt expr */
- 281, /* (268) key_opt ::= */
- 281, /* (269) key_opt ::= KEY expr */
- 181, /* (270) cmd ::= REINDEX */
- 181, /* (271) cmd ::= REINDEX nm dbnm */
- 181, /* (272) cmd ::= ANALYZE */
- 181, /* (273) cmd ::= ANALYZE nm dbnm */
- 181, /* (274) cmd ::= ALTER TABLE fullname RENAME TO nm */
- 181, /* (275) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
- 282, /* (276) add_column_fullname ::= fullname */
- 181, /* (277) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
- 181, /* (278) cmd ::= create_vtab */
- 181, /* (279) cmd ::= create_vtab LP vtabarglist RP */
- 284, /* (280) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
- 286, /* (281) vtabarg ::= */
- 287, /* (282) vtabargtoken ::= ANY */
- 287, /* (283) vtabargtoken ::= lp anylist RP */
- 288, /* (284) lp ::= LP */
- 254, /* (285) with ::= WITH wqlist */
- 254, /* (286) with ::= WITH RECURSIVE wqlist */
- 230, /* (287) wqlist ::= nm eidlist_opt AS LP select RP */
- 230, /* (288) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
- 290, /* (289) windowdefn_list ::= windowdefn */
- 290, /* (290) windowdefn_list ::= windowdefn_list COMMA windowdefn */
- 291, /* (291) windowdefn ::= nm AS LP window RP */
- 292, /* (292) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
- 292, /* (293) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
- 292, /* (294) window ::= ORDER BY sortlist frame_opt */
- 292, /* (295) window ::= nm ORDER BY sortlist frame_opt */
- 292, /* (296) window ::= frame_opt */
- 292, /* (297) window ::= nm frame_opt */
- 293, /* (298) frame_opt ::= */
- 293, /* (299) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
- 293, /* (300) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
- 296, /* (301) range_or_rows ::= RANGE|ROWS|GROUPS */
- 298, /* (302) frame_bound_s ::= frame_bound */
- 298, /* (303) frame_bound_s ::= UNBOUNDED PRECEDING */
- 299, /* (304) frame_bound_e ::= frame_bound */
- 299, /* (305) frame_bound_e ::= UNBOUNDED FOLLOWING */
- 297, /* (306) frame_bound ::= expr PRECEDING|FOLLOWING */
- 297, /* (307) frame_bound ::= CURRENT ROW */
- 300, /* (308) frame_exclude_opt ::= */
- 300, /* (309) frame_exclude_opt ::= EXCLUDE frame_exclude */
- 301, /* (310) frame_exclude ::= NO OTHERS */
- 301, /* (311) frame_exclude ::= CURRENT ROW */
- 301, /* (312) frame_exclude ::= GROUP|TIES */
- 240, /* (313) window_clause ::= WINDOW windowdefn_list */
- 259, /* (314) over_clause ::= filter_opt OVER LP window RP */
- 259, /* (315) over_clause ::= filter_opt OVER nm */
- 295, /* (316) filter_opt ::= */
- 295, /* (317) filter_opt ::= FILTER LP WHERE expr RP */
- 176, /* (318) input ::= cmdlist */
- 177, /* (319) cmdlist ::= cmdlist ecmd */
- 177, /* (320) cmdlist ::= ecmd */
- 178, /* (321) ecmd ::= SEMI */
- 178, /* (322) ecmd ::= cmdx SEMI */
- 178, /* (323) ecmd ::= explain cmdx */
- 183, /* (324) trans_opt ::= */
- 183, /* (325) trans_opt ::= TRANSACTION */
- 183, /* (326) trans_opt ::= TRANSACTION nm */
- 185, /* (327) savepoint_opt ::= SAVEPOINT */
- 185, /* (328) savepoint_opt ::= */
- 181, /* (329) cmd ::= create_table create_table_args */
- 192, /* (330) columnlist ::= columnlist COMMA columnname carglist */
- 192, /* (331) columnlist ::= columnname carglist */
- 184, /* (332) nm ::= ID|INDEXED */
- 184, /* (333) nm ::= STRING */
- 184, /* (334) nm ::= JOIN_KW */
- 198, /* (335) typetoken ::= typename */
- 199, /* (336) typename ::= ID|STRING */
- 200, /* (337) signed ::= plus_num */
- 200, /* (338) signed ::= minus_num */
- 197, /* (339) carglist ::= carglist ccons */
- 197, /* (340) carglist ::= */
- 205, /* (341) ccons ::= NULL onconf */
- 193, /* (342) conslist_opt ::= COMMA conslist */
- 217, /* (343) conslist ::= conslist tconscomma tcons */
- 217, /* (344) conslist ::= tcons */
- 218, /* (345) tconscomma ::= */
- 222, /* (346) defer_subclause_opt ::= defer_subclause */
- 224, /* (347) resolvetype ::= raisetype */
- 228, /* (348) selectnowith ::= oneselect */
- 229, /* (349) oneselect ::= values */
- 243, /* (350) sclp ::= selcollist COMMA */
- 244, /* (351) as ::= ID|STRING */
- 207, /* (352) expr ::= term */
- 260, /* (353) likeop ::= LIKE_KW|MATCH */
- 251, /* (354) exprlist ::= nexprlist */
- 270, /* (355) nmnum ::= plus_num */
- 270, /* (356) nmnum ::= nm */
- 270, /* (357) nmnum ::= ON */
- 270, /* (358) nmnum ::= DELETE */
- 270, /* (359) nmnum ::= DEFAULT */
- 201, /* (360) plus_num ::= INTEGER|FLOAT */
- 275, /* (361) foreach_clause ::= */
- 275, /* (362) foreach_clause ::= FOR EACH ROW */
- 278, /* (363) trnm ::= nm */
- 279, /* (364) tridxby ::= */
- 280, /* (365) database_kw_opt ::= DATABASE */
- 280, /* (366) database_kw_opt ::= */
- 283, /* (367) kwcolumn_opt ::= */
- 283, /* (368) kwcolumn_opt ::= COLUMNKW */
- 285, /* (369) vtabarglist ::= vtabarg */
- 285, /* (370) vtabarglist ::= vtabarglist COMMA vtabarg */
- 286, /* (371) vtabarg ::= vtabarg vtabargtoken */
- 289, /* (372) anylist ::= */
- 289, /* (373) anylist ::= anylist LP anylist RP */
- 289, /* (374) anylist ::= anylist ANY */
- 254, /* (375) with ::= */
+ 183, /* (0) explain ::= EXPLAIN */
+ 183, /* (1) explain ::= EXPLAIN QUERY PLAN */
+ 182, /* (2) cmdx ::= cmd */
+ 184, /* (3) cmd ::= BEGIN transtype trans_opt */
+ 185, /* (4) transtype ::= */
+ 185, /* (5) transtype ::= DEFERRED */
+ 185, /* (6) transtype ::= IMMEDIATE */
+ 185, /* (7) transtype ::= EXCLUSIVE */
+ 184, /* (8) cmd ::= COMMIT|END trans_opt */
+ 184, /* (9) cmd ::= ROLLBACK trans_opt */
+ 184, /* (10) cmd ::= SAVEPOINT nm */
+ 184, /* (11) cmd ::= RELEASE savepoint_opt nm */
+ 184, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */
+ 189, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */
+ 191, /* (14) createkw ::= CREATE */
+ 193, /* (15) ifnotexists ::= */
+ 193, /* (16) ifnotexists ::= IF NOT EXISTS */
+ 192, /* (17) temp ::= TEMP */
+ 192, /* (18) temp ::= */
+ 190, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_options */
+ 190, /* (20) create_table_args ::= AS select */
+ 197, /* (21) table_options ::= */
+ 197, /* (22) table_options ::= WITHOUT nm */
+ 199, /* (23) columnname ::= nm typetoken */
+ 201, /* (24) typetoken ::= */
+ 201, /* (25) typetoken ::= typename LP signed RP */
+ 201, /* (26) typetoken ::= typename LP signed COMMA signed RP */
+ 202, /* (27) typename ::= typename ID|STRING */
+ 206, /* (28) scanpt ::= */
+ 207, /* (29) scantok ::= */
+ 208, /* (30) ccons ::= CONSTRAINT nm */
+ 208, /* (31) ccons ::= DEFAULT scantok term */
+ 208, /* (32) ccons ::= DEFAULT LP expr RP */
+ 208, /* (33) ccons ::= DEFAULT PLUS scantok term */
+ 208, /* (34) ccons ::= DEFAULT MINUS scantok term */
+ 208, /* (35) ccons ::= DEFAULT scantok ID|INDEXED */
+ 208, /* (36) ccons ::= NOT NULL onconf */
+ 208, /* (37) ccons ::= PRIMARY KEY sortorder onconf autoinc */
+ 208, /* (38) ccons ::= UNIQUE onconf */
+ 208, /* (39) ccons ::= CHECK LP expr RP */
+ 208, /* (40) ccons ::= REFERENCES nm eidlist_opt refargs */
+ 208, /* (41) ccons ::= defer_subclause */
+ 208, /* (42) ccons ::= COLLATE ID|STRING */
+ 213, /* (43) autoinc ::= */
+ 213, /* (44) autoinc ::= AUTOINCR */
+ 215, /* (45) refargs ::= */
+ 215, /* (46) refargs ::= refargs refarg */
+ 217, /* (47) refarg ::= MATCH nm */
+ 217, /* (48) refarg ::= ON INSERT refact */
+ 217, /* (49) refarg ::= ON DELETE refact */
+ 217, /* (50) refarg ::= ON UPDATE refact */
+ 218, /* (51) refact ::= SET NULL */
+ 218, /* (52) refact ::= SET DEFAULT */
+ 218, /* (53) refact ::= CASCADE */
+ 218, /* (54) refact ::= RESTRICT */
+ 218, /* (55) refact ::= NO ACTION */
+ 216, /* (56) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
+ 216, /* (57) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
+ 219, /* (58) init_deferred_pred_opt ::= */
+ 219, /* (59) init_deferred_pred_opt ::= INITIALLY DEFERRED */
+ 219, /* (60) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
+ 196, /* (61) conslist_opt ::= */
+ 221, /* (62) tconscomma ::= COMMA */
+ 222, /* (63) tcons ::= CONSTRAINT nm */
+ 222, /* (64) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
+ 222, /* (65) tcons ::= UNIQUE LP sortlist RP onconf */
+ 222, /* (66) tcons ::= CHECK LP expr RP onconf */
+ 222, /* (67) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
+ 225, /* (68) defer_subclause_opt ::= */
+ 211, /* (69) onconf ::= */
+ 211, /* (70) onconf ::= ON CONFLICT resolvetype */
+ 226, /* (71) orconf ::= */
+ 226, /* (72) orconf ::= OR resolvetype */
+ 227, /* (73) resolvetype ::= IGNORE */
+ 227, /* (74) resolvetype ::= REPLACE */
+ 184, /* (75) cmd ::= DROP TABLE ifexists fullname */
+ 229, /* (76) ifexists ::= IF EXISTS */
+ 229, /* (77) ifexists ::= */
+ 184, /* (78) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
+ 184, /* (79) cmd ::= DROP VIEW ifexists fullname */
+ 184, /* (80) cmd ::= select */
+ 198, /* (81) select ::= WITH wqlist selectnowith */
+ 198, /* (82) select ::= WITH RECURSIVE wqlist selectnowith */
+ 198, /* (83) select ::= selectnowith */
+ 231, /* (84) selectnowith ::= selectnowith multiselect_op oneselect */
+ 234, /* (85) multiselect_op ::= UNION */
+ 234, /* (86) multiselect_op ::= UNION ALL */
+ 234, /* (87) multiselect_op ::= EXCEPT|INTERSECT */
+ 232, /* (88) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
+ 232, /* (89) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
+ 244, /* (90) values ::= VALUES LP nexprlist RP */
+ 244, /* (91) values ::= values COMMA LP nexprlist RP */
+ 235, /* (92) distinct ::= DISTINCT */
+ 235, /* (93) distinct ::= ALL */
+ 235, /* (94) distinct ::= */
+ 246, /* (95) sclp ::= */
+ 236, /* (96) selcollist ::= sclp scanpt expr scanpt as */
+ 236, /* (97) selcollist ::= sclp scanpt STAR */
+ 236, /* (98) selcollist ::= sclp scanpt nm DOT STAR */
+ 247, /* (99) as ::= AS nm */
+ 247, /* (100) as ::= */
+ 237, /* (101) from ::= */
+ 237, /* (102) from ::= FROM seltablist */
+ 249, /* (103) stl_prefix ::= seltablist joinop */
+ 249, /* (104) stl_prefix ::= */
+ 248, /* (105) seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */
+ 248, /* (106) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */
+ 248, /* (107) seltablist ::= stl_prefix LP select RP as on_opt using_opt */
+ 248, /* (108) seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */
+ 194, /* (109) dbnm ::= */
+ 194, /* (110) dbnm ::= DOT nm */
+ 230, /* (111) fullname ::= nm */
+ 230, /* (112) fullname ::= nm DOT nm */
+ 255, /* (113) xfullname ::= nm */
+ 255, /* (114) xfullname ::= nm DOT nm */
+ 255, /* (115) xfullname ::= nm DOT nm AS nm */
+ 255, /* (116) xfullname ::= nm AS nm */
+ 250, /* (117) joinop ::= COMMA|JOIN */
+ 250, /* (118) joinop ::= JOIN_KW JOIN */
+ 250, /* (119) joinop ::= JOIN_KW nm JOIN */
+ 250, /* (120) joinop ::= JOIN_KW nm nm JOIN */
+ 252, /* (121) on_opt ::= ON expr */
+ 252, /* (122) on_opt ::= */
+ 251, /* (123) indexed_opt ::= */
+ 251, /* (124) indexed_opt ::= INDEXED BY nm */
+ 251, /* (125) indexed_opt ::= NOT INDEXED */
+ 253, /* (126) using_opt ::= USING LP idlist RP */
+ 253, /* (127) using_opt ::= */
+ 241, /* (128) orderby_opt ::= */
+ 241, /* (129) orderby_opt ::= ORDER BY sortlist */
+ 223, /* (130) sortlist ::= sortlist COMMA expr sortorder nulls */
+ 223, /* (131) sortlist ::= expr sortorder nulls */
+ 212, /* (132) sortorder ::= ASC */
+ 212, /* (133) sortorder ::= DESC */
+ 212, /* (134) sortorder ::= */
+ 257, /* (135) nulls ::= NULLS FIRST */
+ 257, /* (136) nulls ::= NULLS LAST */
+ 257, /* (137) nulls ::= */
+ 239, /* (138) groupby_opt ::= */
+ 239, /* (139) groupby_opt ::= GROUP BY nexprlist */
+ 240, /* (140) having_opt ::= */
+ 240, /* (141) having_opt ::= HAVING expr */
+ 242, /* (142) limit_opt ::= */
+ 242, /* (143) limit_opt ::= LIMIT expr */
+ 242, /* (144) limit_opt ::= LIMIT expr OFFSET expr */
+ 242, /* (145) limit_opt ::= LIMIT expr COMMA expr */
+ 184, /* (146) cmd ::= with DELETE FROM xfullname indexed_opt where_opt */
+ 238, /* (147) where_opt ::= */
+ 238, /* (148) where_opt ::= WHERE expr */
+ 184, /* (149) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */
+ 259, /* (150) setlist ::= setlist COMMA nm EQ expr */
+ 259, /* (151) setlist ::= setlist COMMA LP idlist RP EQ expr */
+ 259, /* (152) setlist ::= nm EQ expr */
+ 259, /* (153) setlist ::= LP idlist RP EQ expr */
+ 184, /* (154) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
+ 184, /* (155) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */
+ 262, /* (156) upsert ::= */
+ 262, /* (157) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */
+ 262, /* (158) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */
+ 262, /* (159) upsert ::= ON CONFLICT DO NOTHING */
+ 260, /* (160) insert_cmd ::= INSERT orconf */
+ 260, /* (161) insert_cmd ::= REPLACE */
+ 261, /* (162) idlist_opt ::= */
+ 261, /* (163) idlist_opt ::= LP idlist RP */
+ 256, /* (164) idlist ::= idlist COMMA nm */
+ 256, /* (165) idlist ::= nm */
+ 210, /* (166) expr ::= LP expr RP */
+ 210, /* (167) expr ::= ID|INDEXED */
+ 210, /* (168) expr ::= JOIN_KW */
+ 210, /* (169) expr ::= nm DOT nm */
+ 210, /* (170) expr ::= nm DOT nm DOT nm */
+ 209, /* (171) term ::= NULL|FLOAT|BLOB */
+ 209, /* (172) term ::= STRING */
+ 209, /* (173) term ::= INTEGER */
+ 210, /* (174) expr ::= VARIABLE */
+ 210, /* (175) expr ::= expr COLLATE ID|STRING */
+ 210, /* (176) expr ::= CAST LP expr AS typetoken RP */
+ 210, /* (177) expr ::= ID|INDEXED LP distinct exprlist RP */
+ 210, /* (178) expr ::= ID|INDEXED LP STAR RP */
+ 210, /* (179) expr ::= ID|INDEXED LP distinct exprlist RP filter_over */
+ 210, /* (180) expr ::= ID|INDEXED LP STAR RP filter_over */
+ 209, /* (181) term ::= CTIME_KW */
+ 210, /* (182) expr ::= LP nexprlist COMMA expr RP */
+ 210, /* (183) expr ::= expr AND expr */
+ 210, /* (184) expr ::= expr OR expr */
+ 210, /* (185) expr ::= expr LT|GT|GE|LE expr */
+ 210, /* (186) expr ::= expr EQ|NE expr */
+ 210, /* (187) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
+ 210, /* (188) expr ::= expr PLUS|MINUS expr */
+ 210, /* (189) expr ::= expr STAR|SLASH|REM expr */
+ 210, /* (190) expr ::= expr CONCAT expr */
+ 264, /* (191) likeop ::= NOT LIKE_KW|MATCH */
+ 210, /* (192) expr ::= expr likeop expr */
+ 210, /* (193) expr ::= expr likeop expr ESCAPE expr */
+ 210, /* (194) expr ::= expr ISNULL|NOTNULL */
+ 210, /* (195) expr ::= expr NOT NULL */
+ 210, /* (196) expr ::= expr IS expr */
+ 210, /* (197) expr ::= expr IS NOT expr */
+ 210, /* (198) expr ::= NOT expr */
+ 210, /* (199) expr ::= BITNOT expr */
+ 210, /* (200) expr ::= PLUS|MINUS expr */
+ 265, /* (201) between_op ::= BETWEEN */
+ 265, /* (202) between_op ::= NOT BETWEEN */
+ 210, /* (203) expr ::= expr between_op expr AND expr */
+ 266, /* (204) in_op ::= IN */
+ 266, /* (205) in_op ::= NOT IN */
+ 210, /* (206) expr ::= expr in_op LP exprlist RP */
+ 210, /* (207) expr ::= LP select RP */
+ 210, /* (208) expr ::= expr in_op LP select RP */
+ 210, /* (209) expr ::= expr in_op nm dbnm paren_exprlist */
+ 210, /* (210) expr ::= EXISTS LP select RP */
+ 210, /* (211) expr ::= CASE case_operand case_exprlist case_else END */
+ 269, /* (212) case_exprlist ::= case_exprlist WHEN expr THEN expr */
+ 269, /* (213) case_exprlist ::= WHEN expr THEN expr */
+ 270, /* (214) case_else ::= ELSE expr */
+ 270, /* (215) case_else ::= */
+ 268, /* (216) case_operand ::= expr */
+ 268, /* (217) case_operand ::= */
+ 254, /* (218) exprlist ::= */
+ 245, /* (219) nexprlist ::= nexprlist COMMA expr */
+ 245, /* (220) nexprlist ::= expr */
+ 267, /* (221) paren_exprlist ::= */
+ 267, /* (222) paren_exprlist ::= LP exprlist RP */
+ 184, /* (223) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
+ 271, /* (224) uniqueflag ::= UNIQUE */
+ 271, /* (225) uniqueflag ::= */
+ 214, /* (226) eidlist_opt ::= */
+ 214, /* (227) eidlist_opt ::= LP eidlist RP */
+ 224, /* (228) eidlist ::= eidlist COMMA nm collate sortorder */
+ 224, /* (229) eidlist ::= nm collate sortorder */
+ 272, /* (230) collate ::= */
+ 272, /* (231) collate ::= COLLATE ID|STRING */
+ 184, /* (232) cmd ::= DROP INDEX ifexists fullname */
+ 184, /* (233) cmd ::= VACUUM vinto */
+ 184, /* (234) cmd ::= VACUUM nm vinto */
+ 273, /* (235) vinto ::= INTO expr */
+ 273, /* (236) vinto ::= */
+ 184, /* (237) cmd ::= PRAGMA nm dbnm */
+ 184, /* (238) cmd ::= PRAGMA nm dbnm EQ nmnum */
+ 184, /* (239) cmd ::= PRAGMA nm dbnm LP nmnum RP */
+ 184, /* (240) cmd ::= PRAGMA nm dbnm EQ minus_num */
+ 184, /* (241) cmd ::= PRAGMA nm dbnm LP minus_num RP */
+ 204, /* (242) plus_num ::= PLUS INTEGER|FLOAT */
+ 205, /* (243) minus_num ::= MINUS INTEGER|FLOAT */
+ 184, /* (244) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
+ 275, /* (245) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
+ 277, /* (246) trigger_time ::= BEFORE|AFTER */
+ 277, /* (247) trigger_time ::= INSTEAD OF */
+ 277, /* (248) trigger_time ::= */
+ 278, /* (249) trigger_event ::= DELETE|INSERT */
+ 278, /* (250) trigger_event ::= UPDATE */
+ 278, /* (251) trigger_event ::= UPDATE OF idlist */
+ 280, /* (252) when_clause ::= */
+ 280, /* (253) when_clause ::= WHEN expr */
+ 276, /* (254) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
+ 276, /* (255) trigger_cmd_list ::= trigger_cmd SEMI */
+ 282, /* (256) trnm ::= nm DOT nm */
+ 283, /* (257) tridxby ::= INDEXED BY nm */
+ 283, /* (258) tridxby ::= NOT INDEXED */
+ 281, /* (259) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */
+ 281, /* (260) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
+ 281, /* (261) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
+ 281, /* (262) trigger_cmd ::= scanpt select scanpt */
+ 210, /* (263) expr ::= RAISE LP IGNORE RP */
+ 210, /* (264) expr ::= RAISE LP raisetype COMMA nm RP */
+ 228, /* (265) raisetype ::= ROLLBACK */
+ 228, /* (266) raisetype ::= ABORT */
+ 228, /* (267) raisetype ::= FAIL */
+ 184, /* (268) cmd ::= DROP TRIGGER ifexists fullname */
+ 184, /* (269) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+ 184, /* (270) cmd ::= DETACH database_kw_opt expr */
+ 285, /* (271) key_opt ::= */
+ 285, /* (272) key_opt ::= KEY expr */
+ 184, /* (273) cmd ::= REINDEX */
+ 184, /* (274) cmd ::= REINDEX nm dbnm */
+ 184, /* (275) cmd ::= ANALYZE */
+ 184, /* (276) cmd ::= ANALYZE nm dbnm */
+ 184, /* (277) cmd ::= ALTER TABLE fullname RENAME TO nm */
+ 184, /* (278) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
+ 286, /* (279) add_column_fullname ::= fullname */
+ 184, /* (280) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
+ 184, /* (281) cmd ::= create_vtab */
+ 184, /* (282) cmd ::= create_vtab LP vtabarglist RP */
+ 288, /* (283) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
+ 290, /* (284) vtabarg ::= */
+ 291, /* (285) vtabargtoken ::= ANY */
+ 291, /* (286) vtabargtoken ::= lp anylist RP */
+ 292, /* (287) lp ::= LP */
+ 258, /* (288) with ::= WITH wqlist */
+ 258, /* (289) with ::= WITH RECURSIVE wqlist */
+ 233, /* (290) wqlist ::= nm eidlist_opt AS LP select RP */
+ 233, /* (291) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
+ 294, /* (292) windowdefn_list ::= windowdefn */
+ 294, /* (293) windowdefn_list ::= windowdefn_list COMMA windowdefn */
+ 295, /* (294) windowdefn ::= nm AS LP window RP */
+ 296, /* (295) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
+ 296, /* (296) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
+ 296, /* (297) window ::= ORDER BY sortlist frame_opt */
+ 296, /* (298) window ::= nm ORDER BY sortlist frame_opt */
+ 296, /* (299) window ::= frame_opt */
+ 296, /* (300) window ::= nm frame_opt */
+ 297, /* (301) frame_opt ::= */
+ 297, /* (302) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
+ 297, /* (303) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
+ 301, /* (304) range_or_rows ::= RANGE|ROWS|GROUPS */
+ 303, /* (305) frame_bound_s ::= frame_bound */
+ 303, /* (306) frame_bound_s ::= UNBOUNDED PRECEDING */
+ 304, /* (307) frame_bound_e ::= frame_bound */
+ 304, /* (308) frame_bound_e ::= UNBOUNDED FOLLOWING */
+ 302, /* (309) frame_bound ::= expr PRECEDING|FOLLOWING */
+ 302, /* (310) frame_bound ::= CURRENT ROW */
+ 305, /* (311) frame_exclude_opt ::= */
+ 305, /* (312) frame_exclude_opt ::= EXCLUDE frame_exclude */
+ 306, /* (313) frame_exclude ::= NO OTHERS */
+ 306, /* (314) frame_exclude ::= CURRENT ROW */
+ 306, /* (315) frame_exclude ::= GROUP|TIES */
+ 243, /* (316) window_clause ::= WINDOW windowdefn_list */
+ 263, /* (317) filter_over ::= filter_clause over_clause */
+ 263, /* (318) filter_over ::= over_clause */
+ 263, /* (319) filter_over ::= filter_clause */
+ 300, /* (320) over_clause ::= OVER LP window RP */
+ 300, /* (321) over_clause ::= OVER nm */
+ 299, /* (322) filter_clause ::= FILTER LP WHERE expr RP */
+ 179, /* (323) input ::= cmdlist */
+ 180, /* (324) cmdlist ::= cmdlist ecmd */
+ 180, /* (325) cmdlist ::= ecmd */
+ 181, /* (326) ecmd ::= SEMI */
+ 181, /* (327) ecmd ::= cmdx SEMI */
+ 181, /* (328) ecmd ::= explain cmdx */
+ 186, /* (329) trans_opt ::= */
+ 186, /* (330) trans_opt ::= TRANSACTION */
+ 186, /* (331) trans_opt ::= TRANSACTION nm */
+ 188, /* (332) savepoint_opt ::= SAVEPOINT */
+ 188, /* (333) savepoint_opt ::= */
+ 184, /* (334) cmd ::= create_table create_table_args */
+ 195, /* (335) columnlist ::= columnlist COMMA columnname carglist */
+ 195, /* (336) columnlist ::= columnname carglist */
+ 187, /* (337) nm ::= ID|INDEXED */
+ 187, /* (338) nm ::= STRING */
+ 187, /* (339) nm ::= JOIN_KW */
+ 201, /* (340) typetoken ::= typename */
+ 202, /* (341) typename ::= ID|STRING */
+ 203, /* (342) signed ::= plus_num */
+ 203, /* (343) signed ::= minus_num */
+ 200, /* (344) carglist ::= carglist ccons */
+ 200, /* (345) carglist ::= */
+ 208, /* (346) ccons ::= NULL onconf */
+ 196, /* (347) conslist_opt ::= COMMA conslist */
+ 220, /* (348) conslist ::= conslist tconscomma tcons */
+ 220, /* (349) conslist ::= tcons */
+ 221, /* (350) tconscomma ::= */
+ 225, /* (351) defer_subclause_opt ::= defer_subclause */
+ 227, /* (352) resolvetype ::= raisetype */
+ 231, /* (353) selectnowith ::= oneselect */
+ 232, /* (354) oneselect ::= values */
+ 246, /* (355) sclp ::= selcollist COMMA */
+ 247, /* (356) as ::= ID|STRING */
+ 210, /* (357) expr ::= term */
+ 264, /* (358) likeop ::= LIKE_KW|MATCH */
+ 254, /* (359) exprlist ::= nexprlist */
+ 274, /* (360) nmnum ::= plus_num */
+ 274, /* (361) nmnum ::= nm */
+ 274, /* (362) nmnum ::= ON */
+ 274, /* (363) nmnum ::= DELETE */
+ 274, /* (364) nmnum ::= DEFAULT */
+ 204, /* (365) plus_num ::= INTEGER|FLOAT */
+ 279, /* (366) foreach_clause ::= */
+ 279, /* (367) foreach_clause ::= FOR EACH ROW */
+ 282, /* (368) trnm ::= nm */
+ 283, /* (369) tridxby ::= */
+ 284, /* (370) database_kw_opt ::= DATABASE */
+ 284, /* (371) database_kw_opt ::= */
+ 287, /* (372) kwcolumn_opt ::= */
+ 287, /* (373) kwcolumn_opt ::= COLUMNKW */
+ 289, /* (374) vtabarglist ::= vtabarg */
+ 289, /* (375) vtabarglist ::= vtabarglist COMMA vtabarg */
+ 290, /* (376) vtabarg ::= vtabarg vtabargtoken */
+ 293, /* (377) anylist ::= */
+ 293, /* (378) anylist ::= anylist LP anylist RP */
+ 293, /* (379) anylist ::= anylist ANY */
+ 258, /* (380) with ::= */
};
/* For rule J, yyRuleInfoNRhs[J] contains the negative of the number
@@ -151560,252 +152594,257 @@ static const signed char yyRuleInfoNRhs[] = {
0, /* (127) using_opt ::= */
0, /* (128) orderby_opt ::= */
-3, /* (129) orderby_opt ::= ORDER BY sortlist */
- -4, /* (130) sortlist ::= sortlist COMMA expr sortorder */
- -2, /* (131) sortlist ::= expr sortorder */
+ -5, /* (130) sortlist ::= sortlist COMMA expr sortorder nulls */
+ -3, /* (131) sortlist ::= expr sortorder nulls */
-1, /* (132) sortorder ::= ASC */
-1, /* (133) sortorder ::= DESC */
0, /* (134) sortorder ::= */
- 0, /* (135) groupby_opt ::= */
- -3, /* (136) groupby_opt ::= GROUP BY nexprlist */
- 0, /* (137) having_opt ::= */
- -2, /* (138) having_opt ::= HAVING expr */
- 0, /* (139) limit_opt ::= */
- -2, /* (140) limit_opt ::= LIMIT expr */
- -4, /* (141) limit_opt ::= LIMIT expr OFFSET expr */
- -4, /* (142) limit_opt ::= LIMIT expr COMMA expr */
- -6, /* (143) cmd ::= with DELETE FROM xfullname indexed_opt where_opt */
- 0, /* (144) where_opt ::= */
- -2, /* (145) where_opt ::= WHERE expr */
- -8, /* (146) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */
- -5, /* (147) setlist ::= setlist COMMA nm EQ expr */
- -7, /* (148) setlist ::= setlist COMMA LP idlist RP EQ expr */
- -3, /* (149) setlist ::= nm EQ expr */
- -5, /* (150) setlist ::= LP idlist RP EQ expr */
- -7, /* (151) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
- -7, /* (152) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */
- 0, /* (153) upsert ::= */
- -11, /* (154) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */
- -8, /* (155) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */
- -4, /* (156) upsert ::= ON CONFLICT DO NOTHING */
- -2, /* (157) insert_cmd ::= INSERT orconf */
- -1, /* (158) insert_cmd ::= REPLACE */
- 0, /* (159) idlist_opt ::= */
- -3, /* (160) idlist_opt ::= LP idlist RP */
- -3, /* (161) idlist ::= idlist COMMA nm */
- -1, /* (162) idlist ::= nm */
- -3, /* (163) expr ::= LP expr RP */
- -1, /* (164) expr ::= ID|INDEXED */
- -1, /* (165) expr ::= JOIN_KW */
- -3, /* (166) expr ::= nm DOT nm */
- -5, /* (167) expr ::= nm DOT nm DOT nm */
- -1, /* (168) term ::= NULL|FLOAT|BLOB */
- -1, /* (169) term ::= STRING */
- -1, /* (170) term ::= INTEGER */
- -1, /* (171) expr ::= VARIABLE */
- -3, /* (172) expr ::= expr COLLATE ID|STRING */
- -6, /* (173) expr ::= CAST LP expr AS typetoken RP */
- -5, /* (174) expr ::= ID|INDEXED LP distinct exprlist RP */
- -4, /* (175) expr ::= ID|INDEXED LP STAR RP */
- -6, /* (176) expr ::= ID|INDEXED LP distinct exprlist RP over_clause */
- -5, /* (177) expr ::= ID|INDEXED LP STAR RP over_clause */
- -1, /* (178) term ::= CTIME_KW */
- -5, /* (179) expr ::= LP nexprlist COMMA expr RP */
- -3, /* (180) expr ::= expr AND expr */
- -3, /* (181) expr ::= expr OR expr */
- -3, /* (182) expr ::= expr LT|GT|GE|LE expr */
- -3, /* (183) expr ::= expr EQ|NE expr */
- -3, /* (184) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
- -3, /* (185) expr ::= expr PLUS|MINUS expr */
- -3, /* (186) expr ::= expr STAR|SLASH|REM expr */
- -3, /* (187) expr ::= expr CONCAT expr */
- -2, /* (188) likeop ::= NOT LIKE_KW|MATCH */
- -3, /* (189) expr ::= expr likeop expr */
- -5, /* (190) expr ::= expr likeop expr ESCAPE expr */
- -2, /* (191) expr ::= expr ISNULL|NOTNULL */
- -3, /* (192) expr ::= expr NOT NULL */
- -3, /* (193) expr ::= expr IS expr */
- -4, /* (194) expr ::= expr IS NOT expr */
- -2, /* (195) expr ::= NOT expr */
- -2, /* (196) expr ::= BITNOT expr */
- -2, /* (197) expr ::= PLUS|MINUS expr */
- -1, /* (198) between_op ::= BETWEEN */
- -2, /* (199) between_op ::= NOT BETWEEN */
- -5, /* (200) expr ::= expr between_op expr AND expr */
- -1, /* (201) in_op ::= IN */
- -2, /* (202) in_op ::= NOT IN */
- -5, /* (203) expr ::= expr in_op LP exprlist RP */
- -3, /* (204) expr ::= LP select RP */
- -5, /* (205) expr ::= expr in_op LP select RP */
- -5, /* (206) expr ::= expr in_op nm dbnm paren_exprlist */
- -4, /* (207) expr ::= EXISTS LP select RP */
- -5, /* (208) expr ::= CASE case_operand case_exprlist case_else END */
- -5, /* (209) case_exprlist ::= case_exprlist WHEN expr THEN expr */
- -4, /* (210) case_exprlist ::= WHEN expr THEN expr */
- -2, /* (211) case_else ::= ELSE expr */
- 0, /* (212) case_else ::= */
- -1, /* (213) case_operand ::= expr */
- 0, /* (214) case_operand ::= */
- 0, /* (215) exprlist ::= */
- -3, /* (216) nexprlist ::= nexprlist COMMA expr */
- -1, /* (217) nexprlist ::= expr */
- 0, /* (218) paren_exprlist ::= */
- -3, /* (219) paren_exprlist ::= LP exprlist RP */
- -12, /* (220) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
- -1, /* (221) uniqueflag ::= UNIQUE */
- 0, /* (222) uniqueflag ::= */
- 0, /* (223) eidlist_opt ::= */
- -3, /* (224) eidlist_opt ::= LP eidlist RP */
- -5, /* (225) eidlist ::= eidlist COMMA nm collate sortorder */
- -3, /* (226) eidlist ::= nm collate sortorder */
- 0, /* (227) collate ::= */
- -2, /* (228) collate ::= COLLATE ID|STRING */
- -4, /* (229) cmd ::= DROP INDEX ifexists fullname */
- -2, /* (230) cmd ::= VACUUM vinto */
- -3, /* (231) cmd ::= VACUUM nm vinto */
- -2, /* (232) vinto ::= INTO expr */
- 0, /* (233) vinto ::= */
- -3, /* (234) cmd ::= PRAGMA nm dbnm */
- -5, /* (235) cmd ::= PRAGMA nm dbnm EQ nmnum */
- -6, /* (236) cmd ::= PRAGMA nm dbnm LP nmnum RP */
- -5, /* (237) cmd ::= PRAGMA nm dbnm EQ minus_num */
- -6, /* (238) cmd ::= PRAGMA nm dbnm LP minus_num RP */
- -2, /* (239) plus_num ::= PLUS INTEGER|FLOAT */
- -2, /* (240) minus_num ::= MINUS INTEGER|FLOAT */
- -5, /* (241) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
- -11, /* (242) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
- -1, /* (243) trigger_time ::= BEFORE|AFTER */
- -2, /* (244) trigger_time ::= INSTEAD OF */
- 0, /* (245) trigger_time ::= */
- -1, /* (246) trigger_event ::= DELETE|INSERT */
- -1, /* (247) trigger_event ::= UPDATE */
- -3, /* (248) trigger_event ::= UPDATE OF idlist */
- 0, /* (249) when_clause ::= */
- -2, /* (250) when_clause ::= WHEN expr */
- -3, /* (251) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
- -2, /* (252) trigger_cmd_list ::= trigger_cmd SEMI */
- -3, /* (253) trnm ::= nm DOT nm */
- -3, /* (254) tridxby ::= INDEXED BY nm */
- -2, /* (255) tridxby ::= NOT INDEXED */
- -8, /* (256) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */
- -8, /* (257) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
- -6, /* (258) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
- -3, /* (259) trigger_cmd ::= scanpt select scanpt */
- -4, /* (260) expr ::= RAISE LP IGNORE RP */
- -6, /* (261) expr ::= RAISE LP raisetype COMMA nm RP */
- -1, /* (262) raisetype ::= ROLLBACK */
- -1, /* (263) raisetype ::= ABORT */
- -1, /* (264) raisetype ::= FAIL */
- -4, /* (265) cmd ::= DROP TRIGGER ifexists fullname */
- -6, /* (266) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
- -3, /* (267) cmd ::= DETACH database_kw_opt expr */
- 0, /* (268) key_opt ::= */
- -2, /* (269) key_opt ::= KEY expr */
- -1, /* (270) cmd ::= REINDEX */
- -3, /* (271) cmd ::= REINDEX nm dbnm */
- -1, /* (272) cmd ::= ANALYZE */
- -3, /* (273) cmd ::= ANALYZE nm dbnm */
- -6, /* (274) cmd ::= ALTER TABLE fullname RENAME TO nm */
- -7, /* (275) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
- -1, /* (276) add_column_fullname ::= fullname */
- -8, /* (277) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
- -1, /* (278) cmd ::= create_vtab */
- -4, /* (279) cmd ::= create_vtab LP vtabarglist RP */
- -8, /* (280) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
- 0, /* (281) vtabarg ::= */
- -1, /* (282) vtabargtoken ::= ANY */
- -3, /* (283) vtabargtoken ::= lp anylist RP */
- -1, /* (284) lp ::= LP */
- -2, /* (285) with ::= WITH wqlist */
- -3, /* (286) with ::= WITH RECURSIVE wqlist */
- -6, /* (287) wqlist ::= nm eidlist_opt AS LP select RP */
- -8, /* (288) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
- -1, /* (289) windowdefn_list ::= windowdefn */
- -3, /* (290) windowdefn_list ::= windowdefn_list COMMA windowdefn */
- -5, /* (291) windowdefn ::= nm AS LP window RP */
- -5, /* (292) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
- -6, /* (293) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
- -4, /* (294) window ::= ORDER BY sortlist frame_opt */
- -5, /* (295) window ::= nm ORDER BY sortlist frame_opt */
- -1, /* (296) window ::= frame_opt */
- -2, /* (297) window ::= nm frame_opt */
- 0, /* (298) frame_opt ::= */
- -3, /* (299) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
- -6, /* (300) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
- -1, /* (301) range_or_rows ::= RANGE|ROWS|GROUPS */
- -1, /* (302) frame_bound_s ::= frame_bound */
- -2, /* (303) frame_bound_s ::= UNBOUNDED PRECEDING */
- -1, /* (304) frame_bound_e ::= frame_bound */
- -2, /* (305) frame_bound_e ::= UNBOUNDED FOLLOWING */
- -2, /* (306) frame_bound ::= expr PRECEDING|FOLLOWING */
- -2, /* (307) frame_bound ::= CURRENT ROW */
- 0, /* (308) frame_exclude_opt ::= */
- -2, /* (309) frame_exclude_opt ::= EXCLUDE frame_exclude */
- -2, /* (310) frame_exclude ::= NO OTHERS */
- -2, /* (311) frame_exclude ::= CURRENT ROW */
- -1, /* (312) frame_exclude ::= GROUP|TIES */
- -2, /* (313) window_clause ::= WINDOW windowdefn_list */
- -5, /* (314) over_clause ::= filter_opt OVER LP window RP */
- -3, /* (315) over_clause ::= filter_opt OVER nm */
- 0, /* (316) filter_opt ::= */
- -5, /* (317) filter_opt ::= FILTER LP WHERE expr RP */
- -1, /* (318) input ::= cmdlist */
- -2, /* (319) cmdlist ::= cmdlist ecmd */
- -1, /* (320) cmdlist ::= ecmd */
- -1, /* (321) ecmd ::= SEMI */
- -2, /* (322) ecmd ::= cmdx SEMI */
- -2, /* (323) ecmd ::= explain cmdx */
- 0, /* (324) trans_opt ::= */
- -1, /* (325) trans_opt ::= TRANSACTION */
- -2, /* (326) trans_opt ::= TRANSACTION nm */
- -1, /* (327) savepoint_opt ::= SAVEPOINT */
- 0, /* (328) savepoint_opt ::= */
- -2, /* (329) cmd ::= create_table create_table_args */
- -4, /* (330) columnlist ::= columnlist COMMA columnname carglist */
- -2, /* (331) columnlist ::= columnname carglist */
- -1, /* (332) nm ::= ID|INDEXED */
- -1, /* (333) nm ::= STRING */
- -1, /* (334) nm ::= JOIN_KW */
- -1, /* (335) typetoken ::= typename */
- -1, /* (336) typename ::= ID|STRING */
- -1, /* (337) signed ::= plus_num */
- -1, /* (338) signed ::= minus_num */
- -2, /* (339) carglist ::= carglist ccons */
- 0, /* (340) carglist ::= */
- -2, /* (341) ccons ::= NULL onconf */
- -2, /* (342) conslist_opt ::= COMMA conslist */
- -3, /* (343) conslist ::= conslist tconscomma tcons */
- -1, /* (344) conslist ::= tcons */
- 0, /* (345) tconscomma ::= */
- -1, /* (346) defer_subclause_opt ::= defer_subclause */
- -1, /* (347) resolvetype ::= raisetype */
- -1, /* (348) selectnowith ::= oneselect */
- -1, /* (349) oneselect ::= values */
- -2, /* (350) sclp ::= selcollist COMMA */
- -1, /* (351) as ::= ID|STRING */
- -1, /* (352) expr ::= term */
- -1, /* (353) likeop ::= LIKE_KW|MATCH */
- -1, /* (354) exprlist ::= nexprlist */
- -1, /* (355) nmnum ::= plus_num */
- -1, /* (356) nmnum ::= nm */
- -1, /* (357) nmnum ::= ON */
- -1, /* (358) nmnum ::= DELETE */
- -1, /* (359) nmnum ::= DEFAULT */
- -1, /* (360) plus_num ::= INTEGER|FLOAT */
- 0, /* (361) foreach_clause ::= */
- -3, /* (362) foreach_clause ::= FOR EACH ROW */
- -1, /* (363) trnm ::= nm */
- 0, /* (364) tridxby ::= */
- -1, /* (365) database_kw_opt ::= DATABASE */
- 0, /* (366) database_kw_opt ::= */
- 0, /* (367) kwcolumn_opt ::= */
- -1, /* (368) kwcolumn_opt ::= COLUMNKW */
- -1, /* (369) vtabarglist ::= vtabarg */
- -3, /* (370) vtabarglist ::= vtabarglist COMMA vtabarg */
- -2, /* (371) vtabarg ::= vtabarg vtabargtoken */
- 0, /* (372) anylist ::= */
- -4, /* (373) anylist ::= anylist LP anylist RP */
- -2, /* (374) anylist ::= anylist ANY */
- 0, /* (375) with ::= */
+ -2, /* (135) nulls ::= NULLS FIRST */
+ -2, /* (136) nulls ::= NULLS LAST */
+ 0, /* (137) nulls ::= */
+ 0, /* (138) groupby_opt ::= */
+ -3, /* (139) groupby_opt ::= GROUP BY nexprlist */
+ 0, /* (140) having_opt ::= */
+ -2, /* (141) having_opt ::= HAVING expr */
+ 0, /* (142) limit_opt ::= */
+ -2, /* (143) limit_opt ::= LIMIT expr */
+ -4, /* (144) limit_opt ::= LIMIT expr OFFSET expr */
+ -4, /* (145) limit_opt ::= LIMIT expr COMMA expr */
+ -6, /* (146) cmd ::= with DELETE FROM xfullname indexed_opt where_opt */
+ 0, /* (147) where_opt ::= */
+ -2, /* (148) where_opt ::= WHERE expr */
+ -8, /* (149) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */
+ -5, /* (150) setlist ::= setlist COMMA nm EQ expr */
+ -7, /* (151) setlist ::= setlist COMMA LP idlist RP EQ expr */
+ -3, /* (152) setlist ::= nm EQ expr */
+ -5, /* (153) setlist ::= LP idlist RP EQ expr */
+ -7, /* (154) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
+ -7, /* (155) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */
+ 0, /* (156) upsert ::= */
+ -11, /* (157) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */
+ -8, /* (158) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */
+ -4, /* (159) upsert ::= ON CONFLICT DO NOTHING */
+ -2, /* (160) insert_cmd ::= INSERT orconf */
+ -1, /* (161) insert_cmd ::= REPLACE */
+ 0, /* (162) idlist_opt ::= */
+ -3, /* (163) idlist_opt ::= LP idlist RP */
+ -3, /* (164) idlist ::= idlist COMMA nm */
+ -1, /* (165) idlist ::= nm */
+ -3, /* (166) expr ::= LP expr RP */
+ -1, /* (167) expr ::= ID|INDEXED */
+ -1, /* (168) expr ::= JOIN_KW */
+ -3, /* (169) expr ::= nm DOT nm */
+ -5, /* (170) expr ::= nm DOT nm DOT nm */
+ -1, /* (171) term ::= NULL|FLOAT|BLOB */
+ -1, /* (172) term ::= STRING */
+ -1, /* (173) term ::= INTEGER */
+ -1, /* (174) expr ::= VARIABLE */
+ -3, /* (175) expr ::= expr COLLATE ID|STRING */
+ -6, /* (176) expr ::= CAST LP expr AS typetoken RP */
+ -5, /* (177) expr ::= ID|INDEXED LP distinct exprlist RP */
+ -4, /* (178) expr ::= ID|INDEXED LP STAR RP */
+ -6, /* (179) expr ::= ID|INDEXED LP distinct exprlist RP filter_over */
+ -5, /* (180) expr ::= ID|INDEXED LP STAR RP filter_over */
+ -1, /* (181) term ::= CTIME_KW */
+ -5, /* (182) expr ::= LP nexprlist COMMA expr RP */
+ -3, /* (183) expr ::= expr AND expr */
+ -3, /* (184) expr ::= expr OR expr */
+ -3, /* (185) expr ::= expr LT|GT|GE|LE expr */
+ -3, /* (186) expr ::= expr EQ|NE expr */
+ -3, /* (187) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
+ -3, /* (188) expr ::= expr PLUS|MINUS expr */
+ -3, /* (189) expr ::= expr STAR|SLASH|REM expr */
+ -3, /* (190) expr ::= expr CONCAT expr */
+ -2, /* (191) likeop ::= NOT LIKE_KW|MATCH */
+ -3, /* (192) expr ::= expr likeop expr */
+ -5, /* (193) expr ::= expr likeop expr ESCAPE expr */
+ -2, /* (194) expr ::= expr ISNULL|NOTNULL */
+ -3, /* (195) expr ::= expr NOT NULL */
+ -3, /* (196) expr ::= expr IS expr */
+ -4, /* (197) expr ::= expr IS NOT expr */
+ -2, /* (198) expr ::= NOT expr */
+ -2, /* (199) expr ::= BITNOT expr */
+ -2, /* (200) expr ::= PLUS|MINUS expr */
+ -1, /* (201) between_op ::= BETWEEN */
+ -2, /* (202) between_op ::= NOT BETWEEN */
+ -5, /* (203) expr ::= expr between_op expr AND expr */
+ -1, /* (204) in_op ::= IN */
+ -2, /* (205) in_op ::= NOT IN */
+ -5, /* (206) expr ::= expr in_op LP exprlist RP */
+ -3, /* (207) expr ::= LP select RP */
+ -5, /* (208) expr ::= expr in_op LP select RP */
+ -5, /* (209) expr ::= expr in_op nm dbnm paren_exprlist */
+ -4, /* (210) expr ::= EXISTS LP select RP */
+ -5, /* (211) expr ::= CASE case_operand case_exprlist case_else END */
+ -5, /* (212) case_exprlist ::= case_exprlist WHEN expr THEN expr */
+ -4, /* (213) case_exprlist ::= WHEN expr THEN expr */
+ -2, /* (214) case_else ::= ELSE expr */
+ 0, /* (215) case_else ::= */
+ -1, /* (216) case_operand ::= expr */
+ 0, /* (217) case_operand ::= */
+ 0, /* (218) exprlist ::= */
+ -3, /* (219) nexprlist ::= nexprlist COMMA expr */
+ -1, /* (220) nexprlist ::= expr */
+ 0, /* (221) paren_exprlist ::= */
+ -3, /* (222) paren_exprlist ::= LP exprlist RP */
+ -12, /* (223) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
+ -1, /* (224) uniqueflag ::= UNIQUE */
+ 0, /* (225) uniqueflag ::= */
+ 0, /* (226) eidlist_opt ::= */
+ -3, /* (227) eidlist_opt ::= LP eidlist RP */
+ -5, /* (228) eidlist ::= eidlist COMMA nm collate sortorder */
+ -3, /* (229) eidlist ::= nm collate sortorder */
+ 0, /* (230) collate ::= */
+ -2, /* (231) collate ::= COLLATE ID|STRING */
+ -4, /* (232) cmd ::= DROP INDEX ifexists fullname */
+ -2, /* (233) cmd ::= VACUUM vinto */
+ -3, /* (234) cmd ::= VACUUM nm vinto */
+ -2, /* (235) vinto ::= INTO expr */
+ 0, /* (236) vinto ::= */
+ -3, /* (237) cmd ::= PRAGMA nm dbnm */
+ -5, /* (238) cmd ::= PRAGMA nm dbnm EQ nmnum */
+ -6, /* (239) cmd ::= PRAGMA nm dbnm LP nmnum RP */
+ -5, /* (240) cmd ::= PRAGMA nm dbnm EQ minus_num */
+ -6, /* (241) cmd ::= PRAGMA nm dbnm LP minus_num RP */
+ -2, /* (242) plus_num ::= PLUS INTEGER|FLOAT */
+ -2, /* (243) minus_num ::= MINUS INTEGER|FLOAT */
+ -5, /* (244) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
+ -11, /* (245) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
+ -1, /* (246) trigger_time ::= BEFORE|AFTER */
+ -2, /* (247) trigger_time ::= INSTEAD OF */
+ 0, /* (248) trigger_time ::= */
+ -1, /* (249) trigger_event ::= DELETE|INSERT */
+ -1, /* (250) trigger_event ::= UPDATE */
+ -3, /* (251) trigger_event ::= UPDATE OF idlist */
+ 0, /* (252) when_clause ::= */
+ -2, /* (253) when_clause ::= WHEN expr */
+ -3, /* (254) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
+ -2, /* (255) trigger_cmd_list ::= trigger_cmd SEMI */
+ -3, /* (256) trnm ::= nm DOT nm */
+ -3, /* (257) tridxby ::= INDEXED BY nm */
+ -2, /* (258) tridxby ::= NOT INDEXED */
+ -8, /* (259) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */
+ -8, /* (260) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
+ -6, /* (261) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
+ -3, /* (262) trigger_cmd ::= scanpt select scanpt */
+ -4, /* (263) expr ::= RAISE LP IGNORE RP */
+ -6, /* (264) expr ::= RAISE LP raisetype COMMA nm RP */
+ -1, /* (265) raisetype ::= ROLLBACK */
+ -1, /* (266) raisetype ::= ABORT */
+ -1, /* (267) raisetype ::= FAIL */
+ -4, /* (268) cmd ::= DROP TRIGGER ifexists fullname */
+ -6, /* (269) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+ -3, /* (270) cmd ::= DETACH database_kw_opt expr */
+ 0, /* (271) key_opt ::= */
+ -2, /* (272) key_opt ::= KEY expr */
+ -1, /* (273) cmd ::= REINDEX */
+ -3, /* (274) cmd ::= REINDEX nm dbnm */
+ -1, /* (275) cmd ::= ANALYZE */
+ -3, /* (276) cmd ::= ANALYZE nm dbnm */
+ -6, /* (277) cmd ::= ALTER TABLE fullname RENAME TO nm */
+ -7, /* (278) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
+ -1, /* (279) add_column_fullname ::= fullname */
+ -8, /* (280) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
+ -1, /* (281) cmd ::= create_vtab */
+ -4, /* (282) cmd ::= create_vtab LP vtabarglist RP */
+ -8, /* (283) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
+ 0, /* (284) vtabarg ::= */
+ -1, /* (285) vtabargtoken ::= ANY */
+ -3, /* (286) vtabargtoken ::= lp anylist RP */
+ -1, /* (287) lp ::= LP */
+ -2, /* (288) with ::= WITH wqlist */
+ -3, /* (289) with ::= WITH RECURSIVE wqlist */
+ -6, /* (290) wqlist ::= nm eidlist_opt AS LP select RP */
+ -8, /* (291) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
+ -1, /* (292) windowdefn_list ::= windowdefn */
+ -3, /* (293) windowdefn_list ::= windowdefn_list COMMA windowdefn */
+ -5, /* (294) windowdefn ::= nm AS LP window RP */
+ -5, /* (295) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
+ -6, /* (296) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
+ -4, /* (297) window ::= ORDER BY sortlist frame_opt */
+ -5, /* (298) window ::= nm ORDER BY sortlist frame_opt */
+ -1, /* (299) window ::= frame_opt */
+ -2, /* (300) window ::= nm frame_opt */
+ 0, /* (301) frame_opt ::= */
+ -3, /* (302) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
+ -6, /* (303) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
+ -1, /* (304) range_or_rows ::= RANGE|ROWS|GROUPS */
+ -1, /* (305) frame_bound_s ::= frame_bound */
+ -2, /* (306) frame_bound_s ::= UNBOUNDED PRECEDING */
+ -1, /* (307) frame_bound_e ::= frame_bound */
+ -2, /* (308) frame_bound_e ::= UNBOUNDED FOLLOWING */
+ -2, /* (309) frame_bound ::= expr PRECEDING|FOLLOWING */
+ -2, /* (310) frame_bound ::= CURRENT ROW */
+ 0, /* (311) frame_exclude_opt ::= */
+ -2, /* (312) frame_exclude_opt ::= EXCLUDE frame_exclude */
+ -2, /* (313) frame_exclude ::= NO OTHERS */
+ -2, /* (314) frame_exclude ::= CURRENT ROW */
+ -1, /* (315) frame_exclude ::= GROUP|TIES */
+ -2, /* (316) window_clause ::= WINDOW windowdefn_list */
+ -2, /* (317) filter_over ::= filter_clause over_clause */
+ -1, /* (318) filter_over ::= over_clause */
+ -1, /* (319) filter_over ::= filter_clause */
+ -4, /* (320) over_clause ::= OVER LP window RP */
+ -2, /* (321) over_clause ::= OVER nm */
+ -5, /* (322) filter_clause ::= FILTER LP WHERE expr RP */
+ -1, /* (323) input ::= cmdlist */
+ -2, /* (324) cmdlist ::= cmdlist ecmd */
+ -1, /* (325) cmdlist ::= ecmd */
+ -1, /* (326) ecmd ::= SEMI */
+ -2, /* (327) ecmd ::= cmdx SEMI */
+ -2, /* (328) ecmd ::= explain cmdx */
+ 0, /* (329) trans_opt ::= */
+ -1, /* (330) trans_opt ::= TRANSACTION */
+ -2, /* (331) trans_opt ::= TRANSACTION nm */
+ -1, /* (332) savepoint_opt ::= SAVEPOINT */
+ 0, /* (333) savepoint_opt ::= */
+ -2, /* (334) cmd ::= create_table create_table_args */
+ -4, /* (335) columnlist ::= columnlist COMMA columnname carglist */
+ -2, /* (336) columnlist ::= columnname carglist */
+ -1, /* (337) nm ::= ID|INDEXED */
+ -1, /* (338) nm ::= STRING */
+ -1, /* (339) nm ::= JOIN_KW */
+ -1, /* (340) typetoken ::= typename */
+ -1, /* (341) typename ::= ID|STRING */
+ -1, /* (342) signed ::= plus_num */
+ -1, /* (343) signed ::= minus_num */
+ -2, /* (344) carglist ::= carglist ccons */
+ 0, /* (345) carglist ::= */
+ -2, /* (346) ccons ::= NULL onconf */
+ -2, /* (347) conslist_opt ::= COMMA conslist */
+ -3, /* (348) conslist ::= conslist tconscomma tcons */
+ -1, /* (349) conslist ::= tcons */
+ 0, /* (350) tconscomma ::= */
+ -1, /* (351) defer_subclause_opt ::= defer_subclause */
+ -1, /* (352) resolvetype ::= raisetype */
+ -1, /* (353) selectnowith ::= oneselect */
+ -1, /* (354) oneselect ::= values */
+ -2, /* (355) sclp ::= selcollist COMMA */
+ -1, /* (356) as ::= ID|STRING */
+ -1, /* (357) expr ::= term */
+ -1, /* (358) likeop ::= LIKE_KW|MATCH */
+ -1, /* (359) exprlist ::= nexprlist */
+ -1, /* (360) nmnum ::= plus_num */
+ -1, /* (361) nmnum ::= nm */
+ -1, /* (362) nmnum ::= ON */
+ -1, /* (363) nmnum ::= DELETE */
+ -1, /* (364) nmnum ::= DEFAULT */
+ -1, /* (365) plus_num ::= INTEGER|FLOAT */
+ 0, /* (366) foreach_clause ::= */
+ -3, /* (367) foreach_clause ::= FOR EACH ROW */
+ -1, /* (368) trnm ::= nm */
+ 0, /* (369) tridxby ::= */
+ -1, /* (370) database_kw_opt ::= DATABASE */
+ 0, /* (371) database_kw_opt ::= */
+ 0, /* (372) kwcolumn_opt ::= */
+ -1, /* (373) kwcolumn_opt ::= COLUMNKW */
+ -1, /* (374) vtabarglist ::= vtabarg */
+ -3, /* (375) vtabarglist ::= vtabarglist COMMA vtabarg */
+ -2, /* (376) vtabarg ::= vtabarg vtabargtoken */
+ 0, /* (377) anylist ::= */
+ -4, /* (378) anylist ::= anylist LP anylist RP */
+ -2, /* (379) anylist ::= anylist ANY */
+ 0, /* (380) with ::= */
};
static void yy_accept(yyParser*); /* Forward Declaration */
@@ -151902,16 +152941,16 @@ static YYACTIONTYPE yy_reduce(
{ sqlite3FinishCoding(pParse); }
break;
case 3: /* cmd ::= BEGIN transtype trans_opt */
-{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy100);}
+{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy32);}
break;
case 4: /* transtype ::= */
-{yymsp[1].minor.yy100 = TK_DEFERRED;}
+{yymsp[1].minor.yy32 = TK_DEFERRED;}
break;
case 5: /* transtype ::= DEFERRED */
case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6);
case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7);
- case 301: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==301);
-{yymsp[0].minor.yy100 = yymsp[0].major; /*A-overwrites-X*/}
+ case 304: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==304);
+{yymsp[0].minor.yy32 = yymsp[0].major; /*A-overwrites-X*/}
break;
case 8: /* cmd ::= COMMIT|END trans_opt */
case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9);
@@ -151934,7 +152973,7 @@ static YYACTIONTYPE yy_reduce(
break;
case 13: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */
{
- sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy100,0,0,yymsp[-2].minor.yy100);
+ sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy32,0,0,yymsp[-2].minor.yy32);
}
break;
case 14: /* createkw ::= CREATE */
@@ -151948,33 +152987,33 @@ static YYACTIONTYPE yy_reduce(
case 68: /* defer_subclause_opt ::= */ yytestcase(yyruleno==68);
case 77: /* ifexists ::= */ yytestcase(yyruleno==77);
case 94: /* distinct ::= */ yytestcase(yyruleno==94);
- case 227: /* collate ::= */ yytestcase(yyruleno==227);
-{yymsp[1].minor.yy100 = 0;}
+ case 230: /* collate ::= */ yytestcase(yyruleno==230);
+{yymsp[1].minor.yy32 = 0;}
break;
case 16: /* ifnotexists ::= IF NOT EXISTS */
-{yymsp[-2].minor.yy100 = 1;}
+{yymsp[-2].minor.yy32 = 1;}
break;
case 17: /* temp ::= TEMP */
case 44: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==44);
-{yymsp[0].minor.yy100 = 1;}
+{yymsp[0].minor.yy32 = 1;}
break;
case 19: /* create_table_args ::= LP columnlist conslist_opt RP table_options */
{
- sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy100,0);
+ sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy32,0);
}
break;
case 20: /* create_table_args ::= AS select */
{
- sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy391);
- sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy391);
+ sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy25);
+ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy25);
}
break;
case 22: /* table_options ::= WITHOUT nm */
{
if( yymsp[0].minor.yy0.n==5 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"rowid",5)==0 ){
- yymsp[-1].minor.yy100 = TF_WithoutRowid | TF_NoVisibleRowid;
+ yymsp[-1].minor.yy32 = TF_WithoutRowid | TF_NoVisibleRowid;
}else{
- yymsp[-1].minor.yy100 = 0;
+ yymsp[-1].minor.yy32 = 0;
sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z);
}
}
@@ -152003,7 +153042,7 @@ static YYACTIONTYPE yy_reduce(
case 28: /* scanpt ::= */
{
assert( yyLookahead!=YYNOCODE );
- yymsp[1].minor.yy528 = yyLookaheadToken.z;
+ yymsp[1].minor.yy8 = yyLookaheadToken.z;
}
break;
case 29: /* scantok ::= */
@@ -152017,17 +153056,17 @@ static YYACTIONTYPE yy_reduce(
{pParse->constraintName = yymsp[0].minor.yy0;}
break;
case 31: /* ccons ::= DEFAULT scantok term */
-{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy102,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
+{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy46,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
break;
case 32: /* ccons ::= DEFAULT LP expr RP */
-{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy102,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);}
+{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy46,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);}
break;
case 33: /* ccons ::= DEFAULT PLUS scantok term */
-{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy102,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
+{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy46,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
break;
case 34: /* ccons ::= DEFAULT MINUS scantok term */
{
- Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy102, 0);
+ Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy46, 0);
sqlite3AddDefaultValue(pParse,p,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);
}
break;
@@ -152042,170 +153081,170 @@ static YYACTIONTYPE yy_reduce(
}
break;
case 36: /* ccons ::= NOT NULL onconf */
-{sqlite3AddNotNull(pParse, yymsp[0].minor.yy100);}
+{sqlite3AddNotNull(pParse, yymsp[0].minor.yy32);}
break;
case 37: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */
-{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy100,yymsp[0].minor.yy100,yymsp[-2].minor.yy100);}
+{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy32,yymsp[0].minor.yy32,yymsp[-2].minor.yy32);}
break;
case 38: /* ccons ::= UNIQUE onconf */
-{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy100,0,0,0,0,
+{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy32,0,0,0,0,
SQLITE_IDXTYPE_UNIQUE);}
break;
case 39: /* ccons ::= CHECK LP expr RP */
-{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy102);}
+{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy46);}
break;
case 40: /* ccons ::= REFERENCES nm eidlist_opt refargs */
-{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy94,yymsp[0].minor.yy100);}
+{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy138,yymsp[0].minor.yy32);}
break;
case 41: /* ccons ::= defer_subclause */
-{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy100);}
+{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy32);}
break;
case 42: /* ccons ::= COLLATE ID|STRING */
{sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);}
break;
case 45: /* refargs ::= */
-{ yymsp[1].minor.yy100 = OE_None*0x0101; /* EV: R-19803-45884 */}
+{ yymsp[1].minor.yy32 = OE_None*0x0101; /* EV: R-19803-45884 */}
break;
case 46: /* refargs ::= refargs refarg */
-{ yymsp[-1].minor.yy100 = (yymsp[-1].minor.yy100 & ~yymsp[0].minor.yy199.mask) | yymsp[0].minor.yy199.value; }
+{ yymsp[-1].minor.yy32 = (yymsp[-1].minor.yy32 & ~yymsp[0].minor.yy495.mask) | yymsp[0].minor.yy495.value; }
break;
case 47: /* refarg ::= MATCH nm */
-{ yymsp[-1].minor.yy199.value = 0; yymsp[-1].minor.yy199.mask = 0x000000; }
+{ yymsp[-1].minor.yy495.value = 0; yymsp[-1].minor.yy495.mask = 0x000000; }
break;
case 48: /* refarg ::= ON INSERT refact */
-{ yymsp[-2].minor.yy199.value = 0; yymsp[-2].minor.yy199.mask = 0x000000; }
+{ yymsp[-2].minor.yy495.value = 0; yymsp[-2].minor.yy495.mask = 0x000000; }
break;
case 49: /* refarg ::= ON DELETE refact */
-{ yymsp[-2].minor.yy199.value = yymsp[0].minor.yy100; yymsp[-2].minor.yy199.mask = 0x0000ff; }
+{ yymsp[-2].minor.yy495.value = yymsp[0].minor.yy32; yymsp[-2].minor.yy495.mask = 0x0000ff; }
break;
case 50: /* refarg ::= ON UPDATE refact */
-{ yymsp[-2].minor.yy199.value = yymsp[0].minor.yy100<<8; yymsp[-2].minor.yy199.mask = 0x00ff00; }
+{ yymsp[-2].minor.yy495.value = yymsp[0].minor.yy32<<8; yymsp[-2].minor.yy495.mask = 0x00ff00; }
break;
case 51: /* refact ::= SET NULL */
-{ yymsp[-1].minor.yy100 = OE_SetNull; /* EV: R-33326-45252 */}
+{ yymsp[-1].minor.yy32 = OE_SetNull; /* EV: R-33326-45252 */}
break;
case 52: /* refact ::= SET DEFAULT */
-{ yymsp[-1].minor.yy100 = OE_SetDflt; /* EV: R-33326-45252 */}
+{ yymsp[-1].minor.yy32 = OE_SetDflt; /* EV: R-33326-45252 */}
break;
case 53: /* refact ::= CASCADE */
-{ yymsp[0].minor.yy100 = OE_Cascade; /* EV: R-33326-45252 */}
+{ yymsp[0].minor.yy32 = OE_Cascade; /* EV: R-33326-45252 */}
break;
case 54: /* refact ::= RESTRICT */
-{ yymsp[0].minor.yy100 = OE_Restrict; /* EV: R-33326-45252 */}
+{ yymsp[0].minor.yy32 = OE_Restrict; /* EV: R-33326-45252 */}
break;
case 55: /* refact ::= NO ACTION */
-{ yymsp[-1].minor.yy100 = OE_None; /* EV: R-33326-45252 */}
+{ yymsp[-1].minor.yy32 = OE_None; /* EV: R-33326-45252 */}
break;
case 56: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
-{yymsp[-2].minor.yy100 = 0;}
+{yymsp[-2].minor.yy32 = 0;}
break;
case 57: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
case 72: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==72);
- case 157: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==157);
-{yymsp[-1].minor.yy100 = yymsp[0].minor.yy100;}
+ case 160: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==160);
+{yymsp[-1].minor.yy32 = yymsp[0].minor.yy32;}
break;
case 59: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */
case 76: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==76);
- case 199: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==199);
- case 202: /* in_op ::= NOT IN */ yytestcase(yyruleno==202);
- case 228: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==228);
-{yymsp[-1].minor.yy100 = 1;}
+ case 202: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==202);
+ case 205: /* in_op ::= NOT IN */ yytestcase(yyruleno==205);
+ case 231: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==231);
+{yymsp[-1].minor.yy32 = 1;}
break;
case 60: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
-{yymsp[-1].minor.yy100 = 0;}
+{yymsp[-1].minor.yy32 = 0;}
break;
case 62: /* tconscomma ::= COMMA */
{pParse->constraintName.n = 0;}
break;
case 64: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
-{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy94,yymsp[0].minor.yy100,yymsp[-2].minor.yy100,0);}
+{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy138,yymsp[0].minor.yy32,yymsp[-2].minor.yy32,0);}
break;
case 65: /* tcons ::= UNIQUE LP sortlist RP onconf */
-{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy94,yymsp[0].minor.yy100,0,0,0,0,
+{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy138,yymsp[0].minor.yy32,0,0,0,0,
SQLITE_IDXTYPE_UNIQUE);}
break;
case 66: /* tcons ::= CHECK LP expr RP onconf */
-{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy102);}
+{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy46);}
break;
case 67: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
{
- sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy94, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy94, yymsp[-1].minor.yy100);
- sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy100);
+ sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy138, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy138, yymsp[-1].minor.yy32);
+ sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy32);
}
break;
case 69: /* onconf ::= */
case 71: /* orconf ::= */ yytestcase(yyruleno==71);
-{yymsp[1].minor.yy100 = OE_Default;}
+{yymsp[1].minor.yy32 = OE_Default;}
break;
case 70: /* onconf ::= ON CONFLICT resolvetype */
-{yymsp[-2].minor.yy100 = yymsp[0].minor.yy100;}
+{yymsp[-2].minor.yy32 = yymsp[0].minor.yy32;}
break;
case 73: /* resolvetype ::= IGNORE */
-{yymsp[0].minor.yy100 = OE_Ignore;}
+{yymsp[0].minor.yy32 = OE_Ignore;}
break;
case 74: /* resolvetype ::= REPLACE */
- case 158: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==158);
-{yymsp[0].minor.yy100 = OE_Replace;}
+ case 161: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==161);
+{yymsp[0].minor.yy32 = OE_Replace;}
break;
case 75: /* cmd ::= DROP TABLE ifexists fullname */
{
- sqlite3DropTable(pParse, yymsp[0].minor.yy407, 0, yymsp[-1].minor.yy100);
+ sqlite3DropTable(pParse, yymsp[0].minor.yy609, 0, yymsp[-1].minor.yy32);
}
break;
case 78: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
{
- sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy94, yymsp[0].minor.yy391, yymsp[-7].minor.yy100, yymsp[-5].minor.yy100);
+ sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy138, yymsp[0].minor.yy25, yymsp[-7].minor.yy32, yymsp[-5].minor.yy32);
}
break;
case 79: /* cmd ::= DROP VIEW ifexists fullname */
{
- sqlite3DropTable(pParse, yymsp[0].minor.yy407, 1, yymsp[-1].minor.yy100);
+ sqlite3DropTable(pParse, yymsp[0].minor.yy609, 1, yymsp[-1].minor.yy32);
}
break;
case 80: /* cmd ::= select */
{
SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0};
- sqlite3Select(pParse, yymsp[0].minor.yy391, &dest);
- sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy391);
+ sqlite3Select(pParse, yymsp[0].minor.yy25, &dest);
+ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy25);
}
break;
case 81: /* select ::= WITH wqlist selectnowith */
{
- Select *p = yymsp[0].minor.yy391;
+ Select *p = yymsp[0].minor.yy25;
if( p ){
- p->pWith = yymsp[-1].minor.yy243;
+ p->pWith = yymsp[-1].minor.yy297;
parserDoubleLinkSelect(pParse, p);
}else{
- sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy243);
+ sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy297);
}
- yymsp[-2].minor.yy391 = p;
+ yymsp[-2].minor.yy25 = p;
}
break;
case 82: /* select ::= WITH RECURSIVE wqlist selectnowith */
{
- Select *p = yymsp[0].minor.yy391;
+ Select *p = yymsp[0].minor.yy25;
if( p ){
- p->pWith = yymsp[-1].minor.yy243;
+ p->pWith = yymsp[-1].minor.yy297;
parserDoubleLinkSelect(pParse, p);
}else{
- sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy243);
+ sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy297);
}
- yymsp[-3].minor.yy391 = p;
+ yymsp[-3].minor.yy25 = p;
}
break;
case 83: /* select ::= selectnowith */
{
- Select *p = yymsp[0].minor.yy391;
+ Select *p = yymsp[0].minor.yy25;
if( p ){
parserDoubleLinkSelect(pParse, p);
}
- yymsp[0].minor.yy391 = p; /*A-overwrites-X*/
+ yymsp[0].minor.yy25 = p; /*A-overwrites-X*/
}
break;
case 84: /* selectnowith ::= selectnowith multiselect_op oneselect */
{
- Select *pRhs = yymsp[0].minor.yy391;
- Select *pLhs = yymsp[-2].minor.yy391;
+ Select *pRhs = yymsp[0].minor.yy25;
+ Select *pLhs = yymsp[-2].minor.yy25;
if( pRhs && pRhs->pPrior ){
SrcList *pFrom;
Token x;
@@ -152215,83 +153254,83 @@ static YYACTIONTYPE yy_reduce(
pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0);
}
if( pRhs ){
- pRhs->op = (u8)yymsp[-1].minor.yy100;
+ pRhs->op = (u8)yymsp[-1].minor.yy32;
pRhs->pPrior = pLhs;
if( ALWAYS(pLhs) ) pLhs->selFlags &= ~SF_MultiValue;
pRhs->selFlags &= ~SF_MultiValue;
- if( yymsp[-1].minor.yy100!=TK_ALL ) pParse->hasCompound = 1;
+ if( yymsp[-1].minor.yy32!=TK_ALL ) pParse->hasCompound = 1;
}else{
sqlite3SelectDelete(pParse->db, pLhs);
}
- yymsp[-2].minor.yy391 = pRhs;
+ yymsp[-2].minor.yy25 = pRhs;
}
break;
case 85: /* multiselect_op ::= UNION */
case 87: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==87);
-{yymsp[0].minor.yy100 = yymsp[0].major; /*A-overwrites-OP*/}
+{yymsp[0].minor.yy32 = yymsp[0].major; /*A-overwrites-OP*/}
break;
case 86: /* multiselect_op ::= UNION ALL */
-{yymsp[-1].minor.yy100 = TK_ALL;}
+{yymsp[-1].minor.yy32 = TK_ALL;}
break;
case 88: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
{
- yymsp[-8].minor.yy391 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy94,yymsp[-5].minor.yy407,yymsp[-4].minor.yy102,yymsp[-3].minor.yy94,yymsp[-2].minor.yy102,yymsp[-1].minor.yy94,yymsp[-7].minor.yy100,yymsp[0].minor.yy102);
+ yymsp[-8].minor.yy25 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy138,yymsp[-5].minor.yy609,yymsp[-4].minor.yy46,yymsp[-3].minor.yy138,yymsp[-2].minor.yy46,yymsp[-1].minor.yy138,yymsp[-7].minor.yy32,yymsp[0].minor.yy46);
}
break;
case 89: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
{
- yymsp[-9].minor.yy391 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy94,yymsp[-6].minor.yy407,yymsp[-5].minor.yy102,yymsp[-4].minor.yy94,yymsp[-3].minor.yy102,yymsp[-1].minor.yy94,yymsp[-8].minor.yy100,yymsp[0].minor.yy102);
- if( yymsp[-9].minor.yy391 ){
- yymsp[-9].minor.yy391->pWinDefn = yymsp[-2].minor.yy379;
+ yymsp[-9].minor.yy25 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy138,yymsp[-6].minor.yy609,yymsp[-5].minor.yy46,yymsp[-4].minor.yy138,yymsp[-3].minor.yy46,yymsp[-1].minor.yy138,yymsp[-8].minor.yy32,yymsp[0].minor.yy46);
+ if( yymsp[-9].minor.yy25 ){
+ yymsp[-9].minor.yy25->pWinDefn = yymsp[-2].minor.yy455;
}else{
- sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy379);
+ sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy455);
}
}
break;
case 90: /* values ::= VALUES LP nexprlist RP */
{
- yymsp[-3].minor.yy391 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy94,0,0,0,0,0,SF_Values,0);
+ yymsp[-3].minor.yy25 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy138,0,0,0,0,0,SF_Values,0);
}
break;
case 91: /* values ::= values COMMA LP nexprlist RP */
{
- Select *pRight, *pLeft = yymsp[-4].minor.yy391;
- pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy94,0,0,0,0,0,SF_Values|SF_MultiValue,0);
+ Select *pRight, *pLeft = yymsp[-4].minor.yy25;
+ pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy138,0,0,0,0,0,SF_Values|SF_MultiValue,0);
if( ALWAYS(pLeft) ) pLeft->selFlags &= ~SF_MultiValue;
if( pRight ){
pRight->op = TK_ALL;
pRight->pPrior = pLeft;
- yymsp[-4].minor.yy391 = pRight;
+ yymsp[-4].minor.yy25 = pRight;
}else{
- yymsp[-4].minor.yy391 = pLeft;
+ yymsp[-4].minor.yy25 = pLeft;
}
}
break;
case 92: /* distinct ::= DISTINCT */
-{yymsp[0].minor.yy100 = SF_Distinct;}
+{yymsp[0].minor.yy32 = SF_Distinct;}
break;
case 93: /* distinct ::= ALL */
-{yymsp[0].minor.yy100 = SF_All;}
+{yymsp[0].minor.yy32 = SF_All;}
break;
case 95: /* sclp ::= */
case 128: /* orderby_opt ::= */ yytestcase(yyruleno==128);
- case 135: /* groupby_opt ::= */ yytestcase(yyruleno==135);
- case 215: /* exprlist ::= */ yytestcase(yyruleno==215);
- case 218: /* paren_exprlist ::= */ yytestcase(yyruleno==218);
- case 223: /* eidlist_opt ::= */ yytestcase(yyruleno==223);
-{yymsp[1].minor.yy94 = 0;}
+ case 138: /* groupby_opt ::= */ yytestcase(yyruleno==138);
+ case 218: /* exprlist ::= */ yytestcase(yyruleno==218);
+ case 221: /* paren_exprlist ::= */ yytestcase(yyruleno==221);
+ case 226: /* eidlist_opt ::= */ yytestcase(yyruleno==226);
+{yymsp[1].minor.yy138 = 0;}
break;
case 96: /* selcollist ::= sclp scanpt expr scanpt as */
{
- yymsp[-4].minor.yy94 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy94, yymsp[-2].minor.yy102);
- if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy94, &yymsp[0].minor.yy0, 1);
- sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy94,yymsp[-3].minor.yy528,yymsp[-1].minor.yy528);
+ yymsp[-4].minor.yy138 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy138, yymsp[-2].minor.yy46);
+ if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy138, &yymsp[0].minor.yy0, 1);
+ sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy138,yymsp[-3].minor.yy8,yymsp[-1].minor.yy8);
}
break;
case 97: /* selcollist ::= sclp scanpt STAR */
{
Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0);
- yymsp[-2].minor.yy94 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy94, p);
+ yymsp[-2].minor.yy138 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy138, p);
}
break;
case 98: /* selcollist ::= sclp scanpt nm DOT STAR */
@@ -152299,58 +153338,58 @@ static YYACTIONTYPE yy_reduce(
Expr *pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0);
Expr *pLeft = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1);
Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight);
- yymsp[-4].minor.yy94 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy94, pDot);
+ yymsp[-4].minor.yy138 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy138, pDot);
}
break;
case 99: /* as ::= AS nm */
case 110: /* dbnm ::= DOT nm */ yytestcase(yyruleno==110);
- case 239: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==239);
- case 240: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==240);
+ case 242: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==242);
+ case 243: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==243);
{yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;}
break;
case 101: /* from ::= */
-{yymsp[1].minor.yy407 = sqlite3DbMallocZero(pParse->db, sizeof(*yymsp[1].minor.yy407));}
+{yymsp[1].minor.yy609 = sqlite3DbMallocZero(pParse->db, sizeof(*yymsp[1].minor.yy609));}
break;
case 102: /* from ::= FROM seltablist */
{
- yymsp[-1].minor.yy407 = yymsp[0].minor.yy407;
- sqlite3SrcListShiftJoinType(yymsp[-1].minor.yy407);
+ yymsp[-1].minor.yy609 = yymsp[0].minor.yy609;
+ sqlite3SrcListShiftJoinType(yymsp[-1].minor.yy609);
}
break;
case 103: /* stl_prefix ::= seltablist joinop */
{
- if( ALWAYS(yymsp[-1].minor.yy407 && yymsp[-1].minor.yy407->nSrc>0) ) yymsp[-1].minor.yy407->a[yymsp[-1].minor.yy407->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy100;
+ if( ALWAYS(yymsp[-1].minor.yy609 && yymsp[-1].minor.yy609->nSrc>0) ) yymsp[-1].minor.yy609->a[yymsp[-1].minor.yy609->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy32;
}
break;
case 104: /* stl_prefix ::= */
-{yymsp[1].minor.yy407 = 0;}
+{yymsp[1].minor.yy609 = 0;}
break;
case 105: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */
{
- yymsp[-6].minor.yy407 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy407,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy102,yymsp[0].minor.yy76);
- sqlite3SrcListIndexedBy(pParse, yymsp[-6].minor.yy407, &yymsp[-2].minor.yy0);
+ yymsp[-6].minor.yy609 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy609,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy46,yymsp[0].minor.yy406);
+ sqlite3SrcListIndexedBy(pParse, yymsp[-6].minor.yy609, &yymsp[-2].minor.yy0);
}
break;
case 106: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */
{
- yymsp[-8].minor.yy407 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-8].minor.yy407,&yymsp[-7].minor.yy0,&yymsp[-6].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy102,yymsp[0].minor.yy76);
- sqlite3SrcListFuncArgs(pParse, yymsp[-8].minor.yy407, yymsp[-4].minor.yy94);
+ yymsp[-8].minor.yy609 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-8].minor.yy609,&yymsp[-7].minor.yy0,&yymsp[-6].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy46,yymsp[0].minor.yy406);
+ sqlite3SrcListFuncArgs(pParse, yymsp[-8].minor.yy609, yymsp[-4].minor.yy138);
}
break;
case 107: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */
{
- yymsp[-6].minor.yy407 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy407,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy391,yymsp[-1].minor.yy102,yymsp[0].minor.yy76);
+ yymsp[-6].minor.yy609 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy609,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy25,yymsp[-1].minor.yy46,yymsp[0].minor.yy406);
}
break;
case 108: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */
{
- if( yymsp[-6].minor.yy407==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy102==0 && yymsp[0].minor.yy76==0 ){
- yymsp[-6].minor.yy407 = yymsp[-4].minor.yy407;
- }else if( yymsp[-4].minor.yy407->nSrc==1 ){
- yymsp[-6].minor.yy407 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy407,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy102,yymsp[0].minor.yy76);
- if( yymsp[-6].minor.yy407 ){
- struct SrcList_item *pNew = &yymsp[-6].minor.yy407->a[yymsp[-6].minor.yy407->nSrc-1];
- struct SrcList_item *pOld = yymsp[-4].minor.yy407->a;
+ if( yymsp[-6].minor.yy609==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy46==0 && yymsp[0].minor.yy406==0 ){
+ yymsp[-6].minor.yy609 = yymsp[-4].minor.yy609;
+ }else if( yymsp[-4].minor.yy609->nSrc==1 ){
+ yymsp[-6].minor.yy609 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy609,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy46,yymsp[0].minor.yy406);
+ if( yymsp[-6].minor.yy609 ){
+ struct SrcList_item *pNew = &yymsp[-6].minor.yy609->a[yymsp[-6].minor.yy609->nSrc-1];
+ struct SrcList_item *pOld = yymsp[-4].minor.yy609->a;
pNew->zName = pOld->zName;
pNew->zDatabase = pOld->zDatabase;
pNew->pSelect = pOld->pSelect;
@@ -152363,12 +153402,12 @@ static YYACTIONTYPE yy_reduce(
pOld->zName = pOld->zDatabase = 0;
pOld->pSelect = 0;
}
- sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy407);
+ sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy609);
}else{
Select *pSubquery;
- sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy407);
- pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy407,0,0,0,0,SF_NestedFrom,0);
- yymsp[-6].minor.yy407 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy407,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy102,yymsp[0].minor.yy76);
+ sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy609);
+ pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy609,0,0,0,0,SF_NestedFrom,0);
+ yymsp[-6].minor.yy609 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy609,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy46,yymsp[0].minor.yy406);
}
}
break;
@@ -152378,63 +153417,63 @@ static YYACTIONTYPE yy_reduce(
break;
case 111: /* fullname ::= nm */
{
- yylhsminor.yy407 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0);
- if( IN_RENAME_OBJECT && yylhsminor.yy407 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy407->a[0].zName, &yymsp[0].minor.yy0);
+ yylhsminor.yy609 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0);
+ if( IN_RENAME_OBJECT && yylhsminor.yy609 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy609->a[0].zName, &yymsp[0].minor.yy0);
}
- yymsp[0].minor.yy407 = yylhsminor.yy407;
+ yymsp[0].minor.yy609 = yylhsminor.yy609;
break;
case 112: /* fullname ::= nm DOT nm */
{
- yylhsminor.yy407 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);
- if( IN_RENAME_OBJECT && yylhsminor.yy407 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy407->a[0].zName, &yymsp[0].minor.yy0);
+ yylhsminor.yy609 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);
+ if( IN_RENAME_OBJECT && yylhsminor.yy609 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy609->a[0].zName, &yymsp[0].minor.yy0);
}
- yymsp[-2].minor.yy407 = yylhsminor.yy407;
+ yymsp[-2].minor.yy609 = yylhsminor.yy609;
break;
case 113: /* xfullname ::= nm */
-{yymsp[0].minor.yy407 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/}
+{yymsp[0].minor.yy609 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/}
break;
case 114: /* xfullname ::= nm DOT nm */
-{yymsp[-2].minor.yy407 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/}
+{yymsp[-2].minor.yy609 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
case 115: /* xfullname ::= nm DOT nm AS nm */
{
- yymsp[-4].minor.yy407 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/
- if( yymsp[-4].minor.yy407 ) yymsp[-4].minor.yy407->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
+ yymsp[-4].minor.yy609 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/
+ if( yymsp[-4].minor.yy609 ) yymsp[-4].minor.yy609->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
}
break;
case 116: /* xfullname ::= nm AS nm */
{
- yymsp[-2].minor.yy407 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/
- if( yymsp[-2].minor.yy407 ) yymsp[-2].minor.yy407->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
+ yymsp[-2].minor.yy609 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/
+ if( yymsp[-2].minor.yy609 ) yymsp[-2].minor.yy609->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
}
break;
case 117: /* joinop ::= COMMA|JOIN */
-{ yymsp[0].minor.yy100 = JT_INNER; }
+{ yymsp[0].minor.yy32 = JT_INNER; }
break;
case 118: /* joinop ::= JOIN_KW JOIN */
-{yymsp[-1].minor.yy100 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/}
+{yymsp[-1].minor.yy32 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/}
break;
case 119: /* joinop ::= JOIN_KW nm JOIN */
-{yymsp[-2].minor.yy100 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/}
+{yymsp[-2].minor.yy32 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/}
break;
case 120: /* joinop ::= JOIN_KW nm nm JOIN */
-{yymsp[-3].minor.yy100 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/}
+{yymsp[-3].minor.yy32 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/}
break;
case 121: /* on_opt ::= ON expr */
- case 138: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==138);
- case 145: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==145);
- case 211: /* case_else ::= ELSE expr */ yytestcase(yyruleno==211);
- case 232: /* vinto ::= INTO expr */ yytestcase(yyruleno==232);
-{yymsp[-1].minor.yy102 = yymsp[0].minor.yy102;}
+ case 141: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==141);
+ case 148: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==148);
+ case 214: /* case_else ::= ELSE expr */ yytestcase(yyruleno==214);
+ case 235: /* vinto ::= INTO expr */ yytestcase(yyruleno==235);
+{yymsp[-1].minor.yy46 = yymsp[0].minor.yy46;}
break;
case 122: /* on_opt ::= */
- case 137: /* having_opt ::= */ yytestcase(yyruleno==137);
- case 139: /* limit_opt ::= */ yytestcase(yyruleno==139);
- case 144: /* where_opt ::= */ yytestcase(yyruleno==144);
- case 212: /* case_else ::= */ yytestcase(yyruleno==212);
- case 214: /* case_operand ::= */ yytestcase(yyruleno==214);
- case 233: /* vinto ::= */ yytestcase(yyruleno==233);
-{yymsp[1].minor.yy102 = 0;}
+ case 140: /* having_opt ::= */ yytestcase(yyruleno==140);
+ case 142: /* limit_opt ::= */ yytestcase(yyruleno==142);
+ case 147: /* where_opt ::= */ yytestcase(yyruleno==147);
+ case 215: /* case_else ::= */ yytestcase(yyruleno==215);
+ case 217: /* case_operand ::= */ yytestcase(yyruleno==217);
+ case 236: /* vinto ::= */ yytestcase(yyruleno==236);
+{yymsp[1].minor.yy46 = 0;}
break;
case 124: /* indexed_opt ::= INDEXED BY nm */
{yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;}
@@ -152443,121 +153482,128 @@ static YYACTIONTYPE yy_reduce(
{yymsp[-1].minor.yy0.z=0; yymsp[-1].minor.yy0.n=1;}
break;
case 126: /* using_opt ::= USING LP idlist RP */
-{yymsp[-3].minor.yy76 = yymsp[-1].minor.yy76;}
+{yymsp[-3].minor.yy406 = yymsp[-1].minor.yy406;}
break;
case 127: /* using_opt ::= */
- case 159: /* idlist_opt ::= */ yytestcase(yyruleno==159);
-{yymsp[1].minor.yy76 = 0;}
+ case 162: /* idlist_opt ::= */ yytestcase(yyruleno==162);
+{yymsp[1].minor.yy406 = 0;}
break;
case 129: /* orderby_opt ::= ORDER BY sortlist */
- case 136: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==136);
-{yymsp[-2].minor.yy94 = yymsp[0].minor.yy94;}
+ case 139: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==139);
+{yymsp[-2].minor.yy138 = yymsp[0].minor.yy138;}
break;
- case 130: /* sortlist ::= sortlist COMMA expr sortorder */
+ case 130: /* sortlist ::= sortlist COMMA expr sortorder nulls */
{
- yymsp[-3].minor.yy94 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy94,yymsp[-1].minor.yy102);
- sqlite3ExprListSetSortOrder(yymsp[-3].minor.yy94,yymsp[0].minor.yy100);
+ yymsp[-4].minor.yy138 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy138,yymsp[-2].minor.yy46);
+ sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy138,yymsp[-1].minor.yy32,yymsp[0].minor.yy32);
}
break;
- case 131: /* sortlist ::= expr sortorder */
+ case 131: /* sortlist ::= expr sortorder nulls */
{
- yymsp[-1].minor.yy94 = sqlite3ExprListAppend(pParse,0,yymsp[-1].minor.yy102); /*A-overwrites-Y*/
- sqlite3ExprListSetSortOrder(yymsp[-1].minor.yy94,yymsp[0].minor.yy100);
+ yymsp[-2].minor.yy138 = sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy46); /*A-overwrites-Y*/
+ sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy138,yymsp[-1].minor.yy32,yymsp[0].minor.yy32);
}
break;
case 132: /* sortorder ::= ASC */
-{yymsp[0].minor.yy100 = SQLITE_SO_ASC;}
+{yymsp[0].minor.yy32 = SQLITE_SO_ASC;}
break;
case 133: /* sortorder ::= DESC */
-{yymsp[0].minor.yy100 = SQLITE_SO_DESC;}
+{yymsp[0].minor.yy32 = SQLITE_SO_DESC;}
break;
case 134: /* sortorder ::= */
-{yymsp[1].minor.yy100 = SQLITE_SO_UNDEFINED;}
+ case 137: /* nulls ::= */ yytestcase(yyruleno==137);
+{yymsp[1].minor.yy32 = SQLITE_SO_UNDEFINED;}
+ break;
+ case 135: /* nulls ::= NULLS FIRST */
+{yymsp[-1].minor.yy32 = SQLITE_SO_ASC;}
break;
- case 140: /* limit_opt ::= LIMIT expr */
-{yymsp[-1].minor.yy102 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy102,0);}
+ case 136: /* nulls ::= NULLS LAST */
+{yymsp[-1].minor.yy32 = SQLITE_SO_DESC;}
break;
- case 141: /* limit_opt ::= LIMIT expr OFFSET expr */
-{yymsp[-3].minor.yy102 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy102,yymsp[0].minor.yy102);}
+ case 143: /* limit_opt ::= LIMIT expr */
+{yymsp[-1].minor.yy46 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy46,0);}
break;
- case 142: /* limit_opt ::= LIMIT expr COMMA expr */
-{yymsp[-3].minor.yy102 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy102,yymsp[-2].minor.yy102);}
+ case 144: /* limit_opt ::= LIMIT expr OFFSET expr */
+{yymsp[-3].minor.yy46 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy46,yymsp[0].minor.yy46);}
break;
- case 143: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt */
+ case 145: /* limit_opt ::= LIMIT expr COMMA expr */
+{yymsp[-3].minor.yy46 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy46,yymsp[-2].minor.yy46);}
+ break;
+ case 146: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt */
{
- sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy407, &yymsp[-1].minor.yy0);
- sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy407,yymsp[0].minor.yy102,0,0);
+ sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy609, &yymsp[-1].minor.yy0);
+ sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy609,yymsp[0].minor.yy46,0,0);
}
break;
- case 146: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */
+ case 149: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist where_opt */
{
- sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy407, &yymsp[-3].minor.yy0);
- sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy94,"set list");
- sqlite3Update(pParse,yymsp[-4].minor.yy407,yymsp[-1].minor.yy94,yymsp[0].minor.yy102,yymsp[-5].minor.yy100,0,0,0);
+ sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy609, &yymsp[-3].minor.yy0);
+ sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy138,"set list");
+ sqlite3Update(pParse,yymsp[-4].minor.yy609,yymsp[-1].minor.yy138,yymsp[0].minor.yy46,yymsp[-5].minor.yy32,0,0,0);
}
break;
- case 147: /* setlist ::= setlist COMMA nm EQ expr */
+ case 150: /* setlist ::= setlist COMMA nm EQ expr */
{
- yymsp[-4].minor.yy94 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy94, yymsp[0].minor.yy102);
- sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy94, &yymsp[-2].minor.yy0, 1);
+ yymsp[-4].minor.yy138 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy138, yymsp[0].minor.yy46);
+ sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy138, &yymsp[-2].minor.yy0, 1);
}
break;
- case 148: /* setlist ::= setlist COMMA LP idlist RP EQ expr */
+ case 151: /* setlist ::= setlist COMMA LP idlist RP EQ expr */
{
- yymsp[-6].minor.yy94 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy94, yymsp[-3].minor.yy76, yymsp[0].minor.yy102);
+ yymsp[-6].minor.yy138 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy138, yymsp[-3].minor.yy406, yymsp[0].minor.yy46);
}
break;
- case 149: /* setlist ::= nm EQ expr */
+ case 152: /* setlist ::= nm EQ expr */
{
- yylhsminor.yy94 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy102);
- sqlite3ExprListSetName(pParse, yylhsminor.yy94, &yymsp[-2].minor.yy0, 1);
+ yylhsminor.yy138 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy46);
+ sqlite3ExprListSetName(pParse, yylhsminor.yy138, &yymsp[-2].minor.yy0, 1);
}
- yymsp[-2].minor.yy94 = yylhsminor.yy94;
+ yymsp[-2].minor.yy138 = yylhsminor.yy138;
break;
- case 150: /* setlist ::= LP idlist RP EQ expr */
+ case 153: /* setlist ::= LP idlist RP EQ expr */
{
- yymsp[-4].minor.yy94 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy76, yymsp[0].minor.yy102);
+ yymsp[-4].minor.yy138 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy406, yymsp[0].minor.yy46);
}
break;
- case 151: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
+ case 154: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
{
- sqlite3Insert(pParse, yymsp[-3].minor.yy407, yymsp[-1].minor.yy391, yymsp[-2].minor.yy76, yymsp[-5].minor.yy100, yymsp[0].minor.yy95);
+ sqlite3Insert(pParse, yymsp[-3].minor.yy609, yymsp[-1].minor.yy25, yymsp[-2].minor.yy406, yymsp[-5].minor.yy32, yymsp[0].minor.yy288);
}
break;
- case 152: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */
+ case 155: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */
{
- sqlite3Insert(pParse, yymsp[-3].minor.yy407, 0, yymsp[-2].minor.yy76, yymsp[-5].minor.yy100, 0);
+ sqlite3Insert(pParse, yymsp[-3].minor.yy609, 0, yymsp[-2].minor.yy406, yymsp[-5].minor.yy32, 0);
}
break;
- case 153: /* upsert ::= */
-{ yymsp[1].minor.yy95 = 0; }
+ case 156: /* upsert ::= */
+{ yymsp[1].minor.yy288 = 0; }
break;
- case 154: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */
-{ yymsp[-10].minor.yy95 = sqlite3UpsertNew(pParse->db,yymsp[-7].minor.yy94,yymsp[-5].minor.yy102,yymsp[-1].minor.yy94,yymsp[0].minor.yy102);}
+ case 157: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */
+{ yymsp[-10].minor.yy288 = sqlite3UpsertNew(pParse->db,yymsp[-7].minor.yy138,yymsp[-5].minor.yy46,yymsp[-1].minor.yy138,yymsp[0].minor.yy46);}
break;
- case 155: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */
-{ yymsp[-7].minor.yy95 = sqlite3UpsertNew(pParse->db,yymsp[-4].minor.yy94,yymsp[-2].minor.yy102,0,0); }
+ case 158: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */
+{ yymsp[-7].minor.yy288 = sqlite3UpsertNew(pParse->db,yymsp[-4].minor.yy138,yymsp[-2].minor.yy46,0,0); }
break;
- case 156: /* upsert ::= ON CONFLICT DO NOTHING */
-{ yymsp[-3].minor.yy95 = sqlite3UpsertNew(pParse->db,0,0,0,0); }
+ case 159: /* upsert ::= ON CONFLICT DO NOTHING */
+{ yymsp[-3].minor.yy288 = sqlite3UpsertNew(pParse->db,0,0,0,0); }
break;
- case 160: /* idlist_opt ::= LP idlist RP */
-{yymsp[-2].minor.yy76 = yymsp[-1].minor.yy76;}
+ case 163: /* idlist_opt ::= LP idlist RP */
+{yymsp[-2].minor.yy406 = yymsp[-1].minor.yy406;}
break;
- case 161: /* idlist ::= idlist COMMA nm */
-{yymsp[-2].minor.yy76 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy76,&yymsp[0].minor.yy0);}
+ case 164: /* idlist ::= idlist COMMA nm */
+{yymsp[-2].minor.yy406 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy406,&yymsp[0].minor.yy0);}
break;
- case 162: /* idlist ::= nm */
-{yymsp[0].minor.yy76 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/}
+ case 165: /* idlist ::= nm */
+{yymsp[0].minor.yy406 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/}
break;
- case 163: /* expr ::= LP expr RP */
-{yymsp[-2].minor.yy102 = yymsp[-1].minor.yy102;}
+ case 166: /* expr ::= LP expr RP */
+{yymsp[-2].minor.yy46 = yymsp[-1].minor.yy46;}
break;
- case 164: /* expr ::= ID|INDEXED */
- case 165: /* expr ::= JOIN_KW */ yytestcase(yyruleno==165);
-{yymsp[0].minor.yy102=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/}
+ case 167: /* expr ::= ID|INDEXED */
+ case 168: /* expr ::= JOIN_KW */ yytestcase(yyruleno==168);
+{yymsp[0].minor.yy46=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
- case 166: /* expr ::= nm DOT nm */
+ case 169: /* expr ::= nm DOT nm */
{
Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1);
Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[0].minor.yy0, 1);
@@ -152565,11 +153611,11 @@ static YYACTIONTYPE yy_reduce(
sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[0].minor.yy0);
sqlite3RenameTokenMap(pParse, (void*)temp1, &yymsp[-2].minor.yy0);
}
- yylhsminor.yy102 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
+ yylhsminor.yy46 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
}
- yymsp[-2].minor.yy102 = yylhsminor.yy102;
+ yymsp[-2].minor.yy46 = yylhsminor.yy46;
break;
- case 167: /* expr ::= nm DOT nm DOT nm */
+ case 170: /* expr ::= nm DOT nm DOT nm */
{
Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-4].minor.yy0, 1);
Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1);
@@ -152579,26 +153625,26 @@ static YYACTIONTYPE yy_reduce(
sqlite3RenameTokenMap(pParse, (void*)temp3, &yymsp[0].minor.yy0);
sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[-2].minor.yy0);
}
- yylhsminor.yy102 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
+ yylhsminor.yy46 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
}
- yymsp[-4].minor.yy102 = yylhsminor.yy102;
+ yymsp[-4].minor.yy46 = yylhsminor.yy46;
break;
- case 168: /* term ::= NULL|FLOAT|BLOB */
- case 169: /* term ::= STRING */ yytestcase(yyruleno==169);
-{yymsp[0].minor.yy102=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/}
+ case 171: /* term ::= NULL|FLOAT|BLOB */
+ case 172: /* term ::= STRING */ yytestcase(yyruleno==172);
+{yymsp[0].minor.yy46=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
- case 170: /* term ::= INTEGER */
+ case 173: /* term ::= INTEGER */
{
- yylhsminor.yy102 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1);
+ yylhsminor.yy46 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1);
}
- yymsp[0].minor.yy102 = yylhsminor.yy102;
+ yymsp[0].minor.yy46 = yylhsminor.yy46;
break;
- case 171: /* expr ::= VARIABLE */
+ case 174: /* expr ::= VARIABLE */
{
if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){
u32 n = yymsp[0].minor.yy0.n;
- yymsp[0].minor.yy102 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0);
- sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy102, n);
+ yymsp[0].minor.yy46 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0);
+ sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy46, n);
}else{
/* When doing a nested parse, one can include terms in an expression
** that look like this: #1 #2 ... These terms refer to registers
@@ -152607,156 +153653,156 @@ static YYACTIONTYPE yy_reduce(
assert( t.n>=2 );
if( pParse->nested==0 ){
sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t);
- yymsp[0].minor.yy102 = 0;
+ yymsp[0].minor.yy46 = 0;
}else{
- yymsp[0].minor.yy102 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
- if( yymsp[0].minor.yy102 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy102->iTable);
+ yymsp[0].minor.yy46 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
+ if( yymsp[0].minor.yy46 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy46->iTable);
}
}
}
break;
- case 172: /* expr ::= expr COLLATE ID|STRING */
+ case 175: /* expr ::= expr COLLATE ID|STRING */
{
- yymsp[-2].minor.yy102 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy102, &yymsp[0].minor.yy0, 1);
+ yymsp[-2].minor.yy46 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy46, &yymsp[0].minor.yy0, 1);
}
break;
- case 173: /* expr ::= CAST LP expr AS typetoken RP */
+ case 176: /* expr ::= CAST LP expr AS typetoken RP */
{
- yymsp[-5].minor.yy102 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1);
- sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy102, yymsp[-3].minor.yy102, 0);
+ yymsp[-5].minor.yy46 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1);
+ sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy46, yymsp[-3].minor.yy46, 0);
}
break;
- case 174: /* expr ::= ID|INDEXED LP distinct exprlist RP */
+ case 177: /* expr ::= ID|INDEXED LP distinct exprlist RP */
{
- yylhsminor.yy102 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy94, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy100);
+ yylhsminor.yy46 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy138, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy32);
}
- yymsp[-4].minor.yy102 = yylhsminor.yy102;
+ yymsp[-4].minor.yy46 = yylhsminor.yy46;
break;
- case 175: /* expr ::= ID|INDEXED LP STAR RP */
+ case 178: /* expr ::= ID|INDEXED LP STAR RP */
{
- yylhsminor.yy102 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0);
+ yylhsminor.yy46 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0);
}
- yymsp[-3].minor.yy102 = yylhsminor.yy102;
+ yymsp[-3].minor.yy46 = yylhsminor.yy46;
break;
- case 176: /* expr ::= ID|INDEXED LP distinct exprlist RP over_clause */
+ case 179: /* expr ::= ID|INDEXED LP distinct exprlist RP filter_over */
{
- yylhsminor.yy102 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy94, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy100);
- sqlite3WindowAttach(pParse, yylhsminor.yy102, yymsp[0].minor.yy379);
+ yylhsminor.yy46 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy138, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy32);
+ sqlite3WindowAttach(pParse, yylhsminor.yy46, yymsp[0].minor.yy455);
}
- yymsp[-5].minor.yy102 = yylhsminor.yy102;
+ yymsp[-5].minor.yy46 = yylhsminor.yy46;
break;
- case 177: /* expr ::= ID|INDEXED LP STAR RP over_clause */
+ case 180: /* expr ::= ID|INDEXED LP STAR RP filter_over */
{
- yylhsminor.yy102 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0);
- sqlite3WindowAttach(pParse, yylhsminor.yy102, yymsp[0].minor.yy379);
+ yylhsminor.yy46 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0);
+ sqlite3WindowAttach(pParse, yylhsminor.yy46, yymsp[0].minor.yy455);
}
- yymsp[-4].minor.yy102 = yylhsminor.yy102;
+ yymsp[-4].minor.yy46 = yylhsminor.yy46;
break;
- case 178: /* term ::= CTIME_KW */
+ case 181: /* term ::= CTIME_KW */
{
- yylhsminor.yy102 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0);
+ yylhsminor.yy46 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0);
}
- yymsp[0].minor.yy102 = yylhsminor.yy102;
+ yymsp[0].minor.yy46 = yylhsminor.yy46;
break;
- case 179: /* expr ::= LP nexprlist COMMA expr RP */
+ case 182: /* expr ::= LP nexprlist COMMA expr RP */
{
- ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy94, yymsp[-1].minor.yy102);
- yymsp[-4].minor.yy102 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
- if( yymsp[-4].minor.yy102 ){
- yymsp[-4].minor.yy102->x.pList = pList;
+ ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy138, yymsp[-1].minor.yy46);
+ yymsp[-4].minor.yy46 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
+ if( yymsp[-4].minor.yy46 ){
+ yymsp[-4].minor.yy46->x.pList = pList;
}else{
sqlite3ExprListDelete(pParse->db, pList);
}
}
break;
- case 180: /* expr ::= expr AND expr */
-{yymsp[-2].minor.yy102=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy102,yymsp[0].minor.yy102);}
+ case 183: /* expr ::= expr AND expr */
+{yymsp[-2].minor.yy46=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy46,yymsp[0].minor.yy46);}
break;
- case 181: /* expr ::= expr OR expr */
- case 182: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==182);
- case 183: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==183);
- case 184: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==184);
- case 185: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==185);
- case 186: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==186);
- case 187: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==187);
-{yymsp[-2].minor.yy102=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy102,yymsp[0].minor.yy102);}
+ case 184: /* expr ::= expr OR expr */
+ case 185: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==185);
+ case 186: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==186);
+ case 187: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==187);
+ case 188: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==188);
+ case 189: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==189);
+ case 190: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==190);
+{yymsp[-2].minor.yy46=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy46,yymsp[0].minor.yy46);}
break;
- case 188: /* likeop ::= NOT LIKE_KW|MATCH */
+ case 191: /* likeop ::= NOT LIKE_KW|MATCH */
{yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/}
break;
- case 189: /* expr ::= expr likeop expr */
+ case 192: /* expr ::= expr likeop expr */
{
ExprList *pList;
int bNot = yymsp[-1].minor.yy0.n & 0x80000000;
yymsp[-1].minor.yy0.n &= 0x7fffffff;
- pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy102);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy102);
- yymsp[-2].minor.yy102 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
- if( bNot ) yymsp[-2].minor.yy102 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy102, 0);
- if( yymsp[-2].minor.yy102 ) yymsp[-2].minor.yy102->flags |= EP_InfixFunc;
+ pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy46);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy46);
+ yymsp[-2].minor.yy46 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
+ if( bNot ) yymsp[-2].minor.yy46 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy46, 0);
+ if( yymsp[-2].minor.yy46 ) yymsp[-2].minor.yy46->flags |= EP_InfixFunc;
}
break;
- case 190: /* expr ::= expr likeop expr ESCAPE expr */
+ case 193: /* expr ::= expr likeop expr ESCAPE expr */
{
ExprList *pList;
int bNot = yymsp[-3].minor.yy0.n & 0x80000000;
yymsp[-3].minor.yy0.n &= 0x7fffffff;
- pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy102);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy102);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy102);
- yymsp[-4].minor.yy102 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0);
- if( bNot ) yymsp[-4].minor.yy102 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy102, 0);
- if( yymsp[-4].minor.yy102 ) yymsp[-4].minor.yy102->flags |= EP_InfixFunc;
+ pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy46);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy46);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy46);
+ yymsp[-4].minor.yy46 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0);
+ if( bNot ) yymsp[-4].minor.yy46 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy46, 0);
+ if( yymsp[-4].minor.yy46 ) yymsp[-4].minor.yy46->flags |= EP_InfixFunc;
}
break;
- case 191: /* expr ::= expr ISNULL|NOTNULL */
-{yymsp[-1].minor.yy102 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy102,0);}
+ case 194: /* expr ::= expr ISNULL|NOTNULL */
+{yymsp[-1].minor.yy46 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy46,0);}
break;
- case 192: /* expr ::= expr NOT NULL */
-{yymsp[-2].minor.yy102 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy102,0);}
+ case 195: /* expr ::= expr NOT NULL */
+{yymsp[-2].minor.yy46 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy46,0);}
break;
- case 193: /* expr ::= expr IS expr */
+ case 196: /* expr ::= expr IS expr */
{
- yymsp[-2].minor.yy102 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy102,yymsp[0].minor.yy102);
- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy102, yymsp[-2].minor.yy102, TK_ISNULL);
+ yymsp[-2].minor.yy46 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy46,yymsp[0].minor.yy46);
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy46, yymsp[-2].minor.yy46, TK_ISNULL);
}
break;
- case 194: /* expr ::= expr IS NOT expr */
+ case 197: /* expr ::= expr IS NOT expr */
{
- yymsp[-3].minor.yy102 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy102,yymsp[0].minor.yy102);
- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy102, yymsp[-3].minor.yy102, TK_NOTNULL);
+ yymsp[-3].minor.yy46 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy46,yymsp[0].minor.yy46);
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy46, yymsp[-3].minor.yy46, TK_NOTNULL);
}
break;
- case 195: /* expr ::= NOT expr */
- case 196: /* expr ::= BITNOT expr */ yytestcase(yyruleno==196);
-{yymsp[-1].minor.yy102 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy102, 0);/*A-overwrites-B*/}
+ case 198: /* expr ::= NOT expr */
+ case 199: /* expr ::= BITNOT expr */ yytestcase(yyruleno==199);
+{yymsp[-1].minor.yy46 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy46, 0);/*A-overwrites-B*/}
break;
- case 197: /* expr ::= PLUS|MINUS expr */
+ case 200: /* expr ::= PLUS|MINUS expr */
{
- yymsp[-1].minor.yy102 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy102, 0);
+ yymsp[-1].minor.yy46 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy46, 0);
/*A-overwrites-B*/
}
break;
- case 198: /* between_op ::= BETWEEN */
- case 201: /* in_op ::= IN */ yytestcase(yyruleno==201);
-{yymsp[0].minor.yy100 = 0;}
+ case 201: /* between_op ::= BETWEEN */
+ case 204: /* in_op ::= IN */ yytestcase(yyruleno==204);
+{yymsp[0].minor.yy32 = 0;}
break;
- case 200: /* expr ::= expr between_op expr AND expr */
+ case 203: /* expr ::= expr between_op expr AND expr */
{
- ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy102);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy102);
- yymsp[-4].minor.yy102 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy102, 0);
- if( yymsp[-4].minor.yy102 ){
- yymsp[-4].minor.yy102->x.pList = pList;
+ ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy46);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy46);
+ yymsp[-4].minor.yy46 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy46, 0);
+ if( yymsp[-4].minor.yy46 ){
+ yymsp[-4].minor.yy46->x.pList = pList;
}else{
sqlite3ExprListDelete(pParse->db, pList);
}
- if( yymsp[-3].minor.yy100 ) yymsp[-4].minor.yy102 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy102, 0);
+ if( yymsp[-3].minor.yy32 ) yymsp[-4].minor.yy46 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy46, 0);
}
break;
- case 203: /* expr ::= expr in_op LP exprlist RP */
+ case 206: /* expr ::= expr in_op LP exprlist RP */
{
- if( yymsp[-1].minor.yy94==0 ){
+ if( yymsp[-1].minor.yy138==0 ){
/* Expressions of the form
**
** expr1 IN ()
@@ -152765,218 +153811,190 @@ static YYACTIONTYPE yy_reduce(
** simplify to constants 0 (false) and 1 (true), respectively,
** regardless of the value of expr1.
*/
- sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy102);
- yymsp[-4].minor.yy102 = sqlite3ExprAlloc(pParse->db, TK_INTEGER,&sqlite3IntTokens[yymsp[-3].minor.yy100],1);
- }else if( yymsp[-1].minor.yy94->nExpr==1 ){
- /* Expressions of the form:
- **
- ** expr1 IN (?1)
- ** expr1 NOT IN (?2)
- **
- ** with exactly one value on the RHS can be simplified to something
- ** like this:
- **
- ** expr1 == ?1
- ** expr1 <> ?2
- **
- ** But, the RHS of the == or <> is marked with the EP_Generic flag
- ** so that it may not contribute to the computation of comparison
- ** affinity or the collating sequence to use for comparison. Otherwise,
- ** the semantics would be subtly different from IN or NOT IN.
- */
- Expr *pRHS = yymsp[-1].minor.yy94->a[0].pExpr;
- yymsp[-1].minor.yy94->a[0].pExpr = 0;
- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy94);
- /* pRHS cannot be NULL because a malloc error would have been detected
- ** before now and control would have never reached this point */
- if( ALWAYS(pRHS) ){
- pRHS->flags &= ~EP_Collate;
- pRHS->flags |= EP_Generic;
- }
- yymsp[-4].minor.yy102 = sqlite3PExpr(pParse, yymsp[-3].minor.yy100 ? TK_NE : TK_EQ, yymsp[-4].minor.yy102, pRHS);
- }else{
- yymsp[-4].minor.yy102 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy102, 0);
- if( yymsp[-4].minor.yy102 ){
- yymsp[-4].minor.yy102->x.pList = yymsp[-1].minor.yy94;
- sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy102);
+ sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy46);
+ yymsp[-4].minor.yy46 = sqlite3Expr(pParse->db, TK_INTEGER, yymsp[-3].minor.yy32 ? "1" : "0");
+ }else{
+ yymsp[-4].minor.yy46 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy46, 0);
+ if( yymsp[-4].minor.yy46 ){
+ yymsp[-4].minor.yy46->x.pList = yymsp[-1].minor.yy138;
+ sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy46);
}else{
- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy94);
+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy138);
}
- if( yymsp[-3].minor.yy100 ) yymsp[-4].minor.yy102 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy102, 0);
+ if( yymsp[-3].minor.yy32 ) yymsp[-4].minor.yy46 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy46, 0);
}
}
break;
- case 204: /* expr ::= LP select RP */
+ case 207: /* expr ::= LP select RP */
{
- yymsp[-2].minor.yy102 = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
- sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy102, yymsp[-1].minor.yy391);
+ yymsp[-2].minor.yy46 = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy46, yymsp[-1].minor.yy25);
}
break;
- case 205: /* expr ::= expr in_op LP select RP */
+ case 208: /* expr ::= expr in_op LP select RP */
{
- yymsp[-4].minor.yy102 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy102, 0);
- sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy102, yymsp[-1].minor.yy391);
- if( yymsp[-3].minor.yy100 ) yymsp[-4].minor.yy102 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy102, 0);
+ yymsp[-4].minor.yy46 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy46, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy46, yymsp[-1].minor.yy25);
+ if( yymsp[-3].minor.yy32 ) yymsp[-4].minor.yy46 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy46, 0);
}
break;
- case 206: /* expr ::= expr in_op nm dbnm paren_exprlist */
+ case 209: /* expr ::= expr in_op nm dbnm paren_exprlist */
{
SrcList *pSrc = sqlite3SrcListAppend(pParse, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);
Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0);
- if( yymsp[0].minor.yy94 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy94);
- yymsp[-4].minor.yy102 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy102, 0);
- sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy102, pSelect);
- if( yymsp[-3].minor.yy100 ) yymsp[-4].minor.yy102 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy102, 0);
+ if( yymsp[0].minor.yy138 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy138);
+ yymsp[-4].minor.yy46 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy46, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy46, pSelect);
+ if( yymsp[-3].minor.yy32 ) yymsp[-4].minor.yy46 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy46, 0);
}
break;
- case 207: /* expr ::= EXISTS LP select RP */
+ case 210: /* expr ::= EXISTS LP select RP */
{
Expr *p;
- p = yymsp[-3].minor.yy102 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0);
- sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy391);
+ p = yymsp[-3].minor.yy46 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0);
+ sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy25);
}
break;
- case 208: /* expr ::= CASE case_operand case_exprlist case_else END */
+ case 211: /* expr ::= CASE case_operand case_exprlist case_else END */
{
- yymsp[-4].minor.yy102 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy102, 0);
- if( yymsp[-4].minor.yy102 ){
- yymsp[-4].minor.yy102->x.pList = yymsp[-1].minor.yy102 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy94,yymsp[-1].minor.yy102) : yymsp[-2].minor.yy94;
- sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy102);
+ yymsp[-4].minor.yy46 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy46, 0);
+ if( yymsp[-4].minor.yy46 ){
+ yymsp[-4].minor.yy46->x.pList = yymsp[-1].minor.yy46 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy138,yymsp[-1].minor.yy46) : yymsp[-2].minor.yy138;
+ sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy46);
}else{
- sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy94);
- sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy102);
+ sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy138);
+ sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy46);
}
}
break;
- case 209: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
+ case 212: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
{
- yymsp[-4].minor.yy94 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy94, yymsp[-2].minor.yy102);
- yymsp[-4].minor.yy94 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy94, yymsp[0].minor.yy102);
+ yymsp[-4].minor.yy138 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy138, yymsp[-2].minor.yy46);
+ yymsp[-4].minor.yy138 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy138, yymsp[0].minor.yy46);
}
break;
- case 210: /* case_exprlist ::= WHEN expr THEN expr */
+ case 213: /* case_exprlist ::= WHEN expr THEN expr */
{
- yymsp[-3].minor.yy94 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy102);
- yymsp[-3].minor.yy94 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy94, yymsp[0].minor.yy102);
+ yymsp[-3].minor.yy138 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy46);
+ yymsp[-3].minor.yy138 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy138, yymsp[0].minor.yy46);
}
break;
- case 213: /* case_operand ::= expr */
-{yymsp[0].minor.yy102 = yymsp[0].minor.yy102; /*A-overwrites-X*/}
+ case 216: /* case_operand ::= expr */
+{yymsp[0].minor.yy46 = yymsp[0].minor.yy46; /*A-overwrites-X*/}
break;
- case 216: /* nexprlist ::= nexprlist COMMA expr */
-{yymsp[-2].minor.yy94 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy94,yymsp[0].minor.yy102);}
+ case 219: /* nexprlist ::= nexprlist COMMA expr */
+{yymsp[-2].minor.yy138 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy138,yymsp[0].minor.yy46);}
break;
- case 217: /* nexprlist ::= expr */
-{yymsp[0].minor.yy94 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy102); /*A-overwrites-Y*/}
+ case 220: /* nexprlist ::= expr */
+{yymsp[0].minor.yy138 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy46); /*A-overwrites-Y*/}
break;
- case 219: /* paren_exprlist ::= LP exprlist RP */
- case 224: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==224);
-{yymsp[-2].minor.yy94 = yymsp[-1].minor.yy94;}
+ case 222: /* paren_exprlist ::= LP exprlist RP */
+ case 227: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==227);
+{yymsp[-2].minor.yy138 = yymsp[-1].minor.yy138;}
break;
- case 220: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
+ case 223: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
{
sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0,
- sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy94, yymsp[-10].minor.yy100,
- &yymsp[-11].minor.yy0, yymsp[0].minor.yy102, SQLITE_SO_ASC, yymsp[-8].minor.yy100, SQLITE_IDXTYPE_APPDEF);
+ sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy138, yymsp[-10].minor.yy32,
+ &yymsp[-11].minor.yy0, yymsp[0].minor.yy46, SQLITE_SO_ASC, yymsp[-8].minor.yy32, SQLITE_IDXTYPE_APPDEF);
if( IN_RENAME_OBJECT && pParse->pNewIndex ){
sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0);
}
}
break;
- case 221: /* uniqueflag ::= UNIQUE */
- case 263: /* raisetype ::= ABORT */ yytestcase(yyruleno==263);
-{yymsp[0].minor.yy100 = OE_Abort;}
+ case 224: /* uniqueflag ::= UNIQUE */
+ case 266: /* raisetype ::= ABORT */ yytestcase(yyruleno==266);
+{yymsp[0].minor.yy32 = OE_Abort;}
break;
- case 222: /* uniqueflag ::= */
-{yymsp[1].minor.yy100 = OE_None;}
+ case 225: /* uniqueflag ::= */
+{yymsp[1].minor.yy32 = OE_None;}
break;
- case 225: /* eidlist ::= eidlist COMMA nm collate sortorder */
+ case 228: /* eidlist ::= eidlist COMMA nm collate sortorder */
{
- yymsp[-4].minor.yy94 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy94, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy100, yymsp[0].minor.yy100);
+ yymsp[-4].minor.yy138 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy138, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy32, yymsp[0].minor.yy32);
}
break;
- case 226: /* eidlist ::= nm collate sortorder */
+ case 229: /* eidlist ::= nm collate sortorder */
{
- yymsp[-2].minor.yy94 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy100, yymsp[0].minor.yy100); /*A-overwrites-Y*/
+ yymsp[-2].minor.yy138 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy32, yymsp[0].minor.yy32); /*A-overwrites-Y*/
}
break;
- case 229: /* cmd ::= DROP INDEX ifexists fullname */
-{sqlite3DropIndex(pParse, yymsp[0].minor.yy407, yymsp[-1].minor.yy100);}
+ case 232: /* cmd ::= DROP INDEX ifexists fullname */
+{sqlite3DropIndex(pParse, yymsp[0].minor.yy609, yymsp[-1].minor.yy32);}
break;
- case 230: /* cmd ::= VACUUM vinto */
-{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy102);}
+ case 233: /* cmd ::= VACUUM vinto */
+{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy46);}
break;
- case 231: /* cmd ::= VACUUM nm vinto */
-{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy102);}
+ case 234: /* cmd ::= VACUUM nm vinto */
+{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy46);}
break;
- case 234: /* cmd ::= PRAGMA nm dbnm */
+ case 237: /* cmd ::= PRAGMA nm dbnm */
{sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);}
break;
- case 235: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
+ case 238: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);}
break;
- case 236: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
+ case 239: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);}
break;
- case 237: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
+ case 240: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);}
break;
- case 238: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */
+ case 241: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);}
break;
- case 241: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
+ case 244: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
{
Token all;
all.z = yymsp[-3].minor.yy0.z;
all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n;
- sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy11, &all);
+ sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy527, &all);
}
break;
- case 242: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
+ case 245: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
{
- sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy100, yymsp[-4].minor.yy298.a, yymsp[-4].minor.yy298.b, yymsp[-2].minor.yy407, yymsp[0].minor.yy102, yymsp[-10].minor.yy100, yymsp[-8].minor.yy100);
+ sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy32, yymsp[-4].minor.yy572.a, yymsp[-4].minor.yy572.b, yymsp[-2].minor.yy609, yymsp[0].minor.yy46, yymsp[-10].minor.yy32, yymsp[-8].minor.yy32);
yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/
}
break;
- case 243: /* trigger_time ::= BEFORE|AFTER */
-{ yymsp[0].minor.yy100 = yymsp[0].major; /*A-overwrites-X*/ }
+ case 246: /* trigger_time ::= BEFORE|AFTER */
+{ yymsp[0].minor.yy32 = yymsp[0].major; /*A-overwrites-X*/ }
break;
- case 244: /* trigger_time ::= INSTEAD OF */
-{ yymsp[-1].minor.yy100 = TK_INSTEAD;}
+ case 247: /* trigger_time ::= INSTEAD OF */
+{ yymsp[-1].minor.yy32 = TK_INSTEAD;}
break;
- case 245: /* trigger_time ::= */
-{ yymsp[1].minor.yy100 = TK_BEFORE; }
+ case 248: /* trigger_time ::= */
+{ yymsp[1].minor.yy32 = TK_BEFORE; }
break;
- case 246: /* trigger_event ::= DELETE|INSERT */
- case 247: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==247);
-{yymsp[0].minor.yy298.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy298.b = 0;}
+ case 249: /* trigger_event ::= DELETE|INSERT */
+ case 250: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==250);
+{yymsp[0].minor.yy572.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy572.b = 0;}
break;
- case 248: /* trigger_event ::= UPDATE OF idlist */
-{yymsp[-2].minor.yy298.a = TK_UPDATE; yymsp[-2].minor.yy298.b = yymsp[0].minor.yy76;}
+ case 251: /* trigger_event ::= UPDATE OF idlist */
+{yymsp[-2].minor.yy572.a = TK_UPDATE; yymsp[-2].minor.yy572.b = yymsp[0].minor.yy406;}
break;
- case 249: /* when_clause ::= */
- case 268: /* key_opt ::= */ yytestcase(yyruleno==268);
- case 316: /* filter_opt ::= */ yytestcase(yyruleno==316);
-{ yymsp[1].minor.yy102 = 0; }
+ case 252: /* when_clause ::= */
+ case 271: /* key_opt ::= */ yytestcase(yyruleno==271);
+{ yymsp[1].minor.yy46 = 0; }
break;
- case 250: /* when_clause ::= WHEN expr */
- case 269: /* key_opt ::= KEY expr */ yytestcase(yyruleno==269);
-{ yymsp[-1].minor.yy102 = yymsp[0].minor.yy102; }
+ case 253: /* when_clause ::= WHEN expr */
+ case 272: /* key_opt ::= KEY expr */ yytestcase(yyruleno==272);
+{ yymsp[-1].minor.yy46 = yymsp[0].minor.yy46; }
break;
- case 251: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
+ case 254: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
{
- assert( yymsp[-2].minor.yy11!=0 );
- yymsp[-2].minor.yy11->pLast->pNext = yymsp[-1].minor.yy11;
- yymsp[-2].minor.yy11->pLast = yymsp[-1].minor.yy11;
+ assert( yymsp[-2].minor.yy527!=0 );
+ yymsp[-2].minor.yy527->pLast->pNext = yymsp[-1].minor.yy527;
+ yymsp[-2].minor.yy527->pLast = yymsp[-1].minor.yy527;
}
break;
- case 252: /* trigger_cmd_list ::= trigger_cmd SEMI */
+ case 255: /* trigger_cmd_list ::= trigger_cmd SEMI */
{
- assert( yymsp[-1].minor.yy11!=0 );
- yymsp[-1].minor.yy11->pLast = yymsp[-1].minor.yy11;
+ assert( yymsp[-1].minor.yy527!=0 );
+ yymsp[-1].minor.yy527->pLast = yymsp[-1].minor.yy527;
}
break;
- case 253: /* trnm ::= nm DOT nm */
+ case 256: /* trnm ::= nm DOT nm */
{
yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;
sqlite3ErrorMsg(pParse,
@@ -152984,328 +154002,342 @@ static YYACTIONTYPE yy_reduce(
"statements within triggers");
}
break;
- case 254: /* tridxby ::= INDEXED BY nm */
+ case 257: /* tridxby ::= INDEXED BY nm */
{
sqlite3ErrorMsg(pParse,
"the INDEXED BY clause is not allowed on UPDATE or DELETE statements "
"within triggers");
}
break;
- case 255: /* tridxby ::= NOT INDEXED */
+ case 258: /* tridxby ::= NOT INDEXED */
{
sqlite3ErrorMsg(pParse,
"the NOT INDEXED clause is not allowed on UPDATE or DELETE statements "
"within triggers");
}
break;
- case 256: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */
-{yylhsminor.yy11 = sqlite3TriggerUpdateStep(pParse, &yymsp[-5].minor.yy0, yymsp[-2].minor.yy94, yymsp[-1].minor.yy102, yymsp[-6].minor.yy100, yymsp[-7].minor.yy0.z, yymsp[0].minor.yy528);}
- yymsp[-7].minor.yy11 = yylhsminor.yy11;
+ case 259: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt scanpt */
+{yylhsminor.yy527 = sqlite3TriggerUpdateStep(pParse, &yymsp[-5].minor.yy0, yymsp[-2].minor.yy138, yymsp[-1].minor.yy46, yymsp[-6].minor.yy32, yymsp[-7].minor.yy0.z, yymsp[0].minor.yy8);}
+ yymsp[-7].minor.yy527 = yylhsminor.yy527;
break;
- case 257: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
+ case 260: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
{
- yylhsminor.yy11 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy76,yymsp[-2].minor.yy391,yymsp[-6].minor.yy100,yymsp[-1].minor.yy95,yymsp[-7].minor.yy528,yymsp[0].minor.yy528);/*yylhsminor.yy11-overwrites-yymsp[-6].minor.yy100*/
+ yylhsminor.yy527 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy406,yymsp[-2].minor.yy25,yymsp[-6].minor.yy32,yymsp[-1].minor.yy288,yymsp[-7].minor.yy8,yymsp[0].minor.yy8);/*yylhsminor.yy527-overwrites-yymsp[-6].minor.yy32*/
}
- yymsp[-7].minor.yy11 = yylhsminor.yy11;
+ yymsp[-7].minor.yy527 = yylhsminor.yy527;
break;
- case 258: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
-{yylhsminor.yy11 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy102, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy528);}
- yymsp[-5].minor.yy11 = yylhsminor.yy11;
+ case 261: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
+{yylhsminor.yy527 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy46, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy8);}
+ yymsp[-5].minor.yy527 = yylhsminor.yy527;
break;
- case 259: /* trigger_cmd ::= scanpt select scanpt */
-{yylhsminor.yy11 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy391, yymsp[-2].minor.yy528, yymsp[0].minor.yy528); /*yylhsminor.yy11-overwrites-yymsp[-1].minor.yy391*/}
- yymsp[-2].minor.yy11 = yylhsminor.yy11;
+ case 262: /* trigger_cmd ::= scanpt select scanpt */
+{yylhsminor.yy527 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy25, yymsp[-2].minor.yy8, yymsp[0].minor.yy8); /*yylhsminor.yy527-overwrites-yymsp[-1].minor.yy25*/}
+ yymsp[-2].minor.yy527 = yylhsminor.yy527;
break;
- case 260: /* expr ::= RAISE LP IGNORE RP */
+ case 263: /* expr ::= RAISE LP IGNORE RP */
{
- yymsp[-3].minor.yy102 = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
- if( yymsp[-3].minor.yy102 ){
- yymsp[-3].minor.yy102->affinity = OE_Ignore;
+ yymsp[-3].minor.yy46 = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
+ if( yymsp[-3].minor.yy46 ){
+ yymsp[-3].minor.yy46->affExpr = OE_Ignore;
}
}
break;
- case 261: /* expr ::= RAISE LP raisetype COMMA nm RP */
+ case 264: /* expr ::= RAISE LP raisetype COMMA nm RP */
{
- yymsp[-5].minor.yy102 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1);
- if( yymsp[-5].minor.yy102 ) {
- yymsp[-5].minor.yy102->affinity = (char)yymsp[-3].minor.yy100;
+ yymsp[-5].minor.yy46 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1);
+ if( yymsp[-5].minor.yy46 ) {
+ yymsp[-5].minor.yy46->affExpr = (char)yymsp[-3].minor.yy32;
}
}
break;
- case 262: /* raisetype ::= ROLLBACK */
-{yymsp[0].minor.yy100 = OE_Rollback;}
+ case 265: /* raisetype ::= ROLLBACK */
+{yymsp[0].minor.yy32 = OE_Rollback;}
break;
- case 264: /* raisetype ::= FAIL */
-{yymsp[0].minor.yy100 = OE_Fail;}
+ case 267: /* raisetype ::= FAIL */
+{yymsp[0].minor.yy32 = OE_Fail;}
break;
- case 265: /* cmd ::= DROP TRIGGER ifexists fullname */
+ case 268: /* cmd ::= DROP TRIGGER ifexists fullname */
{
- sqlite3DropTrigger(pParse,yymsp[0].minor.yy407,yymsp[-1].minor.yy100);
+ sqlite3DropTrigger(pParse,yymsp[0].minor.yy609,yymsp[-1].minor.yy32);
}
break;
- case 266: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+ case 269: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
{
- sqlite3Attach(pParse, yymsp[-3].minor.yy102, yymsp[-1].minor.yy102, yymsp[0].minor.yy102);
+ sqlite3Attach(pParse, yymsp[-3].minor.yy46, yymsp[-1].minor.yy46, yymsp[0].minor.yy46);
}
break;
- case 267: /* cmd ::= DETACH database_kw_opt expr */
+ case 270: /* cmd ::= DETACH database_kw_opt expr */
{
- sqlite3Detach(pParse, yymsp[0].minor.yy102);
+ sqlite3Detach(pParse, yymsp[0].minor.yy46);
}
break;
- case 270: /* cmd ::= REINDEX */
+ case 273: /* cmd ::= REINDEX */
{sqlite3Reindex(pParse, 0, 0);}
break;
- case 271: /* cmd ::= REINDEX nm dbnm */
+ case 274: /* cmd ::= REINDEX nm dbnm */
{sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
break;
- case 272: /* cmd ::= ANALYZE */
+ case 275: /* cmd ::= ANALYZE */
{sqlite3Analyze(pParse, 0, 0);}
break;
- case 273: /* cmd ::= ANALYZE nm dbnm */
+ case 276: /* cmd ::= ANALYZE nm dbnm */
{sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
break;
- case 274: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
+ case 277: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
{
- sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy407,&yymsp[0].minor.yy0);
+ sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy609,&yymsp[0].minor.yy0);
}
break;
- case 275: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
+ case 278: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
{
yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n;
sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0);
}
break;
- case 276: /* add_column_fullname ::= fullname */
+ case 279: /* add_column_fullname ::= fullname */
{
disableLookaside(pParse);
- sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy407);
+ sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy609);
}
break;
- case 277: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
+ case 280: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
{
- sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy407, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0);
+ sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy609, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0);
}
break;
- case 278: /* cmd ::= create_vtab */
+ case 281: /* cmd ::= create_vtab */
{sqlite3VtabFinishParse(pParse,0);}
break;
- case 279: /* cmd ::= create_vtab LP vtabarglist RP */
+ case 282: /* cmd ::= create_vtab LP vtabarglist RP */
{sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);}
break;
- case 280: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
+ case 283: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
{
- sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy100);
+ sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy32);
}
break;
- case 281: /* vtabarg ::= */
+ case 284: /* vtabarg ::= */
{sqlite3VtabArgInit(pParse);}
break;
- case 282: /* vtabargtoken ::= ANY */
- case 283: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==283);
- case 284: /* lp ::= LP */ yytestcase(yyruleno==284);
+ case 285: /* vtabargtoken ::= ANY */
+ case 286: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==286);
+ case 287: /* lp ::= LP */ yytestcase(yyruleno==287);
{sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);}
break;
- case 285: /* with ::= WITH wqlist */
- case 286: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==286);
-{ sqlite3WithPush(pParse, yymsp[0].minor.yy243, 1); }
+ case 288: /* with ::= WITH wqlist */
+ case 289: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==289);
+{ sqlite3WithPush(pParse, yymsp[0].minor.yy297, 1); }
break;
- case 287: /* wqlist ::= nm eidlist_opt AS LP select RP */
+ case 290: /* wqlist ::= nm eidlist_opt AS LP select RP */
{
- yymsp[-5].minor.yy243 = sqlite3WithAdd(pParse, 0, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy94, yymsp[-1].minor.yy391); /*A-overwrites-X*/
+ yymsp[-5].minor.yy297 = sqlite3WithAdd(pParse, 0, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy138, yymsp[-1].minor.yy25); /*A-overwrites-X*/
}
break;
- case 288: /* wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
+ case 291: /* wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
{
- yymsp[-7].minor.yy243 = sqlite3WithAdd(pParse, yymsp[-7].minor.yy243, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy94, yymsp[-1].minor.yy391);
+ yymsp[-7].minor.yy297 = sqlite3WithAdd(pParse, yymsp[-7].minor.yy297, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy138, yymsp[-1].minor.yy25);
}
break;
- case 289: /* windowdefn_list ::= windowdefn */
-{ yylhsminor.yy379 = yymsp[0].minor.yy379; }
- yymsp[0].minor.yy379 = yylhsminor.yy379;
+ case 292: /* windowdefn_list ::= windowdefn */
+{ yylhsminor.yy455 = yymsp[0].minor.yy455; }
+ yymsp[0].minor.yy455 = yylhsminor.yy455;
break;
- case 290: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */
+ case 293: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */
{
- assert( yymsp[0].minor.yy379!=0 );
- sqlite3WindowChain(pParse, yymsp[0].minor.yy379, yymsp[-2].minor.yy379);
- yymsp[0].minor.yy379->pNextWin = yymsp[-2].minor.yy379;
- yylhsminor.yy379 = yymsp[0].minor.yy379;
+ assert( yymsp[0].minor.yy455!=0 );
+ sqlite3WindowChain(pParse, yymsp[0].minor.yy455, yymsp[-2].minor.yy455);
+ yymsp[0].minor.yy455->pNextWin = yymsp[-2].minor.yy455;
+ yylhsminor.yy455 = yymsp[0].minor.yy455;
}
- yymsp[-2].minor.yy379 = yylhsminor.yy379;
+ yymsp[-2].minor.yy455 = yylhsminor.yy455;
break;
- case 291: /* windowdefn ::= nm AS LP window RP */
+ case 294: /* windowdefn ::= nm AS LP window RP */
{
- if( ALWAYS(yymsp[-1].minor.yy379) ){
- yymsp[-1].minor.yy379->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n);
+ if( ALWAYS(yymsp[-1].minor.yy455) ){
+ yymsp[-1].minor.yy455->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n);
}
- yylhsminor.yy379 = yymsp[-1].minor.yy379;
+ yylhsminor.yy455 = yymsp[-1].minor.yy455;
}
- yymsp[-4].minor.yy379 = yylhsminor.yy379;
+ yymsp[-4].minor.yy455 = yylhsminor.yy455;
break;
- case 292: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */
+ case 295: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */
{
- yymsp[-4].minor.yy379 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy379, yymsp[-2].minor.yy94, yymsp[-1].minor.yy94, 0);
+ yymsp[-4].minor.yy455 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy455, yymsp[-2].minor.yy138, yymsp[-1].minor.yy138, 0);
}
break;
- case 293: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
+ case 296: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
{
- yylhsminor.yy379 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy379, yymsp[-2].minor.yy94, yymsp[-1].minor.yy94, &yymsp[-5].minor.yy0);
+ yylhsminor.yy455 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy455, yymsp[-2].minor.yy138, yymsp[-1].minor.yy138, &yymsp[-5].minor.yy0);
}
- yymsp[-5].minor.yy379 = yylhsminor.yy379;
+ yymsp[-5].minor.yy455 = yylhsminor.yy455;
break;
- case 294: /* window ::= ORDER BY sortlist frame_opt */
+ case 297: /* window ::= ORDER BY sortlist frame_opt */
{
- yymsp[-3].minor.yy379 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy379, 0, yymsp[-1].minor.yy94, 0);
+ yymsp[-3].minor.yy455 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy455, 0, yymsp[-1].minor.yy138, 0);
}
break;
- case 295: /* window ::= nm ORDER BY sortlist frame_opt */
+ case 298: /* window ::= nm ORDER BY sortlist frame_opt */
{
- yylhsminor.yy379 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy379, 0, yymsp[-1].minor.yy94, &yymsp[-4].minor.yy0);
+ yylhsminor.yy455 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy455, 0, yymsp[-1].minor.yy138, &yymsp[-4].minor.yy0);
}
- yymsp[-4].minor.yy379 = yylhsminor.yy379;
+ yymsp[-4].minor.yy455 = yylhsminor.yy455;
break;
- case 296: /* window ::= frame_opt */
+ case 299: /* window ::= frame_opt */
+ case 318: /* filter_over ::= over_clause */ yytestcase(yyruleno==318);
{
- yylhsminor.yy379 = yymsp[0].minor.yy379;
+ yylhsminor.yy455 = yymsp[0].minor.yy455;
}
- yymsp[0].minor.yy379 = yylhsminor.yy379;
+ yymsp[0].minor.yy455 = yylhsminor.yy455;
break;
- case 297: /* window ::= nm frame_opt */
+ case 300: /* window ::= nm frame_opt */
{
- yylhsminor.yy379 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy379, 0, 0, &yymsp[-1].minor.yy0);
+ yylhsminor.yy455 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy455, 0, 0, &yymsp[-1].minor.yy0);
}
- yymsp[-1].minor.yy379 = yylhsminor.yy379;
+ yymsp[-1].minor.yy455 = yylhsminor.yy455;
break;
- case 298: /* frame_opt ::= */
+ case 301: /* frame_opt ::= */
{
- yymsp[1].minor.yy379 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0);
+ yymsp[1].minor.yy455 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0);
}
break;
- case 299: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
+ case 302: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
{
- yylhsminor.yy379 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy100, yymsp[-1].minor.yy389.eType, yymsp[-1].minor.yy389.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy218);
+ yylhsminor.yy455 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy32, yymsp[-1].minor.yy57.eType, yymsp[-1].minor.yy57.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy118);
}
- yymsp[-2].minor.yy379 = yylhsminor.yy379;
+ yymsp[-2].minor.yy455 = yylhsminor.yy455;
break;
- case 300: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
+ case 303: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
{
- yylhsminor.yy379 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy100, yymsp[-3].minor.yy389.eType, yymsp[-3].minor.yy389.pExpr, yymsp[-1].minor.yy389.eType, yymsp[-1].minor.yy389.pExpr, yymsp[0].minor.yy218);
+ yylhsminor.yy455 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy32, yymsp[-3].minor.yy57.eType, yymsp[-3].minor.yy57.pExpr, yymsp[-1].minor.yy57.eType, yymsp[-1].minor.yy57.pExpr, yymsp[0].minor.yy118);
}
- yymsp[-5].minor.yy379 = yylhsminor.yy379;
+ yymsp[-5].minor.yy455 = yylhsminor.yy455;
break;
- case 302: /* frame_bound_s ::= frame_bound */
- case 304: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==304);
-{yylhsminor.yy389 = yymsp[0].minor.yy389;}
- yymsp[0].minor.yy389 = yylhsminor.yy389;
+ case 305: /* frame_bound_s ::= frame_bound */
+ case 307: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==307);
+{yylhsminor.yy57 = yymsp[0].minor.yy57;}
+ yymsp[0].minor.yy57 = yylhsminor.yy57;
break;
- case 303: /* frame_bound_s ::= UNBOUNDED PRECEDING */
- case 305: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==305);
- case 307: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==307);
-{yylhsminor.yy389.eType = yymsp[-1].major; yylhsminor.yy389.pExpr = 0;}
- yymsp[-1].minor.yy389 = yylhsminor.yy389;
+ case 306: /* frame_bound_s ::= UNBOUNDED PRECEDING */
+ case 308: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==308);
+ case 310: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==310);
+{yylhsminor.yy57.eType = yymsp[-1].major; yylhsminor.yy57.pExpr = 0;}
+ yymsp[-1].minor.yy57 = yylhsminor.yy57;
break;
- case 306: /* frame_bound ::= expr PRECEDING|FOLLOWING */
-{yylhsminor.yy389.eType = yymsp[0].major; yylhsminor.yy389.pExpr = yymsp[-1].minor.yy102;}
- yymsp[-1].minor.yy389 = yylhsminor.yy389;
+ case 309: /* frame_bound ::= expr PRECEDING|FOLLOWING */
+{yylhsminor.yy57.eType = yymsp[0].major; yylhsminor.yy57.pExpr = yymsp[-1].minor.yy46;}
+ yymsp[-1].minor.yy57 = yylhsminor.yy57;
break;
- case 308: /* frame_exclude_opt ::= */
-{yymsp[1].minor.yy218 = 0;}
+ case 311: /* frame_exclude_opt ::= */
+{yymsp[1].minor.yy118 = 0;}
break;
- case 309: /* frame_exclude_opt ::= EXCLUDE frame_exclude */
-{yymsp[-1].minor.yy218 = yymsp[0].minor.yy218;}
+ case 312: /* frame_exclude_opt ::= EXCLUDE frame_exclude */
+{yymsp[-1].minor.yy118 = yymsp[0].minor.yy118;}
break;
- case 310: /* frame_exclude ::= NO OTHERS */
- case 311: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==311);
-{yymsp[-1].minor.yy218 = yymsp[-1].major; /*A-overwrites-X*/}
+ case 313: /* frame_exclude ::= NO OTHERS */
+ case 314: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==314);
+{yymsp[-1].minor.yy118 = yymsp[-1].major; /*A-overwrites-X*/}
break;
- case 312: /* frame_exclude ::= GROUP|TIES */
-{yymsp[0].minor.yy218 = yymsp[0].major; /*A-overwrites-X*/}
+ case 315: /* frame_exclude ::= GROUP|TIES */
+{yymsp[0].minor.yy118 = yymsp[0].major; /*A-overwrites-X*/}
break;
- case 313: /* window_clause ::= WINDOW windowdefn_list */
-{ yymsp[-1].minor.yy379 = yymsp[0].minor.yy379; }
+ case 316: /* window_clause ::= WINDOW windowdefn_list */
+{ yymsp[-1].minor.yy455 = yymsp[0].minor.yy455; }
break;
- case 314: /* over_clause ::= filter_opt OVER LP window RP */
+ case 317: /* filter_over ::= filter_clause over_clause */
{
- yylhsminor.yy379 = yymsp[-1].minor.yy379;
- assert( yylhsminor.yy379!=0 );
- yylhsminor.yy379->pFilter = yymsp[-4].minor.yy102;
+ yymsp[0].minor.yy455->pFilter = yymsp[-1].minor.yy46;
+ yylhsminor.yy455 = yymsp[0].minor.yy455;
}
- yymsp[-4].minor.yy379 = yylhsminor.yy379;
+ yymsp[-1].minor.yy455 = yylhsminor.yy455;
break;
- case 315: /* over_clause ::= filter_opt OVER nm */
+ case 319: /* filter_over ::= filter_clause */
{
- yylhsminor.yy379 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
- if( yylhsminor.yy379 ){
- yylhsminor.yy379->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n);
- yylhsminor.yy379->pFilter = yymsp[-2].minor.yy102;
+ yylhsminor.yy455 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
+ if( yylhsminor.yy455 ){
+ yylhsminor.yy455->eFrmType = TK_FILTER;
+ yylhsminor.yy455->pFilter = yymsp[0].minor.yy46;
}else{
- sqlite3ExprDelete(pParse->db, yymsp[-2].minor.yy102);
+ sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy46);
}
}
- yymsp[-2].minor.yy379 = yylhsminor.yy379;
+ yymsp[0].minor.yy455 = yylhsminor.yy455;
break;
- case 317: /* filter_opt ::= FILTER LP WHERE expr RP */
-{ yymsp[-4].minor.yy102 = yymsp[-1].minor.yy102; }
+ case 320: /* over_clause ::= OVER LP window RP */
+{
+ yymsp[-3].minor.yy455 = yymsp[-1].minor.yy455;
+ assert( yymsp[-3].minor.yy455!=0 );
+}
+ break;
+ case 321: /* over_clause ::= OVER nm */
+{
+ yymsp[-1].minor.yy455 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
+ if( yymsp[-1].minor.yy455 ){
+ yymsp[-1].minor.yy455->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n);
+ }
+}
+ break;
+ case 322: /* filter_clause ::= FILTER LP WHERE expr RP */
+{ yymsp[-4].minor.yy46 = yymsp[-1].minor.yy46; }
break;
default:
- /* (318) input ::= cmdlist */ yytestcase(yyruleno==318);
- /* (319) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==319);
- /* (320) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=320);
- /* (321) ecmd ::= SEMI */ yytestcase(yyruleno==321);
- /* (322) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==322);
- /* (323) ecmd ::= explain cmdx */ yytestcase(yyruleno==323);
- /* (324) trans_opt ::= */ yytestcase(yyruleno==324);
- /* (325) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==325);
- /* (326) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==326);
- /* (327) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==327);
- /* (328) savepoint_opt ::= */ yytestcase(yyruleno==328);
- /* (329) cmd ::= create_table create_table_args */ yytestcase(yyruleno==329);
- /* (330) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==330);
- /* (331) columnlist ::= columnname carglist */ yytestcase(yyruleno==331);
- /* (332) nm ::= ID|INDEXED */ yytestcase(yyruleno==332);
- /* (333) nm ::= STRING */ yytestcase(yyruleno==333);
- /* (334) nm ::= JOIN_KW */ yytestcase(yyruleno==334);
- /* (335) typetoken ::= typename */ yytestcase(yyruleno==335);
- /* (336) typename ::= ID|STRING */ yytestcase(yyruleno==336);
- /* (337) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=337);
- /* (338) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=338);
- /* (339) carglist ::= carglist ccons */ yytestcase(yyruleno==339);
- /* (340) carglist ::= */ yytestcase(yyruleno==340);
- /* (341) ccons ::= NULL onconf */ yytestcase(yyruleno==341);
- /* (342) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==342);
- /* (343) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==343);
- /* (344) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=344);
- /* (345) tconscomma ::= */ yytestcase(yyruleno==345);
- /* (346) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=346);
- /* (347) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=347);
- /* (348) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=348);
- /* (349) oneselect ::= values */ yytestcase(yyruleno==349);
- /* (350) sclp ::= selcollist COMMA */ yytestcase(yyruleno==350);
- /* (351) as ::= ID|STRING */ yytestcase(yyruleno==351);
- /* (352) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=352);
- /* (353) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==353);
- /* (354) exprlist ::= nexprlist */ yytestcase(yyruleno==354);
- /* (355) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=355);
- /* (356) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=356);
- /* (357) nmnum ::= ON */ yytestcase(yyruleno==357);
- /* (358) nmnum ::= DELETE */ yytestcase(yyruleno==358);
- /* (359) nmnum ::= DEFAULT */ yytestcase(yyruleno==359);
- /* (360) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==360);
- /* (361) foreach_clause ::= */ yytestcase(yyruleno==361);
- /* (362) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==362);
- /* (363) trnm ::= nm */ yytestcase(yyruleno==363);
- /* (364) tridxby ::= */ yytestcase(yyruleno==364);
- /* (365) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==365);
- /* (366) database_kw_opt ::= */ yytestcase(yyruleno==366);
- /* (367) kwcolumn_opt ::= */ yytestcase(yyruleno==367);
- /* (368) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==368);
- /* (369) vtabarglist ::= vtabarg */ yytestcase(yyruleno==369);
- /* (370) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==370);
- /* (371) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==371);
- /* (372) anylist ::= */ yytestcase(yyruleno==372);
- /* (373) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==373);
- /* (374) anylist ::= anylist ANY */ yytestcase(yyruleno==374);
- /* (375) with ::= */ yytestcase(yyruleno==375);
+ /* (323) input ::= cmdlist */ yytestcase(yyruleno==323);
+ /* (324) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==324);
+ /* (325) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=325);
+ /* (326) ecmd ::= SEMI */ yytestcase(yyruleno==326);
+ /* (327) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==327);
+ /* (328) ecmd ::= explain cmdx */ yytestcase(yyruleno==328);
+ /* (329) trans_opt ::= */ yytestcase(yyruleno==329);
+ /* (330) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==330);
+ /* (331) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==331);
+ /* (332) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==332);
+ /* (333) savepoint_opt ::= */ yytestcase(yyruleno==333);
+ /* (334) cmd ::= create_table create_table_args */ yytestcase(yyruleno==334);
+ /* (335) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==335);
+ /* (336) columnlist ::= columnname carglist */ yytestcase(yyruleno==336);
+ /* (337) nm ::= ID|INDEXED */ yytestcase(yyruleno==337);
+ /* (338) nm ::= STRING */ yytestcase(yyruleno==338);
+ /* (339) nm ::= JOIN_KW */ yytestcase(yyruleno==339);
+ /* (340) typetoken ::= typename */ yytestcase(yyruleno==340);
+ /* (341) typename ::= ID|STRING */ yytestcase(yyruleno==341);
+ /* (342) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=342);
+ /* (343) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=343);
+ /* (344) carglist ::= carglist ccons */ yytestcase(yyruleno==344);
+ /* (345) carglist ::= */ yytestcase(yyruleno==345);
+ /* (346) ccons ::= NULL onconf */ yytestcase(yyruleno==346);
+ /* (347) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==347);
+ /* (348) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==348);
+ /* (349) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=349);
+ /* (350) tconscomma ::= */ yytestcase(yyruleno==350);
+ /* (351) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=351);
+ /* (352) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=352);
+ /* (353) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=353);
+ /* (354) oneselect ::= values */ yytestcase(yyruleno==354);
+ /* (355) sclp ::= selcollist COMMA */ yytestcase(yyruleno==355);
+ /* (356) as ::= ID|STRING */ yytestcase(yyruleno==356);
+ /* (357) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=357);
+ /* (358) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==358);
+ /* (359) exprlist ::= nexprlist */ yytestcase(yyruleno==359);
+ /* (360) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=360);
+ /* (361) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=361);
+ /* (362) nmnum ::= ON */ yytestcase(yyruleno==362);
+ /* (363) nmnum ::= DELETE */ yytestcase(yyruleno==363);
+ /* (364) nmnum ::= DEFAULT */ yytestcase(yyruleno==364);
+ /* (365) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==365);
+ /* (366) foreach_clause ::= */ yytestcase(yyruleno==366);
+ /* (367) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==367);
+ /* (368) trnm ::= nm */ yytestcase(yyruleno==368);
+ /* (369) tridxby ::= */ yytestcase(yyruleno==369);
+ /* (370) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==370);
+ /* (371) database_kw_opt ::= */ yytestcase(yyruleno==371);
+ /* (372) kwcolumn_opt ::= */ yytestcase(yyruleno==372);
+ /* (373) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==373);
+ /* (374) vtabarglist ::= vtabarg */ yytestcase(yyruleno==374);
+ /* (375) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==375);
+ /* (376) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==376);
+ /* (377) anylist ::= */ yytestcase(yyruleno==377);
+ /* (378) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==378);
+ /* (379) anylist ::= anylist ANY */ yytestcase(yyruleno==379);
+ /* (380) with ::= */ yytestcase(yyruleno==380);
break;
/********** End reduce actions ************************************************/
};
@@ -153597,9 +154629,8 @@ SQLITE_PRIVATE void sqlite3Parser(
*/
SQLITE_PRIVATE int sqlite3ParserFallback(int iToken){
#ifdef YYFALLBACK
- if( iToken<(int)(sizeof(yyFallback)/sizeof(yyFallback[0])) ){
- return yyFallback[iToken];
- }
+ assert( iToken<(int)(sizeof(yyFallback)/sizeof(yyFallback[0])) );
+ return yyFallback[iToken];
#else
(void)iToken;
#endif
@@ -153768,144 +154799,146 @@ const unsigned char ebcdicToAscii[] = {
** is substantially reduced. This is important for embedded applications
** on platforms with limited memory.
*/
-/* Hash score: 214 */
-/* zKWText[] encodes 950 bytes of keyword text in 629 bytes */
+/* Hash score: 221 */
+/* zKWText[] encodes 967 bytes of keyword text in 638 bytes */
/* REINDEXEDESCAPEACHECKEYBEFOREIGNOREGEXPLAINSTEADDATABASELECT */
-/* ABLEFTHENDEFERRABLELSEXCLUDELETEMPORARYCONSTRAINTERSECTIES */
-/* AVEPOINTOFFSETRANSACTIONATURALTERAISEXCEPTRIGGEREFERENCES */
-/* UNIQUERYWITHOUTERELEASEXCLUSIVEXISTSATTACHAVINGLOBEGINNERANGE */
-/* BETWEENOTHINGROUPSCASCADETACHCASECOLLATECREATECURRENT_DATE */
-/* IMMEDIATEJOINSERTLIKEMATCHPLANALYZEPRAGMABORTUPDATEVALUES */
-/* VIRTUALIMITWHENOTNULLWHERECURSIVEAFTERENAMEANDEFAULT */
+/* ABLEFTHENDEFERRABLELSEXCLUDELETEMPORARYISNULLSAVEPOINTERSECT */
+/* IESNOTNULLIKEXCEPTRANSACTIONATURALTERAISEXCLUSIVEXISTS */
+/* CONSTRAINTOFFSETRIGGEREFERENCESUNIQUERYWITHOUTERELEASEATTACH */
+/* AVINGLOBEGINNERANGEBETWEENOTHINGROUPSCASCADETACHCASECOLLATE */
+/* CREATECURRENT_DATEIMMEDIATEJOINSERTMATCHPLANALYZEPRAGMABORT */
+/* UPDATEVALUESVIRTUALASTWHENWHERECURSIVEAFTERENAMEANDEFAULT */
/* AUTOINCREMENTCASTCOLUMNCOMMITCONFLICTCROSSCURRENT_TIMESTAMP */
-/* ARTITIONDEFERREDISTINCTDROPRECEDINGFAILFILTEREPLACEFOLLOWING */
-/* FROMFULLIFISNULLORDERESTRICTOTHERSOVERIGHTROLLBACKROWS */
+/* ARTITIONDEFERREDISTINCTDROPRECEDINGFAILIMITFILTEREPLACEFIRST */
+/* FOLLOWINGFROMFULLIFORDERESTRICTOTHERSOVERIGHTROLLBACKROWS */
/* UNBOUNDEDUNIONUSINGVACUUMVIEWINDOWBYINITIALLYPRIMARY */
-static const char zKWText[628] = {
+static const char zKWText[637] = {
'R','E','I','N','D','E','X','E','D','E','S','C','A','P','E','A','C','H',
'E','C','K','E','Y','B','E','F','O','R','E','I','G','N','O','R','E','G',
'E','X','P','L','A','I','N','S','T','E','A','D','D','A','T','A','B','A',
'S','E','L','E','C','T','A','B','L','E','F','T','H','E','N','D','E','F',
'E','R','R','A','B','L','E','L','S','E','X','C','L','U','D','E','L','E',
- 'T','E','M','P','O','R','A','R','Y','C','O','N','S','T','R','A','I','N',
- 'T','E','R','S','E','C','T','I','E','S','A','V','E','P','O','I','N','T',
- 'O','F','F','S','E','T','R','A','N','S','A','C','T','I','O','N','A','T',
- 'U','R','A','L','T','E','R','A','I','S','E','X','C','E','P','T','R','I',
- 'G','G','E','R','E','F','E','R','E','N','C','E','S','U','N','I','Q','U',
- 'E','R','Y','W','I','T','H','O','U','T','E','R','E','L','E','A','S','E',
- 'X','C','L','U','S','I','V','E','X','I','S','T','S','A','T','T','A','C',
- 'H','A','V','I','N','G','L','O','B','E','G','I','N','N','E','R','A','N',
- 'G','E','B','E','T','W','E','E','N','O','T','H','I','N','G','R','O','U',
- 'P','S','C','A','S','C','A','D','E','T','A','C','H','C','A','S','E','C',
- 'O','L','L','A','T','E','C','R','E','A','T','E','C','U','R','R','E','N',
- 'T','_','D','A','T','E','I','M','M','E','D','I','A','T','E','J','O','I',
- 'N','S','E','R','T','L','I','K','E','M','A','T','C','H','P','L','A','N',
- 'A','L','Y','Z','E','P','R','A','G','M','A','B','O','R','T','U','P','D',
- 'A','T','E','V','A','L','U','E','S','V','I','R','T','U','A','L','I','M',
- 'I','T','W','H','E','N','O','T','N','U','L','L','W','H','E','R','E','C',
- 'U','R','S','I','V','E','A','F','T','E','R','E','N','A','M','E','A','N',
- 'D','E','F','A','U','L','T','A','U','T','O','I','N','C','R','E','M','E',
- 'N','T','C','A','S','T','C','O','L','U','M','N','C','O','M','M','I','T',
- 'C','O','N','F','L','I','C','T','C','R','O','S','S','C','U','R','R','E',
- 'N','T','_','T','I','M','E','S','T','A','M','P','A','R','T','I','T','I',
- 'O','N','D','E','F','E','R','R','E','D','I','S','T','I','N','C','T','D',
- 'R','O','P','R','E','C','E','D','I','N','G','F','A','I','L','F','I','L',
- 'T','E','R','E','P','L','A','C','E','F','O','L','L','O','W','I','N','G',
- 'F','R','O','M','F','U','L','L','I','F','I','S','N','U','L','L','O','R',
- 'D','E','R','E','S','T','R','I','C','T','O','T','H','E','R','S','O','V',
- 'E','R','I','G','H','T','R','O','L','L','B','A','C','K','R','O','W','S',
- 'U','N','B','O','U','N','D','E','D','U','N','I','O','N','U','S','I','N',
- 'G','V','A','C','U','U','M','V','I','E','W','I','N','D','O','W','B','Y',
- 'I','N','I','T','I','A','L','L','Y','P','R','I','M','A','R','Y',
+ 'T','E','M','P','O','R','A','R','Y','I','S','N','U','L','L','S','A','V',
+ 'E','P','O','I','N','T','E','R','S','E','C','T','I','E','S','N','O','T',
+ 'N','U','L','L','I','K','E','X','C','E','P','T','R','A','N','S','A','C',
+ 'T','I','O','N','A','T','U','R','A','L','T','E','R','A','I','S','E','X',
+ 'C','L','U','S','I','V','E','X','I','S','T','S','C','O','N','S','T','R',
+ 'A','I','N','T','O','F','F','S','E','T','R','I','G','G','E','R','E','F',
+ 'E','R','E','N','C','E','S','U','N','I','Q','U','E','R','Y','W','I','T',
+ 'H','O','U','T','E','R','E','L','E','A','S','E','A','T','T','A','C','H',
+ 'A','V','I','N','G','L','O','B','E','G','I','N','N','E','R','A','N','G',
+ 'E','B','E','T','W','E','E','N','O','T','H','I','N','G','R','O','U','P',
+ 'S','C','A','S','C','A','D','E','T','A','C','H','C','A','S','E','C','O',
+ 'L','L','A','T','E','C','R','E','A','T','E','C','U','R','R','E','N','T',
+ '_','D','A','T','E','I','M','M','E','D','I','A','T','E','J','O','I','N',
+ 'S','E','R','T','M','A','T','C','H','P','L','A','N','A','L','Y','Z','E',
+ 'P','R','A','G','M','A','B','O','R','T','U','P','D','A','T','E','V','A',
+ 'L','U','E','S','V','I','R','T','U','A','L','A','S','T','W','H','E','N',
+ 'W','H','E','R','E','C','U','R','S','I','V','E','A','F','T','E','R','E',
+ 'N','A','M','E','A','N','D','E','F','A','U','L','T','A','U','T','O','I',
+ 'N','C','R','E','M','E','N','T','C','A','S','T','C','O','L','U','M','N',
+ 'C','O','M','M','I','T','C','O','N','F','L','I','C','T','C','R','O','S',
+ 'S','C','U','R','R','E','N','T','_','T','I','M','E','S','T','A','M','P',
+ 'A','R','T','I','T','I','O','N','D','E','F','E','R','R','E','D','I','S',
+ 'T','I','N','C','T','D','R','O','P','R','E','C','E','D','I','N','G','F',
+ 'A','I','L','I','M','I','T','F','I','L','T','E','R','E','P','L','A','C',
+ 'E','F','I','R','S','T','F','O','L','L','O','W','I','N','G','F','R','O',
+ 'M','F','U','L','L','I','F','O','R','D','E','R','E','S','T','R','I','C',
+ 'T','O','T','H','E','R','S','O','V','E','R','I','G','H','T','R','O','L',
+ 'L','B','A','C','K','R','O','W','S','U','N','B','O','U','N','D','E','D',
+ 'U','N','I','O','N','U','S','I','N','G','V','A','C','U','U','M','V','I',
+ 'E','W','I','N','D','O','W','B','Y','I','N','I','T','I','A','L','L','Y',
+ 'P','R','I','M','A','R','Y',
};
/* aKWHash[i] is the hash value for the i-th keyword */
static const unsigned char aKWHash[127] = {
- 75, 111, 127, 73, 108, 29, 0, 0, 83, 0, 77, 63, 0,
- 37, 33, 78, 15, 0, 126, 86, 57, 120, 128, 19, 0, 0,
- 133, 0, 131, 123, 0, 22, 98, 0, 9, 0, 0, 117, 71,
- 0, 69, 6, 0, 49, 95, 140, 0, 129, 106, 0, 0, 54,
- 0, 109, 24, 0, 17, 0, 134, 56, 23, 26, 5, 58, 135,
- 101, 0, 0, 139, 112, 62, 138, 59, 115, 65, 0, 96, 0,
- 105, 45, 0, 104, 0, 0, 0, 100, 97, 102, 107, 119, 14,
- 31, 118, 0, 81, 0, 136, 116, 137, 61, 124, 132, 80, 121,
- 88, 30, 85, 0, 0, 99, 35, 125, 122, 0, 130, 0, 0,
- 41, 0, 91, 89, 90, 0, 20, 87, 113, 82,
+ 82, 113, 130, 80, 110, 29, 0, 0, 89, 0, 83, 70, 0,
+ 53, 35, 84, 15, 0, 129, 92, 64, 124, 131, 19, 0, 0,
+ 136, 0, 134, 126, 0, 22, 100, 0, 9, 0, 0, 121, 78,
+ 0, 76, 6, 0, 58, 97, 143, 0, 132, 108, 0, 0, 48,
+ 0, 111, 24, 0, 17, 0, 137, 63, 23, 26, 5, 65, 138,
+ 103, 120, 0, 142, 114, 69, 141, 66, 118, 72, 0, 98, 0,
+ 107, 41, 0, 106, 0, 0, 0, 102, 99, 104, 109, 123, 14,
+ 50, 122, 0, 87, 0, 139, 119, 140, 68, 127, 135, 86, 81,
+ 37, 91, 117, 0, 0, 101, 51, 128, 125, 0, 133, 0, 0,
+ 44, 0, 93, 67, 39, 0, 20, 45, 115, 88,
};
/* aKWNext[] forms the hash collision chain. If aKWHash[i]==0
** then the i-th keyword has no more hash collisions. Otherwise,
** the next keyword with the same hash is aKWHash[i]-1. */
-static const unsigned char aKWNext[140] = {
+static const unsigned char aKWNext[143] = {
0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0,
0, 2, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0,
- 0, 0, 0, 21, 0, 0, 12, 0, 0, 0, 0, 0, 0,
- 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 51, 28, 0, 0, 38, 0, 0, 0, 44, 0, 0, 0, 3,
- 0, 0, 67, 1, 66, 0, 0, 0, 36, 0, 47, 0, 0,
- 0, 0, 0, 48, 50, 76, 0, 0, 42, 0, 60, 0, 0,
- 0, 43, 0, 16, 55, 10, 0, 0, 0, 0, 0, 0, 0,
- 11, 72, 93, 0, 0, 8, 0, 110, 0, 103, 40, 53, 70,
- 0, 114, 0, 74, 52, 0, 0, 92, 39, 46, 0, 68, 32,
- 84, 0, 34, 27, 25, 18, 94, 0, 64, 79,
+ 0, 0, 0, 21, 0, 0, 0, 0, 12, 0, 0, 0, 0,
+ 0, 0, 0, 7, 0, 36, 0, 0, 28, 0, 0, 0, 31,
+ 0, 0, 0, 40, 0, 0, 0, 0, 0, 60, 0, 54, 0,
+ 0, 38, 47, 0, 0, 0, 3, 0, 0, 74, 1, 73, 0,
+ 0, 0, 52, 0, 0, 0, 0, 0, 0, 57, 59, 56, 30,
+ 0, 0, 0, 46, 0, 16, 49, 10, 0, 0, 0, 0, 0,
+ 0, 0, 11, 79, 95, 0, 0, 8, 0, 112, 0, 105, 0,
+ 43, 62, 0, 77, 0, 116, 0, 61, 0, 0, 94, 42, 55,
+ 0, 75, 34, 90, 32, 33, 27, 25, 18, 96, 0, 71, 85,
};
/* aKWLen[i] is the length (in bytes) of the i-th keyword */
-static const unsigned char aKWLen[140] = {
+static const unsigned char aKWLen[143] = {
7, 7, 5, 4, 6, 4, 5, 3, 6, 7, 3, 6, 6,
7, 7, 3, 8, 2, 6, 5, 4, 4, 3, 10, 4, 7,
- 6, 9, 4, 2, 10, 9, 4, 9, 4, 6, 2, 3, 11,
- 6, 2, 7, 5, 5, 6, 7, 10, 6, 5, 7, 4, 5,
- 7, 9, 6, 6, 6, 4, 5, 5, 5, 7, 7, 6, 5,
- 7, 3, 6, 4, 7, 6, 12, 9, 4, 6, 4, 5, 4,
- 7, 6, 5, 6, 6, 7, 5, 4, 7, 3, 2, 4, 5,
- 9, 5, 6, 3, 7, 13, 2, 2, 4, 6, 6, 8, 5,
- 17, 12, 7, 9, 8, 8, 2, 4, 9, 4, 6, 7, 9,
- 4, 4, 2, 6, 5, 8, 6, 4, 5, 8, 4, 3, 9,
- 5, 5, 6, 4, 6, 2, 2, 9, 3, 7,
+ 6, 9, 4, 2, 6, 5, 9, 9, 4, 7, 3, 2, 4,
+ 4, 6, 11, 6, 2, 7, 5, 5, 9, 6, 10, 4, 6,
+ 2, 3, 7, 10, 6, 5, 7, 4, 5, 7, 6, 6, 4,
+ 5, 5, 5, 7, 7, 6, 5, 7, 3, 6, 4, 7, 6,
+ 12, 9, 4, 6, 5, 4, 7, 6, 5, 6, 6, 7, 4,
+ 4, 5, 9, 5, 6, 3, 7, 13, 2, 2, 4, 6, 6,
+ 8, 5, 17, 12, 7, 9, 8, 8, 2, 4, 9, 4, 5,
+ 6, 7, 5, 9, 4, 4, 2, 5, 8, 6, 4, 5, 8,
+ 4, 3, 9, 5, 5, 6, 4, 6, 2, 2, 9, 3, 7,
};
/* aKWOffset[i] is the index into zKWText[] of the start of
** the text for the i-th keyword. */
-static const unsigned short int aKWOffset[140] = {
+static const unsigned short int aKWOffset[143] = {
0, 2, 2, 8, 9, 14, 16, 20, 23, 25, 25, 29, 33,
36, 41, 46, 48, 53, 54, 59, 62, 65, 67, 69, 78, 81,
- 86, 90, 90, 94, 99, 106, 114, 117, 123, 126, 126, 129, 131,
- 136, 140, 141, 146, 150, 154, 159, 165, 175, 178, 183, 183, 187,
- 191, 197, 205, 211, 216, 221, 224, 227, 231, 236, 242, 248, 248,
- 254, 255, 259, 265, 269, 276, 282, 294, 303, 305, 311, 315, 320,
- 322, 329, 334, 339, 345, 351, 357, 362, 365, 365, 365, 368, 372,
- 375, 384, 388, 394, 396, 403, 405, 407, 416, 420, 426, 432, 440,
- 445, 445, 445, 461, 470, 477, 478, 485, 488, 497, 501, 506, 513,
- 522, 526, 530, 532, 538, 542, 550, 556, 559, 564, 572, 572, 576,
- 585, 590, 595, 601, 604, 607, 610, 612, 617, 621,
+ 86, 90, 90, 94, 99, 101, 105, 111, 119, 123, 123, 123, 126,
+ 129, 132, 137, 142, 146, 147, 152, 156, 160, 168, 174, 181, 184,
+ 184, 187, 189, 195, 205, 208, 213, 213, 217, 221, 228, 233, 238,
+ 241, 244, 248, 253, 259, 265, 265, 271, 272, 276, 282, 286, 293,
+ 299, 311, 320, 322, 328, 333, 335, 342, 347, 352, 358, 364, 370,
+ 374, 378, 381, 390, 394, 400, 402, 409, 411, 413, 422, 426, 432,
+ 438, 446, 451, 451, 451, 467, 476, 483, 484, 491, 494, 503, 506,
+ 511, 516, 523, 528, 537, 541, 545, 547, 551, 559, 565, 568, 573,
+ 581, 581, 585, 594, 599, 604, 610, 613, 616, 619, 621, 626, 630,
};
/* aKWCode[i] is the parser symbol code for the i-th keyword */
-static const unsigned char aKWCode[140] = {
+static const unsigned char aKWCode[143] = {
TK_REINDEX, TK_INDEXED, TK_INDEX, TK_DESC, TK_ESCAPE,
TK_EACH, TK_CHECK, TK_KEY, TK_BEFORE, TK_FOREIGN,
TK_FOR, TK_IGNORE, TK_LIKE_KW, TK_EXPLAIN, TK_INSTEAD,
TK_ADD, TK_DATABASE, TK_AS, TK_SELECT, TK_TABLE,
TK_JOIN_KW, TK_THEN, TK_END, TK_DEFERRABLE, TK_ELSE,
TK_EXCLUDE, TK_DELETE, TK_TEMP, TK_TEMP, TK_OR,
- TK_CONSTRAINT, TK_INTERSECT, TK_TIES, TK_SAVEPOINT, TK_INTO,
- TK_OFFSET, TK_OF, TK_SET, TK_TRANSACTION,TK_ACTION,
- TK_ON, TK_JOIN_KW, TK_ALTER, TK_RAISE, TK_EXCEPT,
- TK_TRIGGER, TK_REFERENCES, TK_UNIQUE, TK_QUERY, TK_WITHOUT,
- TK_WITH, TK_JOIN_KW, TK_RELEASE, TK_EXCLUSIVE, TK_EXISTS,
- TK_ATTACH, TK_HAVING, TK_LIKE_KW, TK_BEGIN, TK_JOIN_KW,
- TK_RANGE, TK_BETWEEN, TK_NOTHING, TK_GROUPS, TK_GROUP,
- TK_CASCADE, TK_ASC, TK_DETACH, TK_CASE, TK_COLLATE,
- TK_CREATE, TK_CTIME_KW, TK_IMMEDIATE, TK_JOIN, TK_INSERT,
- TK_LIKE_KW, TK_MATCH, TK_PLAN, TK_ANALYZE, TK_PRAGMA,
- TK_ABORT, TK_UPDATE, TK_VALUES, TK_VIRTUAL, TK_LIMIT,
- TK_WHEN, TK_NOTNULL, TK_NOT, TK_NO, TK_NULL,
- TK_WHERE, TK_RECURSIVE, TK_AFTER, TK_RENAME, TK_AND,
- TK_DEFAULT, TK_AUTOINCR, TK_TO, TK_IN, TK_CAST,
- TK_COLUMNKW, TK_COMMIT, TK_CONFLICT, TK_JOIN_KW, TK_CTIME_KW,
- TK_CTIME_KW, TK_CURRENT, TK_PARTITION, TK_DEFERRED, TK_DISTINCT,
- TK_IS, TK_DROP, TK_PRECEDING, TK_FAIL, TK_FILTER,
- TK_REPLACE, TK_FOLLOWING, TK_FROM, TK_JOIN_KW, TK_IF,
- TK_ISNULL, TK_ORDER, TK_RESTRICT, TK_OTHERS, TK_OVER,
- TK_JOIN_KW, TK_ROLLBACK, TK_ROWS, TK_ROW, TK_UNBOUNDED,
- TK_UNION, TK_USING, TK_VACUUM, TK_VIEW, TK_WINDOW,
- TK_DO, TK_BY, TK_INITIALLY, TK_ALL, TK_PRIMARY,
+ TK_ISNULL, TK_NULLS, TK_SAVEPOINT, TK_INTERSECT, TK_TIES,
+ TK_NOTNULL, TK_NOT, TK_NO, TK_NULL, TK_LIKE_KW,
+ TK_EXCEPT, TK_TRANSACTION,TK_ACTION, TK_ON, TK_JOIN_KW,
+ TK_ALTER, TK_RAISE, TK_EXCLUSIVE, TK_EXISTS, TK_CONSTRAINT,
+ TK_INTO, TK_OFFSET, TK_OF, TK_SET, TK_TRIGGER,
+ TK_REFERENCES, TK_UNIQUE, TK_QUERY, TK_WITHOUT, TK_WITH,
+ TK_JOIN_KW, TK_RELEASE, TK_ATTACH, TK_HAVING, TK_LIKE_KW,
+ TK_BEGIN, TK_JOIN_KW, TK_RANGE, TK_BETWEEN, TK_NOTHING,
+ TK_GROUPS, TK_GROUP, TK_CASCADE, TK_ASC, TK_DETACH,
+ TK_CASE, TK_COLLATE, TK_CREATE, TK_CTIME_KW, TK_IMMEDIATE,
+ TK_JOIN, TK_INSERT, TK_MATCH, TK_PLAN, TK_ANALYZE,
+ TK_PRAGMA, TK_ABORT, TK_UPDATE, TK_VALUES, TK_VIRTUAL,
+ TK_LAST, TK_WHEN, TK_WHERE, TK_RECURSIVE, TK_AFTER,
+ TK_RENAME, TK_AND, TK_DEFAULT, TK_AUTOINCR, TK_TO,
+ TK_IN, TK_CAST, TK_COLUMNKW, TK_COMMIT, TK_CONFLICT,
+ TK_JOIN_KW, TK_CTIME_KW, TK_CTIME_KW, TK_CURRENT, TK_PARTITION,
+ TK_DEFERRED, TK_DISTINCT, TK_IS, TK_DROP, TK_PRECEDING,
+ TK_FAIL, TK_LIMIT, TK_FILTER, TK_REPLACE, TK_FIRST,
+ TK_FOLLOWING, TK_FROM, TK_JOIN_KW, TK_IF, TK_ORDER,
+ TK_RESTRICT, TK_OTHERS, TK_OVER, TK_JOIN_KW, TK_ROLLBACK,
+ TK_ROWS, TK_ROW, TK_UNBOUNDED, TK_UNION, TK_USING,
+ TK_VACUUM, TK_VIEW, TK_WINDOW, TK_DO, TK_BY,
+ TK_INITIALLY, TK_ALL, TK_PRIMARY,
};
/* Check to see if z[0..n-1] is a keyword. If it is, write the
** parser symbol code for that keyword into *pType. Always
@@ -153956,116 +154989,119 @@ static int keywordCode(const char *z, int n, int *pType){
testcase( i==27 ); /* TEMPORARY */
testcase( i==28 ); /* TEMP */
testcase( i==29 ); /* OR */
- testcase( i==30 ); /* CONSTRAINT */
- testcase( i==31 ); /* INTERSECT */
- testcase( i==32 ); /* TIES */
- testcase( i==33 ); /* SAVEPOINT */
- testcase( i==34 ); /* INTO */
- testcase( i==35 ); /* OFFSET */
- testcase( i==36 ); /* OF */
- testcase( i==37 ); /* SET */
- testcase( i==38 ); /* TRANSACTION */
- testcase( i==39 ); /* ACTION */
- testcase( i==40 ); /* ON */
- testcase( i==41 ); /* NATURAL */
- testcase( i==42 ); /* ALTER */
- testcase( i==43 ); /* RAISE */
- testcase( i==44 ); /* EXCEPT */
- testcase( i==45 ); /* TRIGGER */
- testcase( i==46 ); /* REFERENCES */
- testcase( i==47 ); /* UNIQUE */
- testcase( i==48 ); /* QUERY */
- testcase( i==49 ); /* WITHOUT */
- testcase( i==50 ); /* WITH */
- testcase( i==51 ); /* OUTER */
- testcase( i==52 ); /* RELEASE */
- testcase( i==53 ); /* EXCLUSIVE */
- testcase( i==54 ); /* EXISTS */
- testcase( i==55 ); /* ATTACH */
- testcase( i==56 ); /* HAVING */
- testcase( i==57 ); /* GLOB */
- testcase( i==58 ); /* BEGIN */
- testcase( i==59 ); /* INNER */
- testcase( i==60 ); /* RANGE */
- testcase( i==61 ); /* BETWEEN */
- testcase( i==62 ); /* NOTHING */
- testcase( i==63 ); /* GROUPS */
- testcase( i==64 ); /* GROUP */
- testcase( i==65 ); /* CASCADE */
- testcase( i==66 ); /* ASC */
- testcase( i==67 ); /* DETACH */
- testcase( i==68 ); /* CASE */
- testcase( i==69 ); /* COLLATE */
- testcase( i==70 ); /* CREATE */
- testcase( i==71 ); /* CURRENT_DATE */
- testcase( i==72 ); /* IMMEDIATE */
- testcase( i==73 ); /* JOIN */
- testcase( i==74 ); /* INSERT */
- testcase( i==75 ); /* LIKE */
- testcase( i==76 ); /* MATCH */
- testcase( i==77 ); /* PLAN */
- testcase( i==78 ); /* ANALYZE */
- testcase( i==79 ); /* PRAGMA */
- testcase( i==80 ); /* ABORT */
- testcase( i==81 ); /* UPDATE */
- testcase( i==82 ); /* VALUES */
- testcase( i==83 ); /* VIRTUAL */
- testcase( i==84 ); /* LIMIT */
- testcase( i==85 ); /* WHEN */
- testcase( i==86 ); /* NOTNULL */
- testcase( i==87 ); /* NOT */
- testcase( i==88 ); /* NO */
- testcase( i==89 ); /* NULL */
- testcase( i==90 ); /* WHERE */
- testcase( i==91 ); /* RECURSIVE */
- testcase( i==92 ); /* AFTER */
- testcase( i==93 ); /* RENAME */
- testcase( i==94 ); /* AND */
- testcase( i==95 ); /* DEFAULT */
- testcase( i==96 ); /* AUTOINCREMENT */
- testcase( i==97 ); /* TO */
- testcase( i==98 ); /* IN */
- testcase( i==99 ); /* CAST */
- testcase( i==100 ); /* COLUMN */
- testcase( i==101 ); /* COMMIT */
- testcase( i==102 ); /* CONFLICT */
- testcase( i==103 ); /* CROSS */
- testcase( i==104 ); /* CURRENT_TIMESTAMP */
- testcase( i==105 ); /* CURRENT_TIME */
- testcase( i==106 ); /* CURRENT */
- testcase( i==107 ); /* PARTITION */
- testcase( i==108 ); /* DEFERRED */
- testcase( i==109 ); /* DISTINCT */
- testcase( i==110 ); /* IS */
- testcase( i==111 ); /* DROP */
- testcase( i==112 ); /* PRECEDING */
- testcase( i==113 ); /* FAIL */
- testcase( i==114 ); /* FILTER */
- testcase( i==115 ); /* REPLACE */
- testcase( i==116 ); /* FOLLOWING */
- testcase( i==117 ); /* FROM */
- testcase( i==118 ); /* FULL */
- testcase( i==119 ); /* IF */
- testcase( i==120 ); /* ISNULL */
- testcase( i==121 ); /* ORDER */
- testcase( i==122 ); /* RESTRICT */
- testcase( i==123 ); /* OTHERS */
- testcase( i==124 ); /* OVER */
- testcase( i==125 ); /* RIGHT */
- testcase( i==126 ); /* ROLLBACK */
- testcase( i==127 ); /* ROWS */
- testcase( i==128 ); /* ROW */
- testcase( i==129 ); /* UNBOUNDED */
- testcase( i==130 ); /* UNION */
- testcase( i==131 ); /* USING */
- testcase( i==132 ); /* VACUUM */
- testcase( i==133 ); /* VIEW */
- testcase( i==134 ); /* WINDOW */
- testcase( i==135 ); /* DO */
- testcase( i==136 ); /* BY */
- testcase( i==137 ); /* INITIALLY */
- testcase( i==138 ); /* ALL */
- testcase( i==139 ); /* PRIMARY */
+ testcase( i==30 ); /* ISNULL */
+ testcase( i==31 ); /* NULLS */
+ testcase( i==32 ); /* SAVEPOINT */
+ testcase( i==33 ); /* INTERSECT */
+ testcase( i==34 ); /* TIES */
+ testcase( i==35 ); /* NOTNULL */
+ testcase( i==36 ); /* NOT */
+ testcase( i==37 ); /* NO */
+ testcase( i==38 ); /* NULL */
+ testcase( i==39 ); /* LIKE */
+ testcase( i==40 ); /* EXCEPT */
+ testcase( i==41 ); /* TRANSACTION */
+ testcase( i==42 ); /* ACTION */
+ testcase( i==43 ); /* ON */
+ testcase( i==44 ); /* NATURAL */
+ testcase( i==45 ); /* ALTER */
+ testcase( i==46 ); /* RAISE */
+ testcase( i==47 ); /* EXCLUSIVE */
+ testcase( i==48 ); /* EXISTS */
+ testcase( i==49 ); /* CONSTRAINT */
+ testcase( i==50 ); /* INTO */
+ testcase( i==51 ); /* OFFSET */
+ testcase( i==52 ); /* OF */
+ testcase( i==53 ); /* SET */
+ testcase( i==54 ); /* TRIGGER */
+ testcase( i==55 ); /* REFERENCES */
+ testcase( i==56 ); /* UNIQUE */
+ testcase( i==57 ); /* QUERY */
+ testcase( i==58 ); /* WITHOUT */
+ testcase( i==59 ); /* WITH */
+ testcase( i==60 ); /* OUTER */
+ testcase( i==61 ); /* RELEASE */
+ testcase( i==62 ); /* ATTACH */
+ testcase( i==63 ); /* HAVING */
+ testcase( i==64 ); /* GLOB */
+ testcase( i==65 ); /* BEGIN */
+ testcase( i==66 ); /* INNER */
+ testcase( i==67 ); /* RANGE */
+ testcase( i==68 ); /* BETWEEN */
+ testcase( i==69 ); /* NOTHING */
+ testcase( i==70 ); /* GROUPS */
+ testcase( i==71 ); /* GROUP */
+ testcase( i==72 ); /* CASCADE */
+ testcase( i==73 ); /* ASC */
+ testcase( i==74 ); /* DETACH */
+ testcase( i==75 ); /* CASE */
+ testcase( i==76 ); /* COLLATE */
+ testcase( i==77 ); /* CREATE */
+ testcase( i==78 ); /* CURRENT_DATE */
+ testcase( i==79 ); /* IMMEDIATE */
+ testcase( i==80 ); /* JOIN */
+ testcase( i==81 ); /* INSERT */
+ testcase( i==82 ); /* MATCH */
+ testcase( i==83 ); /* PLAN */
+ testcase( i==84 ); /* ANALYZE */
+ testcase( i==85 ); /* PRAGMA */
+ testcase( i==86 ); /* ABORT */
+ testcase( i==87 ); /* UPDATE */
+ testcase( i==88 ); /* VALUES */
+ testcase( i==89 ); /* VIRTUAL */
+ testcase( i==90 ); /* LAST */
+ testcase( i==91 ); /* WHEN */
+ testcase( i==92 ); /* WHERE */
+ testcase( i==93 ); /* RECURSIVE */
+ testcase( i==94 ); /* AFTER */
+ testcase( i==95 ); /* RENAME */
+ testcase( i==96 ); /* AND */
+ testcase( i==97 ); /* DEFAULT */
+ testcase( i==98 ); /* AUTOINCREMENT */
+ testcase( i==99 ); /* TO */
+ testcase( i==100 ); /* IN */
+ testcase( i==101 ); /* CAST */
+ testcase( i==102 ); /* COLUMN */
+ testcase( i==103 ); /* COMMIT */
+ testcase( i==104 ); /* CONFLICT */
+ testcase( i==105 ); /* CROSS */
+ testcase( i==106 ); /* CURRENT_TIMESTAMP */
+ testcase( i==107 ); /* CURRENT_TIME */
+ testcase( i==108 ); /* CURRENT */
+ testcase( i==109 ); /* PARTITION */
+ testcase( i==110 ); /* DEFERRED */
+ testcase( i==111 ); /* DISTINCT */
+ testcase( i==112 ); /* IS */
+ testcase( i==113 ); /* DROP */
+ testcase( i==114 ); /* PRECEDING */
+ testcase( i==115 ); /* FAIL */
+ testcase( i==116 ); /* LIMIT */
+ testcase( i==117 ); /* FILTER */
+ testcase( i==118 ); /* REPLACE */
+ testcase( i==119 ); /* FIRST */
+ testcase( i==120 ); /* FOLLOWING */
+ testcase( i==121 ); /* FROM */
+ testcase( i==122 ); /* FULL */
+ testcase( i==123 ); /* IF */
+ testcase( i==124 ); /* ORDER */
+ testcase( i==125 ); /* RESTRICT */
+ testcase( i==126 ); /* OTHERS */
+ testcase( i==127 ); /* OVER */
+ testcase( i==128 ); /* RIGHT */
+ testcase( i==129 ); /* ROLLBACK */
+ testcase( i==130 ); /* ROWS */
+ testcase( i==131 ); /* ROW */
+ testcase( i==132 ); /* UNBOUNDED */
+ testcase( i==133 ); /* UNION */
+ testcase( i==134 ); /* USING */
+ testcase( i==135 ); /* VACUUM */
+ testcase( i==136 ); /* VIEW */
+ testcase( i==137 ); /* WINDOW */
+ testcase( i==138 ); /* DO */
+ testcase( i==139 ); /* BY */
+ testcase( i==140 ); /* INITIALLY */
+ testcase( i==141 ); /* ALL */
+ testcase( i==142 ); /* PRIMARY */
*pType = aKWCode[i];
break;
}
@@ -154077,7 +155113,7 @@ SQLITE_PRIVATE int sqlite3KeywordCode(const unsigned char *z, int n){
keywordCode((char*)z, n, &id);
return id;
}
-#define SQLITE_N_KEYWORD 140
+#define SQLITE_N_KEYWORD 143
SQLITE_API int sqlite3_keyword_name(int i,const char **pzName,int *pnName){
if( i<0 || i>=SQLITE_N_KEYWORD ) return SQLITE_ERROR;
*pzName = zKWText + aKWOffset[i];
@@ -156038,6 +157074,7 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){
} aFlagOp[] = {
{ SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_ForeignKeys },
{ SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger },
+ { SQLITE_DBCONFIG_ENABLE_VIEW, SQLITE_EnableView },
{ SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, SQLITE_Fts3Tokenizer },
{ SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, SQLITE_LoadExtension },
{ SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE, SQLITE_NoCkptOnClose },
@@ -156437,11 +157474,8 @@ SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){
#ifndef SQLITE_OMIT_VIRTUALTABLE
for(i=sqliteHashFirst(&db->aModule); i; i=sqliteHashNext(i)){
Module *pMod = (Module *)sqliteHashData(i);
- if( pMod->xDestroy ){
- pMod->xDestroy(pMod->pAux);
- }
sqlite3VtabEponymousTableClear(db, pMod);
- sqlite3DbFree(db, pMod);
+ sqlite3VtabModuleUnref(db, pMod);
}
sqlite3HashClear(&db->aModule);
#endif
@@ -156922,7 +157956,8 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
}
assert( SQLITE_FUNC_CONSTANT==SQLITE_DETERMINISTIC );
- extraFlags = enc & SQLITE_DETERMINISTIC;
+ assert( SQLITE_FUNC_DIRECT==SQLITE_DIRECTONLY );
+ extraFlags = enc & (SQLITE_DETERMINISTIC|SQLITE_DIRECTONLY|SQLITE_SUBTYPE);
enc &= (SQLITE_FUNC_ENCMASK|SQLITE_ANY);
#ifndef SQLITE_OMIT_UTF16
@@ -156985,6 +158020,7 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
p->u.pDestructor = pDestructor;
p->funcFlags = (p->funcFlags & SQLITE_FUNC_ENCMASK) | extraFlags;
testcase( p->funcFlags & SQLITE_DETERMINISTIC );
+ testcase( p->funcFlags & SQLITE_DIRECTONLY );
p->xSFunc = xSFunc ? xSFunc : xStep;
p->xFinalize = xFinal;
p->xValue = xValue;
@@ -158277,6 +159313,7 @@ static int openDatabase(
db->nMaxSorterMmap = 0x7FFFFFFF;
db->flags |= SQLITE_ShortColNames
| SQLITE_EnableTrigger
+ | SQLITE_EnableView
| SQLITE_CacheSpill
/* The SQLITE_DQS compile-time option determines the default settings
@@ -159026,12 +160063,33 @@ SQLITE_API int sqlite3_test_control(int op, ...){
break;
}
- /*
- ** Reset the PRNG back to its uninitialized state. The next call
- ** to sqlite3_randomness() will reseed the PRNG using a single call
- ** to the xRandomness method of the default VFS.
+ /* sqlite3_test_control(SQLITE_TESTCTRL_PRNG_SEED, int x, sqlite3 *db);
+ **
+ ** Control the seed for the pseudo-random number generator (PRNG) that
+ ** is built into SQLite. Cases:
+ **
+ ** x!=0 && db!=0 Seed the PRNG to the current value of the
+ ** schema cookie in the main database for db, or
+ ** x if the schema cookie is zero. This case
+ ** is convenient to use with database fuzzers
+ ** as it allows the fuzzer some control over the
+ ** the PRNG seed.
+ **
+ ** x!=0 && db==0 Seed the PRNG to the value of x.
+ **
+ ** x==0 && db==0 Revert to default behavior of using the
+ ** xRandomness method on the primary VFS.
+ **
+ ** This test-control also resets the PRNG so that the new seed will
+ ** be used for the next call to sqlite3_randomness().
*/
- case SQLITE_TESTCTRL_PRNG_RESET: {
+ case SQLITE_TESTCTRL_PRNG_SEED: {
+ int x = va_arg(ap, int);
+ int y;
+ sqlite3 *db = va_arg(ap, sqlite3*);
+ assert( db==0 || db->aDb[0].pSchema!=0 );
+ if( db && (y = db->aDb[0].pSchema->schema_cookie)!=0 ){ x = y; }
+ sqlite3Config.iPrngSeed = x;
sqlite3_randomness(0,0);
break;
}
@@ -159244,6 +160302,17 @@ SQLITE_API int sqlite3_test_control(int op, ...){
break;
}
+ /* sqlite3_test_control(SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS, int);
+ **
+ ** Set or clear a flag that causes SQLite to verify that type, name,
+ ** and tbl_name fields of the sqlite_master table. This is normally
+ ** on, but it is sometimes useful to turn it off for testing.
+ */
+ case SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS: {
+ sqlite3GlobalConfig.bExtraSchemaChecks = va_arg(ap, int);
+ break;
+ }
+
/* Set the threshold at which OP_Once counters reset back to zero.
** By default this is 0x7ffffffe (over 2 billion), but that value is
** too big to test in a reasonable amount of time, so this control is
@@ -161190,6 +162259,18 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int);
SQLITE_EXTENSION_INIT1
#endif
+/*
+** The following are copied from sqliteInt.h.
+**
+** Constants for the largest and smallest possible 64-bit signed integers.
+** These macros are designed to work correctly on both 32-bit and 64-bit
+** compilers.
+*/
+#ifndef SQLITE_AMALGAMATION
+# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
+# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
+#endif
+
static int fts3EvalNext(Fts3Cursor *pCsr);
static int fts3EvalStart(Fts3Cursor *pCsr);
static int fts3TermSegReaderCursor(
@@ -162968,10 +164049,11 @@ static void fts3ColumnlistCopy(char **pp, char **ppPoslist){
}
/*
-** Value used to signify the end of an position-list. This is safe because
-** it is not possible to have a document with 2^31 terms.
+** Value used to signify the end of an position-list. This must be
+** as large or larger than any value that might appear on the
+** position-list, even a position list that has been corrupted.
*/
-#define POSITION_LIST_END 0x7fffffff
+#define POSITION_LIST_END LARGEST_INT64
/*
** This function is used to help parse position-lists. When this function is
@@ -163047,14 +164129,14 @@ static int fts3PoslistMerge(
fts3GetVarint32(&p1[1], &iCol1);
if( iCol1==0 ) return FTS_CORRUPT_VTAB;
}
- else if( *p1==POS_END ) iCol1 = POSITION_LIST_END;
+ else if( *p1==POS_END ) iCol1 = 0x7fffffff;
else iCol1 = 0;
if( *p2==POS_COLUMN ){
fts3GetVarint32(&p2[1], &iCol2);
if( iCol2==0 ) return FTS_CORRUPT_VTAB;
}
- else if( *p2==POS_END ) iCol2 = POSITION_LIST_END;
+ else if( *p2==POS_END ) iCol2 = 0x7fffffff;
else iCol2 = 0;
if( iCol1==iCol2 ){
@@ -163356,7 +164438,8 @@ static void fts3PutDeltaVarint3(
iWrite = *piPrev - iVal;
}
assert( *pbFirst || *piPrev==0 );
- assert( *pbFirst==0 || iWrite>0 );
+ assert_fts3_nc( *pbFirst==0 || iWrite>0 );
+ assert( *pbFirst==0 || iWrite>=0 );
*pp += sqlite3Fts3PutVarint(*pp, iWrite);
*piPrev = iVal;
*pbFirst = 1;
@@ -163462,6 +164545,8 @@ static int fts3DoclistOrMerge(
fts3PoslistCopy(&p, &p2);
fts3GetDeltaVarint3(&p2, pEnd2, bDescDoclist, &i2);
}
+
+ assert( (p-aOut)<=((p1?(p1-a1):n1)+(p2?(p2-a2):n2)+FTS3_VARINT_MAX-1) );
}
if( rc!=SQLITE_OK ){
@@ -164062,18 +165147,6 @@ static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){
}
/*
-** The following are copied from sqliteInt.h.
-**
-** Constants for the largest and smallest possible 64-bit signed integers.
-** These macros are designed to work correctly on both 32-bit and 64-bit
-** compilers.
-*/
-#ifndef SQLITE_AMALGAMATION
-# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
-# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
-#endif
-
-/*
** If the numeric type of argument pVal is "integer", then return it
** converted to a 64-bit signed integer. Otherwise, return a copy of
** the second parameter, iDefault.
@@ -174802,14 +175875,14 @@ static int nodeReaderInit(NodeReader *p, const char *aNode, int nNode){
p->nNode = nNode;
/* Figure out if this is a leaf or an internal node. */
- if( p->aNode[0] ){
+ if( aNode && aNode[0] ){
/* An internal node. */
p->iOff = 1 + sqlite3Fts3GetVarint(&p->aNode[1], &p->iChild);
}else{
p->iOff = 1;
}
- return nodeReaderNext(p);
+ return aNode ? nodeReaderNext(p) : SQLITE_OK;
}
/*
@@ -174946,6 +176019,7 @@ static int fts3AppendToNode(
nPrefix = fts3PrefixCompress(pPrev->a, pPrev->n, zTerm, nTerm);
nSuffix = nTerm - nPrefix;
+ if( nSuffix<=0 ) return FTS_CORRUPT_VTAB;
memcpy(pPrev->a, zTerm, nTerm);
pPrev->n = nTerm;
@@ -175300,8 +176374,8 @@ static int fts3IncrmergeLoad(
NodeReader reader;
pNode = &pWriter->aNodeWriter[i];
- rc = nodeReaderInit(&reader, pNode->block.a, pNode->block.n);
- if( reader.aNode ){
+ if( pNode->block.a){
+ rc = nodeReaderInit(&reader, pNode->block.a, pNode->block.n);
while( reader.aNode && rc==SQLITE_OK ) rc = nodeReaderNext(&reader);
blobGrowBuffer(&pNode->key, reader.term.n, &rc);
if( rc==SQLITE_OK ){
@@ -177166,10 +178240,10 @@ static void fts3SnippetDetails(
while( iCsr<(iStart+pIter->nSnippet) && iCsr>=iStart ){
int j;
- u64 mPhrase = (u64)1 << i;
+ u64 mPhrase = (u64)1 << (i%64);
u64 mPos = (u64)1 << (iCsr - iStart);
assert( iCsr>=iStart && (iCsr - iStart)<=64 );
- assert( i>=0 && i<=64 );
+ assert( i>=0 );
if( (mCover|mCovered)&mPhrase ){
iScore++;
}else{
@@ -180330,6 +181404,7 @@ static JsonNode *jsonLookupStep(
const char *zKey;
JsonNode *pRoot = &pParse->aNode[iRoot];
if( zPath[0]==0 ) return pRoot;
+ if( pRoot->jnFlags & JNODE_REPLACE ) return 0;
if( zPath[0]=='.' ){
if( pRoot->eType!=JSON_OBJECT ) return 0;
zPath++;
@@ -181066,7 +182141,7 @@ static void jsonArrayStep(
if( pStr->zBuf==0 ){
jsonInit(pStr, ctx);
jsonAppendChar(pStr, '[');
- }else{
+ }else if( pStr->nUsed>1 ){
jsonAppendChar(pStr, ',');
pStr->pCtx = ctx;
}
@@ -181114,9 +182189,11 @@ static void jsonGroupInverse(
int argc,
sqlite3_value **argv
){
- int i;
+ unsigned int i;
int inStr = 0;
+ int nNest = 0;
char *z;
+ char c;
JsonString *pStr;
UNUSED_PARAM(argc);
UNUSED_PARAM(argv);
@@ -181127,12 +182204,18 @@ static void jsonGroupInverse(
if( NEVER(!pStr) ) return;
#endif
z = pStr->zBuf;
- for(i=1; z[i]!=',' || inStr; i++){
- assert( i<pStr->nUsed );
- if( z[i]=='"' ){
+ for(i=1; (c = z[i])!=',' || inStr || nNest; i++){
+ if( i>=pStr->nUsed ){
+ pStr->nUsed = 1;
+ return;
+ }
+ if( c=='"' ){
inStr = !inStr;
- }else if( z[i]=='\\' ){
+ }else if( c=='\\' ){
i++;
+ }else if( !inStr ){
+ if( c=='{' || c=='[' ) nNest++;
+ if( c=='}' || c==']' ) nNest--;
}
}
pStr->nUsed -= i;
@@ -181162,7 +182245,7 @@ static void jsonObjectStep(
if( pStr->zBuf==0 ){
jsonInit(pStr, ctx);
jsonAppendChar(pStr, '{');
- }else{
+ }else if( pStr->nUsed>1 ){
jsonAppendChar(pStr, ',');
pStr->pCtx = ctx;
}
@@ -181750,14 +182833,14 @@ SQLITE_PRIVATE int sqlite3Json1Init(sqlite3 *db){
#endif
for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
rc = sqlite3_create_function(db, aFunc[i].zName, aFunc[i].nArg,
- SQLITE_UTF8 | SQLITE_DETERMINISTIC,
+ SQLITE_UTF8 | SQLITE_DETERMINISTIC,
(void*)&aFunc[i].flag,
aFunc[i].xFunc, 0, 0);
}
#ifndef SQLITE_OMIT_WINDOWFUNC
for(i=0; i<sizeof(aAgg)/sizeof(aAgg[0]) && rc==SQLITE_OK; i++){
rc = sqlite3_create_window_function(db, aAgg[i].zName, aAgg[i].nArg,
- SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
+ SQLITE_SUBTYPE | SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
aAgg[i].xStep, aAgg[i].xFinal,
aAgg[i].xValue, jsonGroupInverse, 0);
}
@@ -182460,7 +183543,6 @@ static int nodeAcquire(
** increase its reference count and return it.
*/
if( (pNode = nodeHashLookup(pRtree, iNode))!=0 ){
- assert( !pParent || !pNode->pParent || pNode->pParent==pParent );
if( pParent && !pNode->pParent ){
if( nodeInParentChain(pNode, pParent) ){
RTREE_IS_CORRUPT(pRtree);
@@ -182468,6 +183550,9 @@ static int nodeAcquire(
}
pParent->nRef++;
pNode->pParent = pParent;
+ }else if( pParent && pNode->pParent && pParent!=pNode->pParent ){
+ RTREE_IS_CORRUPT(pRtree);
+ return SQLITE_CORRUPT_VTAB;
}
pNode->nRef++;
*ppNode = pNode;
@@ -183355,13 +184440,14 @@ static int rtreeStepToLeaf(RtreeCursor *pCur){
eInt = pRtree->eCoordType==RTREE_COORD_INT32;
while( (p = rtreeSearchPointFirst(pCur))!=0 && p->iLevel>0 ){
+ u8 *pCellData;
pNode = rtreeNodeOfFirstSearchPoint(pCur, &rc);
if( rc ) return rc;
nCell = NCELL(pNode);
assert( nCell<200 );
+ pCellData = pNode->zData + (4+pRtree->nBytesPerCell*p->iCell);
while( p->iCell<nCell ){
sqlite3_rtree_dbl rScore = (sqlite3_rtree_dbl)-1;
- u8 *pCellData = pNode->zData + (4+pRtree->nBytesPerCell*p->iCell);
eWithin = FULLY_WITHIN;
for(ii=0; ii<nConstraint; ii++){
RtreeConstraint *pConstraint = pCur->aConstraint + ii;
@@ -183374,13 +184460,23 @@ static int rtreeStepToLeaf(RtreeCursor *pCur){
}else{
rtreeNonleafConstraint(pConstraint, eInt, pCellData, &eWithin);
}
- if( eWithin==NOT_WITHIN ) break;
+ if( eWithin==NOT_WITHIN ){
+ p->iCell++;
+ pCellData += pRtree->nBytesPerCell;
+ break;
+ }
}
- p->iCell++;
if( eWithin==NOT_WITHIN ) continue;
+ p->iCell++;
x.iLevel = p->iLevel - 1;
if( x.iLevel ){
x.id = readInt64(pCellData);
+ for(ii=0; ii<pCur->nPoint; ii++){
+ if( pCur->aPoint[ii].id==x.id ){
+ RTREE_IS_CORRUPT(pRtree);
+ return SQLITE_CORRUPT_VTAB;
+ }
+ }
x.iCell = 0;
}else{
x.id = p->id;
@@ -189688,6 +190784,7 @@ SQLITE_API void sqlite3rbu_destroy_vfs(const char *zName);
typedef struct RbuFrame RbuFrame;
typedef struct RbuObjIter RbuObjIter;
typedef struct RbuState RbuState;
+typedef struct RbuSpan RbuSpan;
typedef struct rbu_vfs rbu_vfs;
typedef struct rbu_file rbu_file;
typedef struct RbuUpdateStmt RbuUpdateStmt;
@@ -189732,6 +190829,11 @@ struct RbuUpdateStmt {
RbuUpdateStmt *pNext;
};
+struct RbuSpan {
+ const char *zSpan;
+ int nSpan;
+};
+
/*
** An iterator of this type is used to iterate through all objects in
** the target database that require updating. For each such table, the
@@ -189781,6 +190883,9 @@ struct RbuObjIter {
sqlite3_stmt *pInsert; /* Statement for INSERT operations */
sqlite3_stmt *pDelete; /* Statement for DELETE ops */
sqlite3_stmt *pTmpInsert; /* Insert into rbu_tmp_$zDataTbl */
+ int nIdxCol;
+ RbuSpan *aIdxCol;
+ char *zIdxSql;
/* Last UPDATE used (for PK b-tree updates only), or NULL. */
RbuUpdateStmt *pRbuUpdate;
@@ -190315,6 +191420,8 @@ static void rbuObjIterClearStatements(RbuObjIter *pIter){
sqlite3_free(pUp);
pUp = pTmp;
}
+ sqlite3_free(pIter->aIdxCol);
+ sqlite3_free(pIter->zIdxSql);
pIter->pSelect = 0;
pIter->pInsert = 0;
@@ -190322,6 +191429,9 @@ static void rbuObjIterClearStatements(RbuObjIter *pIter){
pIter->pRbuUpdate = 0;
pIter->pTmpInsert = 0;
pIter->nCol = 0;
+ pIter->nIdxCol = 0;
+ pIter->aIdxCol = 0;
+ pIter->zIdxSql = 0;
}
/*
@@ -190436,8 +191546,8 @@ static void rbuTargetNameFunc(
zIn = (const char*)sqlite3_value_text(argv[0]);
if( zIn ){
if( rbuIsVacuum(p) ){
- assert( argc==2 );
- if( 0==sqlite3_value_int(argv[1]) ){
+ assert( argc==2 || argc==1 );
+ if( argc==1 || 0==sqlite3_value_int(argv[1]) ){
sqlite3_result_text(pCtx, zIn, -1, SQLITE_STATIC);
}
}else{
@@ -190595,14 +191705,15 @@ static void rbuAllocateIterArrays(sqlite3rbu *p, RbuObjIter *pIter, int nCol){
static char *rbuStrndup(const char *zStr, int *pRc){
char *zRet = 0;
- assert( *pRc==SQLITE_OK );
- if( zStr ){
- size_t nCopy = strlen(zStr) + 1;
- zRet = (char*)sqlite3_malloc64(nCopy);
- if( zRet ){
- memcpy(zRet, zStr, nCopy);
- }else{
- *pRc = SQLITE_NOMEM;
+ if( *pRc==SQLITE_OK ){
+ if( zStr ){
+ size_t nCopy = strlen(zStr) + 1;
+ zRet = (char*)sqlite3_malloc64(nCopy);
+ if( zRet ){
+ memcpy(zRet, zStr, nCopy);
+ }else{
+ *pRc = SQLITE_NOMEM;
+ }
}
}
@@ -190774,6 +191885,9 @@ static void rbuObjIterCacheIndexedCols(sqlite3rbu *p, RbuObjIter *pIter){
while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXInfo) ){
int iCid = sqlite3_column_int(pXInfo, 1);
if( iCid>=0 ) pIter->abIndexed[iCid] = 1;
+ if( iCid==-2 ){
+ memset(pIter->abIndexed, 0x01, sizeof(u8)*pIter->nTblCol);
+ }
}
rbuFinalize(p, pXInfo);
bIndex = 1;
@@ -191185,29 +192299,37 @@ static char *rbuObjIterGetIndexCols(
int iCid = sqlite3_column_int(pXInfo, 1);
int bDesc = sqlite3_column_int(pXInfo, 3);
const char *zCollate = (const char*)sqlite3_column_text(pXInfo, 4);
- const char *zCol;
+ const char *zCol = 0;
const char *zType;
- if( iCid<0 ){
- /* An integer primary key. If the table has an explicit IPK, use
- ** its name. Otherwise, use "rbu_rowid". */
- if( pIter->eType==RBU_PK_IPK ){
- int i;
- for(i=0; pIter->abTblPk[i]==0; i++);
- assert( i<pIter->nTblCol );
- zCol = pIter->azTblCol[i];
- }else if( rbuIsVacuum(p) ){
- zCol = "_rowid_";
+ if( iCid==-2 ){
+ int iSeq = sqlite3_column_int(pXInfo, 0);
+ zRet = sqlite3_mprintf("%z%s(%.*s) COLLATE %Q", zRet, zCom,
+ pIter->aIdxCol[iSeq].nSpan, pIter->aIdxCol[iSeq].zSpan, zCollate
+ );
+ zType = "";
+ }else {
+ if( iCid<0 ){
+ /* An integer primary key. If the table has an explicit IPK, use
+ ** its name. Otherwise, use "rbu_rowid". */
+ if( pIter->eType==RBU_PK_IPK ){
+ int i;
+ for(i=0; pIter->abTblPk[i]==0; i++);
+ assert( i<pIter->nTblCol );
+ zCol = pIter->azTblCol[i];
+ }else if( rbuIsVacuum(p) ){
+ zCol = "_rowid_";
+ }else{
+ zCol = "rbu_rowid";
+ }
+ zType = "INTEGER";
}else{
- zCol = "rbu_rowid";
+ zCol = pIter->azTblCol[iCid];
+ zType = pIter->azTblType[iCid];
}
- zType = "INTEGER";
- }else{
- zCol = pIter->azTblCol[iCid];
- zType = pIter->azTblType[iCid];
+ zRet = sqlite3_mprintf("%z%s\"%w\" COLLATE %Q", zRet, zCom,zCol,zCollate);
}
- zRet = sqlite3_mprintf("%z%s\"%w\" COLLATE %Q", zRet, zCom, zCol, zCollate);
if( pIter->bUnique==0 || sqlite3_column_int(pXInfo, 5) ){
const char *zOrder = (bDesc ? " DESC" : "");
zImpPK = sqlite3_mprintf("%z%s\"rbu_imp_%d%w\"%s",
@@ -191687,6 +192809,8 @@ static char *rbuObjIterGetIndexWhere(sqlite3rbu *p, RbuObjIter *pIter){
int rc = p->rc;
char *zRet = 0;
+ assert( pIter->zIdxSql==0 && pIter->nIdxCol==0 && pIter->aIdxCol==0 );
+
if( rc==SQLITE_OK ){
rc = prepareAndCollectError(p->dbMain, &pStmt, &p->zErrmsg,
"SELECT trim(sql) FROM sqlite_master WHERE type='index' AND name=?"
@@ -191696,21 +192820,50 @@ static char *rbuObjIterGetIndexWhere(sqlite3rbu *p, RbuObjIter *pIter){
int rc2;
rc = sqlite3_bind_text(pStmt, 1, pIter->zIdx, -1, SQLITE_STATIC);
if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
- const char *zSql = (const char*)sqlite3_column_text(pStmt, 0);
+ char *zSql = (char*)sqlite3_column_text(pStmt, 0);
+ if( zSql ){
+ pIter->zIdxSql = zSql = rbuStrndup(zSql, &rc);
+ }
if( zSql ){
int nParen = 0; /* Number of open parenthesis */
int i;
+ int iIdxCol = 0;
+ int nIdxAlloc = 0;
for(i=0; zSql[i]; i++){
char c = zSql[i];
+
+ /* If necessary, grow the pIter->aIdxCol[] array */
+ if( iIdxCol==nIdxAlloc ){
+ RbuSpan *aIdxCol = (RbuSpan*)sqlite3_realloc(
+ pIter->aIdxCol, (nIdxAlloc+16)*sizeof(RbuSpan)
+ );
+ if( aIdxCol==0 ){
+ rc = SQLITE_NOMEM;
+ break;
+ }
+ pIter->aIdxCol = aIdxCol;
+ nIdxAlloc += 16;
+ }
+
if( c=='(' ){
+ if( nParen==0 ){
+ assert( iIdxCol==0 );
+ pIter->aIdxCol[0].zSpan = &zSql[i+1];
+ }
nParen++;
}
else if( c==')' ){
nParen--;
if( nParen==0 ){
+ int nSpan = &zSql[i] - pIter->aIdxCol[iIdxCol].zSpan;
+ pIter->aIdxCol[iIdxCol++].nSpan = nSpan;
i++;
break;
}
+ }else if( c==',' && nParen==1 ){
+ int nSpan = &zSql[i] - pIter->aIdxCol[iIdxCol].zSpan;
+ pIter->aIdxCol[iIdxCol++].nSpan = nSpan;
+ pIter->aIdxCol[iIdxCol].zSpan = &zSql[i+1];
}else if( c=='"' || c=='\'' || c=='`' ){
for(i++; 1; i++){
if( zSql[i]==c ){
@@ -191722,11 +192875,19 @@ static char *rbuObjIterGetIndexWhere(sqlite3rbu *p, RbuObjIter *pIter){
for(i++; 1; i++){
if( zSql[i]==']' ) break;
}
+ }else if( c=='-' && zSql[i+1]=='-' ){
+ for(i=i+2; zSql[i] && zSql[i]!='\n'; i++);
+ if( zSql[i]=='\0' ) break;
+ }else if( c=='/' && zSql[i+1]=='*' ){
+ for(i=i+2; zSql[i] && (zSql[i]!='*' || zSql[i+1]!='/'); i++);
+ if( zSql[i]=='\0' ) break;
+ i++;
}
}
if( zSql[i] ){
zRet = rbuStrndup(&zSql[i], &rc);
}
+ pIter->nIdxCol = iIdxCol;
}
}
@@ -191771,11 +192932,11 @@ static int rbuObjIterPrepareAll(
int nBind = 0;
assert( pIter->eType!=RBU_PK_VTAB );
+ zPart = rbuObjIterGetIndexWhere(p, pIter);
zCollist = rbuObjIterGetIndexCols(
p, pIter, &zImposterCols, &zImposterPK, &zWhere, &nBind
);
zBind = rbuObjIterGetBindlist(p, nBind);
- zPart = rbuObjIterGetIndexWhere(p, pIter);
/* Create the imposter table used to write to this index. */
sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 0, 1);
@@ -193301,10 +194462,11 @@ static void rbuIndexCntFunc(
sqlite3_stmt *pStmt = 0;
char *zErrmsg = 0;
int rc;
+ sqlite3 *db = (rbuIsVacuum(p) ? p->dbRbu : p->dbMain);
assert( nVal==1 );
- rc = prepareFreeAndCollectError(p->dbMain, &pStmt, &zErrmsg,
+ rc = prepareFreeAndCollectError(db, &pStmt, &zErrmsg,
sqlite3_mprintf("SELECT count(*) FROM sqlite_master "
"WHERE type='index' AND tbl_name = %Q", sqlite3_value_text(apVal[0]))
);
@@ -193319,7 +194481,7 @@ static void rbuIndexCntFunc(
if( rc==SQLITE_OK ){
sqlite3_result_int(pCtx, nIndex);
}else{
- sqlite3_result_error(pCtx, sqlite3_errmsg(p->dbMain), -1);
+ sqlite3_result_error(pCtx, sqlite3_errmsg(db), -1);
}
}
@@ -197762,7 +198924,7 @@ static int sessionBufferGrow(SessionBuffer *p, size_t nByte, int *pRc){
i64 nNew = p->nAlloc ? p->nAlloc : 128;
do {
nNew = nNew*2;
- }while( (nNew-p->nBuf)<nByte );
+ }while( (size_t)(nNew-p->nBuf)<nByte );
aNew = (u8 *)sqlite3_realloc64(p->aBuf, nNew);
if( 0==aNew ){
@@ -202163,6 +203325,7 @@ struct Fts5Config {
char *zContentExprlist;
Fts5Tokenizer *pTok;
fts5_tokenizer *pTokApi;
+ int bLock; /* True when table is preparing statement */
/* Values loaded from the %_config table */
int iCookie; /* Incremented when %_config is modified */
@@ -202679,6 +203842,7 @@ static int sqlite3Fts5ExprEof(Fts5Expr*);
static i64 sqlite3Fts5ExprRowid(Fts5Expr*);
static void sqlite3Fts5ExprFree(Fts5Expr*);
+static int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2);
/* Called during startup to register a UDF with SQLite */
static int sqlite3Fts5ExprInit(Fts5Global*, sqlite3*);
@@ -203530,15 +204694,18 @@ static fts5YYACTIONTYPE fts5yy_find_shift_action(
do{
i = fts5yy_shift_ofst[stateno];
assert( i>=0 );
- /* assert( i+fts5YYNFTS5TOKEN<=(int)fts5YY_NLOOKAHEAD ); */
+ assert( i<=fts5YY_ACTTAB_COUNT );
+ assert( i+fts5YYNFTS5TOKEN<=(int)fts5YY_NLOOKAHEAD );
assert( iLookAhead!=fts5YYNOCODE );
assert( iLookAhead < fts5YYNFTS5TOKEN );
i += iLookAhead;
- if( i>=fts5YY_NLOOKAHEAD || fts5yy_lookahead[i]!=iLookAhead ){
+ assert( i<(int)fts5YY_NLOOKAHEAD );
+ if( fts5yy_lookahead[i]!=iLookAhead ){
#ifdef fts5YYFALLBACK
fts5YYCODETYPE iFallback; /* Fallback token */
- if( iLookAhead<sizeof(fts5yyFallback)/sizeof(fts5yyFallback[0])
- && (iFallback = fts5yyFallback[iLookAhead])!=0 ){
+ assert( iLookAhead<sizeof(fts5yyFallback)/sizeof(fts5yyFallback[0]) );
+ iFallback = fts5yyFallback[iLookAhead];
+ if( iFallback!=0 ){
#ifndef NDEBUG
if( fts5yyTraceFILE ){
fprintf(fts5yyTraceFILE, "%sFALLBACK %s => %s\n",
@@ -203553,16 +204720,8 @@ static fts5YYACTIONTYPE fts5yy_find_shift_action(
#ifdef fts5YYWILDCARD
{
int j = i - iLookAhead + fts5YYWILDCARD;
- if(
-#if fts5YY_SHIFT_MIN+fts5YYWILDCARD<0
- j>=0 &&
-#endif
-#if fts5YY_SHIFT_MAX+fts5YYWILDCARD>=fts5YY_ACTTAB_COUNT
- j<fts5YY_ACTTAB_COUNT &&
-#endif
- j<(int)(sizeof(fts5yy_lookahead)/sizeof(fts5yy_lookahead[0])) &&
- fts5yy_lookahead[j]==fts5YYWILDCARD && iLookAhead>0
- ){
+ assert( j<(int)(sizeof(fts5yy_lookahead)/sizeof(fts5yy_lookahead[0])) );
+ if( fts5yy_lookahead[j]==fts5YYWILDCARD && iLookAhead>0 ){
#ifndef NDEBUG
if( fts5yyTraceFILE ){
fprintf(fts5yyTraceFILE, "%sWILDCARD %s => %s\n",
@@ -203576,6 +204735,7 @@ static fts5YYACTIONTYPE fts5yy_find_shift_action(
#endif /* fts5YYWILDCARD */
return fts5yy_default[stateno];
}else{
+ assert( i>=0 && i<sizeof(fts5yy_action)/sizeof(fts5yy_action[0]) );
return fts5yy_action[i];
}
}while(1);
@@ -204276,9 +205436,8 @@ static void sqlite3Fts5Parser(
*/
static int sqlite3Fts5ParserFallback(int iToken){
#ifdef fts5YYFALLBACK
- if( iToken<(int)(sizeof(fts5yyFallback)/sizeof(fts5yyFallback[0])) ){
- return fts5yyFallback[iToken];
- }
+ assert( iToken<(int)(sizeof(fts5yyFallback)/sizeof(fts5yyFallback[0])) );
+ return fts5yyFallback[iToken];
#else
(void)iToken;
#endif
@@ -206091,7 +207250,7 @@ static int sqlite3Fts5ConfigDeclareVtab(Fts5Config *pConfig){
rc = sqlite3_declare_vtab(pConfig->db, zSql);
sqlite3_free(zSql);
}
-
+
return rc;
}
@@ -206679,6 +207838,42 @@ static void sqlite3Fts5ExprFree(Fts5Expr *p){
}
}
+static int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2){
+ Fts5Parse sParse;
+ memset(&sParse, 0, sizeof(sParse));
+
+ if( *pp1 ){
+ Fts5Expr *p1 = *pp1;
+ int nPhrase = p1->nPhrase + p2->nPhrase;
+
+ p1->pRoot = sqlite3Fts5ParseNode(&sParse, FTS5_AND, p1->pRoot, p2->pRoot,0);
+ p2->pRoot = 0;
+
+ if( sParse.rc==SQLITE_OK ){
+ Fts5ExprPhrase **ap = (Fts5ExprPhrase**)sqlite3_realloc(
+ p1->apExprPhrase, nPhrase * sizeof(Fts5ExprPhrase*)
+ );
+ if( ap==0 ){
+ sParse.rc = SQLITE_NOMEM;
+ }else{
+ int i;
+ memmove(&ap[p2->nPhrase], ap, p1->nPhrase*sizeof(Fts5ExprPhrase*));
+ for(i=0; i<p2->nPhrase; i++){
+ ap[i] = p2->apExprPhrase[i];
+ }
+ p1->nPhrase = nPhrase;
+ p1->apExprPhrase = ap;
+ }
+ }
+ sqlite3_free(p2->apExprPhrase);
+ sqlite3_free(p2);
+ }else{
+ *pp1 = p2;
+ }
+
+ return sParse.rc;
+}
+
/*
** Argument pTerm must be a synonym iterator. Return the current rowid
** that it points to.
@@ -210475,6 +211670,7 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){
}else{
/* TODO1: Fix this */
pRet->p[nByte] = 0x00;
+ pRet->p[nByte+1] = 0x00;
pRet->szLeaf = fts5GetU16(&pRet->p[2]);
}
}
@@ -210497,7 +211693,7 @@ static void fts5DataRelease(Fts5Data *pData){
static Fts5Data *fts5LeafRead(Fts5Index *p, i64 iRowid){
Fts5Data *pRet = fts5DataRead(p, iRowid);
if( pRet ){
- if( pRet->szLeaf>pRet->nn ){
+ if( pRet->nn<4 || pRet->szLeaf>pRet->nn ){
p->rc = FTS5_CORRUPT;
fts5DataRelease(pRet);
pRet = 0;
@@ -214781,9 +215977,12 @@ static void fts5MergePrefixLists(
Fts5PoslistWriter writer;
memset(&writer, 0, sizeof(writer));
+ /* See the earlier comment in this function for an explanation of why
+ ** corrupt input position lists might cause the output to consume
+ ** at most 20 bytes of unexpected space. */
fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
fts5BufferZero(&tmp);
- sqlite3Fts5BufferSize(&p->rc, &tmp, i1.nPoslist + i2.nPoslist);
+ sqlite3Fts5BufferSize(&p->rc, &tmp, i1.nPoslist + i2.nPoslist + 10 + 10);
if( p->rc ) break;
sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1);
@@ -214831,6 +216030,12 @@ static void fts5MergePrefixLists(
}
/* WRITEPOSLISTSIZE */
+ assert_nc( tmp.n<=i1.nPoslist+i2.nPoslist );
+ assert( tmp.n<=i1.nPoslist+i2.nPoslist+10+10 );
+ if( tmp.n>i1.nPoslist+i2.nPoslist ){
+ if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT;
+ break;
+ }
fts5BufferSafeAppendVarint(&out, tmp.n * 2);
fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n);
fts5DoclistIterNext(&i1);
@@ -216832,17 +218037,39 @@ static void fts5SetUniqueFlag(sqlite3_index_info *pIdxInfo){
** Implementation of the xBestIndex method for FTS5 tables. Within the
** WHERE constraint, it searches for the following:
**
-** 1. A MATCH constraint against the special column.
+** 1. A MATCH constraint against the table column.
** 2. A MATCH constraint against the "rank" column.
-** 3. An == constraint against the rowid column.
-** 4. A < or <= constraint against the rowid column.
-** 5. A > or >= constraint against the rowid column.
+** 3. A MATCH constraint against some other column.
+** 4. An == constraint against the rowid column.
+** 5. A < or <= constraint against the rowid column.
+** 6. A > or >= constraint against the rowid column.
**
-** Within the ORDER BY, either:
+** Within the ORDER BY, the following are supported:
**
** 5. ORDER BY rank [ASC|DESC]
** 6. ORDER BY rowid [ASC|DESC]
**
+** Information for the xFilter call is passed via both the idxNum and
+** idxStr variables. Specifically, idxNum is a bitmask of the following
+** flags used to encode the ORDER BY clause:
+**
+** FTS5_BI_ORDER_RANK
+** FTS5_BI_ORDER_ROWID
+** FTS5_BI_ORDER_DESC
+**
+** idxStr is used to encode data from the WHERE clause. For each argument
+** passed to the xFilter method, the following is appended to idxStr:
+**
+** Match against table column: "m"
+** Match against rank column: "r"
+** Match against other column: "<column-number>"
+** Equality constraint against the rowid: "="
+** A < or <= against the rowid: "<"
+** A > or >= against the rowid: ">"
+**
+** This function ensures that there is at most one "r" or "=". And that if
+** there exists an "=" then there is no "<" or ">".
+**
** Costs are assigned as follows:
**
** a) If an unusable MATCH operator is present in the WHERE clause, the
@@ -216870,32 +218097,18 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
Fts5Config *pConfig = pTab->pConfig;
const int nCol = pConfig->nCol;
int idxFlags = 0; /* Parameter passed through to xFilter() */
- int bHasMatch;
- int iNext;
int i;
- struct Constraint {
- int op; /* Mask against sqlite3_index_constraint.op */
- int fts5op; /* FTS5 mask for idxFlags */
- int iCol; /* 0==rowid, 1==tbl, 2==rank */
- int omit; /* True to omit this if found */
- int iConsIndex; /* Index in pInfo->aConstraint[] */
- } aConstraint[] = {
- {SQLITE_INDEX_CONSTRAINT_MATCH|SQLITE_INDEX_CONSTRAINT_EQ,
- FTS5_BI_MATCH, 1, 1, -1},
- {SQLITE_INDEX_CONSTRAINT_MATCH|SQLITE_INDEX_CONSTRAINT_EQ,
- FTS5_BI_RANK, 2, 1, -1},
- {SQLITE_INDEX_CONSTRAINT_EQ, FTS5_BI_ROWID_EQ, 0, 0, -1},
- {SQLITE_INDEX_CONSTRAINT_LT|SQLITE_INDEX_CONSTRAINT_LE,
- FTS5_BI_ROWID_LE, 0, 0, -1},
- {SQLITE_INDEX_CONSTRAINT_GT|SQLITE_INDEX_CONSTRAINT_GE,
- FTS5_BI_ROWID_GE, 0, 0, -1},
- };
+ char *idxStr;
+ int iIdxStr = 0;
+ int iCons = 0;
+
+ int bSeenEq = 0;
+ int bSeenGt = 0;
+ int bSeenLt = 0;
+ int bSeenMatch = 0;
+ int bSeenRank = 0;
- int aColMap[3];
- aColMap[0] = -1;
- aColMap[1] = nCol;
- aColMap[2] = nCol+1;
assert( SQLITE_INDEX_CONSTRAINT_EQ<SQLITE_INDEX_CONSTRAINT_MATCH );
assert( SQLITE_INDEX_CONSTRAINT_GT<SQLITE_INDEX_CONSTRAINT_MATCH );
@@ -216903,40 +218116,85 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
assert( SQLITE_INDEX_CONSTRAINT_GE<SQLITE_INDEX_CONSTRAINT_MATCH );
assert( SQLITE_INDEX_CONSTRAINT_LE<SQLITE_INDEX_CONSTRAINT_MATCH );
- /* Set idxFlags flags for all WHERE clause terms that will be used. */
+ if( pConfig->bLock ){
+ pTab->base.zErrMsg = sqlite3_mprintf(
+ "recursively defined fts5 content table"
+ );
+ return SQLITE_ERROR;
+ }
+
+ idxStr = (char*)sqlite3_malloc(pInfo->nConstraint * 6 + 1);
+ if( idxStr==0 ) return SQLITE_NOMEM;
+ pInfo->idxStr = idxStr;
+ pInfo->needToFreeIdxStr = 1;
+
for(i=0; i<pInfo->nConstraint; i++){
struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
int iCol = p->iColumn;
-
- if( (p->op==SQLITE_INDEX_CONSTRAINT_MATCH && iCol>=0 && iCol<=nCol)
- || (p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol==nCol)
+ if( p->op==SQLITE_INDEX_CONSTRAINT_MATCH
+ || (p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol>=nCol)
){
/* A MATCH operator or equivalent */
- if( p->usable ){
- idxFlags = (idxFlags & 0xFFFF) | FTS5_BI_MATCH | (iCol << 16);
- aConstraint[0].iConsIndex = i;
- }else{
+ if( p->usable==0 || iCol<0 ){
/* As there exists an unusable MATCH constraint this is an
** unusable plan. Set a prohibitively high cost. */
pInfo->estimatedCost = 1e50;
+ assert( iIdxStr < pInfo->nConstraint*6 + 1 );
+ idxStr[iIdxStr] = 0;
return SQLITE_OK;
+ }else{
+ if( iCol==nCol+1 ){
+ if( bSeenRank ) continue;
+ idxStr[iIdxStr++] = 'r';
+ bSeenRank = 1;
+ }else{
+ bSeenMatch = 1;
+ idxStr[iIdxStr++] = 'm';
+ if( iCol<nCol ){
+ sqlite3_snprintf(6, &idxStr[iIdxStr], "%d", iCol);
+ idxStr += strlen(&idxStr[iIdxStr]);
+ assert( idxStr[iIdxStr]=='\0' );
+ }
+ }
+ pInfo->aConstraintUsage[i].argvIndex = ++iCons;
+ pInfo->aConstraintUsage[i].omit = 1;
}
- }else if( p->op<=SQLITE_INDEX_CONSTRAINT_MATCH ){
- int j;
- for(j=1; j<ArraySize(aConstraint); j++){
- struct Constraint *pC = &aConstraint[j];
- if( iCol==aColMap[pC->iCol] && (p->op & pC->op) && p->usable ){
- pC->iConsIndex = i;
- idxFlags |= pC->fts5op;
+ }
+ else if( p->usable && bSeenEq==0
+ && p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol<0
+ ){
+ idxStr[iIdxStr++] = '=';
+ bSeenEq = 1;
+ pInfo->aConstraintUsage[i].argvIndex = ++iCons;
+ }
+ }
+
+ if( bSeenEq==0 ){
+ for(i=0; i<pInfo->nConstraint; i++){
+ struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
+ if( p->iColumn<0 && p->usable ){
+ int op = p->op;
+ if( op==SQLITE_INDEX_CONSTRAINT_LT || op==SQLITE_INDEX_CONSTRAINT_LE ){
+ if( bSeenLt ) continue;
+ idxStr[iIdxStr++] = '<';
+ pInfo->aConstraintUsage[i].argvIndex = ++iCons;
+ bSeenLt = 1;
+ }else
+ if( op==SQLITE_INDEX_CONSTRAINT_GT || op==SQLITE_INDEX_CONSTRAINT_GE ){
+ if( bSeenGt ) continue;
+ idxStr[iIdxStr++] = '>';
+ pInfo->aConstraintUsage[i].argvIndex = ++iCons;
+ bSeenGt = 1;
}
}
}
}
+ idxStr[iIdxStr] = '\0';
/* Set idxFlags flags for the ORDER BY clause */
if( pInfo->nOrderBy==1 ){
int iSort = pInfo->aOrderBy[0].iColumn;
- if( iSort==(pConfig->nCol+1) && BitFlagTest(idxFlags, FTS5_BI_MATCH) ){
+ if( iSort==(pConfig->nCol+1) && bSeenMatch ){
idxFlags |= FTS5_BI_ORDER_RANK;
}else if( iSort==-1 ){
idxFlags |= FTS5_BI_ORDER_ROWID;
@@ -216950,26 +218208,15 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
}
/* Calculate the estimated cost based on the flags set in idxFlags. */
- bHasMatch = BitFlagTest(idxFlags, FTS5_BI_MATCH);
- if( BitFlagTest(idxFlags, FTS5_BI_ROWID_EQ) ){
- pInfo->estimatedCost = bHasMatch ? 100.0 : 10.0;
- if( bHasMatch==0 ) fts5SetUniqueFlag(pInfo);
- }else if( BitFlagAllTest(idxFlags, FTS5_BI_ROWID_LE|FTS5_BI_ROWID_GE) ){
- pInfo->estimatedCost = bHasMatch ? 500.0 : 250000.0;
- }else if( BitFlagTest(idxFlags, FTS5_BI_ROWID_LE|FTS5_BI_ROWID_GE) ){
- pInfo->estimatedCost = bHasMatch ? 750.0 : 750000.0;
+ if( bSeenEq ){
+ pInfo->estimatedCost = bSeenMatch ? 100.0 : 10.0;
+ if( bSeenMatch==0 ) fts5SetUniqueFlag(pInfo);
+ }else if( bSeenLt && bSeenGt ){
+ pInfo->estimatedCost = bSeenMatch ? 500.0 : 250000.0;
+ }else if( bSeenLt || bSeenGt ){
+ pInfo->estimatedCost = bSeenMatch ? 750.0 : 750000.0;
}else{
- pInfo->estimatedCost = bHasMatch ? 1000.0 : 1000000.0;
- }
-
- /* Assign argvIndex values to each constraint in use. */
- iNext = 1;
- for(i=0; i<ArraySize(aConstraint); i++){
- struct Constraint *pC = &aConstraint[i];
- if( pC->iConsIndex>=0 ){
- pInfo->aConstraintUsage[pC->iConsIndex].argvIndex = iNext++;
- pInfo->aConstraintUsage[pC->iConsIndex].omit = (unsigned char)pC->omit;
- }
+ pInfo->estimatedCost = bSeenMatch ? 1000.0 : 1000000.0;
}
pInfo->idxNum = idxFlags;
@@ -217292,7 +218539,7 @@ static int fts5CursorFirstSorted(
**
** If SQLite a built-in statement cache, this wouldn't be a problem. */
rc = fts5PrepareStatement(&pSorter->pStmt, pConfig,
- "SELECT rowid, rank FROM %Q.%Q ORDER BY %s(%s%s%s) %s",
+ "SELECT rowid, rank FROM %Q.%Q ORDER BY %s(\"%w\"%s%s) %s",
pConfig->zDb, pConfig->zName, zRank, pConfig->zName,
(zRankArgs ? ", " : ""),
(zRankArgs ? zRankArgs : ""),
@@ -217348,10 +218595,10 @@ static int fts5SpecialMatch(
assert( pTab->p.base.zErrMsg==0 );
pCsr->ePlan = FTS5_PLAN_SPECIAL;
- if( 0==sqlite3_strnicmp("reads", z, n) ){
+ if( n==5 && 0==sqlite3_strnicmp("reads", z, n) ){
pCsr->iSpecial = sqlite3Fts5IndexReads(pTab->p.pIndex);
}
- else if( 0==sqlite3_strnicmp("id", z, n) ){
+ else if( n==2 && 0==sqlite3_strnicmp("id", z, n) ){
pCsr->iSpecial = pCsr->iCsrId;
}
else{
@@ -217492,7 +218739,7 @@ static i64 fts5GetRowidLimit(sqlite3_value *pVal, i64 iDefault){
static int fts5FilterMethod(
sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */
int idxNum, /* Strategy index */
- const char *zUnused, /* Unused */
+ const char *idxStr, /* Unused */
int nVal, /* Number of elements in apVal */
sqlite3_value **apVal /* Arguments for the indexing scheme */
){
@@ -217500,19 +218747,17 @@ static int fts5FilterMethod(
Fts5Config *pConfig = pTab->p.pConfig;
Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
int rc = SQLITE_OK; /* Error code */
- int iVal = 0; /* Counter for apVal[] */
int bDesc; /* True if ORDER BY [rank|rowid] DESC */
int bOrderByRank; /* True if ORDER BY rank */
- sqlite3_value *pMatch = 0; /* <tbl> MATCH ? expression (or NULL) */
sqlite3_value *pRank = 0; /* rank MATCH ? expression (or NULL) */
sqlite3_value *pRowidEq = 0; /* rowid = ? expression (or NULL) */
sqlite3_value *pRowidLe = 0; /* rowid <= ? expression (or NULL) */
sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */
int iCol; /* Column on LHS of MATCH operator */
char **pzErrmsg = pConfig->pzErrmsg;
-
- UNUSED_PARAM(zUnused);
- UNUSED_PARAM(nVal);
+ int i;
+ int iIdxStr = 0;
+ Fts5Expr *pExpr = 0;
if( pCsr->ePlan ){
fts5FreeCursorComponents(pCsr);
@@ -217525,23 +218770,60 @@ static int fts5FilterMethod(
assert( pCsr->pRank==0 );
assert( pCsr->zRank==0 );
assert( pCsr->zRankArgs==0 );
+ assert( pTab->pSortCsr==0 || nVal==0 );
assert( pzErrmsg==0 || pzErrmsg==&pTab->p.base.zErrMsg );
pConfig->pzErrmsg = &pTab->p.base.zErrMsg;
- /* Decode the arguments passed through to this function.
- **
- ** Note: The following set of if(...) statements must be in the same
- ** order as the corresponding entries in the struct at the top of
- ** fts5BestIndexMethod(). */
- if( BitFlagTest(idxNum, FTS5_BI_MATCH) ) pMatch = apVal[iVal++];
- if( BitFlagTest(idxNum, FTS5_BI_RANK) ) pRank = apVal[iVal++];
- if( BitFlagTest(idxNum, FTS5_BI_ROWID_EQ) ) pRowidEq = apVal[iVal++];
- if( BitFlagTest(idxNum, FTS5_BI_ROWID_LE) ) pRowidLe = apVal[iVal++];
- if( BitFlagTest(idxNum, FTS5_BI_ROWID_GE) ) pRowidGe = apVal[iVal++];
- iCol = (idxNum>>16);
- assert( iCol>=0 && iCol<=pConfig->nCol );
- assert( iVal==nVal );
+ /* Decode the arguments passed through to this function. */
+ for(i=0; i<nVal; i++){
+ switch( idxStr[iIdxStr++] ){
+ case 'r':
+ pRank = apVal[i];
+ break;
+ case 'm': {
+ const char *zText = (const char*)sqlite3_value_text(apVal[i]);
+ if( zText==0 ) zText = "";
+
+ if( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' ){
+ iCol = 0;
+ do{
+ iCol = iCol*10 + (idxStr[iIdxStr]-'0');
+ iIdxStr++;
+ }while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' );
+ }else{
+ iCol = pConfig->nCol;
+ }
+
+ if( zText[0]=='*' ){
+ /* The user has issued a query of the form "MATCH '*...'". This
+ ** indicates that the MATCH expression is not a full text query,
+ ** but a request for an internal parameter. */
+ rc = fts5SpecialMatch(pTab, pCsr, &zText[1]);
+ goto filter_out;
+ }else{
+ char **pzErr = &pTab->p.base.zErrMsg;
+ rc = sqlite3Fts5ExprNew(pConfig, iCol, zText, &pExpr, pzErr);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5ExprAnd(&pCsr->pExpr, pExpr);
+ pExpr = 0;
+ }
+ if( rc!=SQLITE_OK ) goto filter_out;
+ }
+
+ break;
+ }
+ case '=':
+ pRowidEq = apVal[i];
+ break;
+ case '<':
+ pRowidLe = apVal[i];
+ break;
+ default: assert( idxStr[iIdxStr-1]=='>' );
+ pRowidGe = apVal[i];
+ break;
+ }
+ }
bOrderByRank = ((idxNum & FTS5_BI_ORDER_RANK) ? 1 : 0);
pCsr->bDesc = bDesc = ((idxNum & FTS5_BI_ORDER_DESC) ? 1 : 0);
@@ -217568,7 +218850,7 @@ static int fts5FilterMethod(
** (pCursor) is used to execute the query issued by function
** fts5CursorFirstSorted() above. */
assert( pRowidEq==0 && pRowidLe==0 && pRowidGe==0 && pRank==0 );
- assert( nVal==0 && pMatch==0 && bOrderByRank==0 && bDesc==0 );
+ assert( nVal==0 && bOrderByRank==0 && bDesc==0 );
assert( pCsr->iLastRowid==LARGEST_INT64 );
assert( pCsr->iFirstRowid==SMALLEST_INT64 );
if( pTab->pSortCsr->bDesc ){
@@ -217581,29 +218863,15 @@ static int fts5FilterMethod(
pCsr->ePlan = FTS5_PLAN_SOURCE;
pCsr->pExpr = pTab->pSortCsr->pExpr;
rc = fts5CursorFirst(pTab, pCsr, bDesc);
- }else if( pMatch ){
- const char *zExpr = (const char*)sqlite3_value_text(apVal[0]);
- if( zExpr==0 ) zExpr = "";
-
+ }else if( pCsr->pExpr ){
rc = fts5CursorParseRank(pConfig, pCsr, pRank);
if( rc==SQLITE_OK ){
- if( zExpr[0]=='*' ){
- /* The user has issued a query of the form "MATCH '*...'". This
- ** indicates that the MATCH expression is not a full text query,
- ** but a request for an internal parameter. */
- rc = fts5SpecialMatch(pTab, pCsr, &zExpr[1]);
+ if( bOrderByRank ){
+ pCsr->ePlan = FTS5_PLAN_SORTED_MATCH;
+ rc = fts5CursorFirstSorted(pTab, pCsr, bDesc);
}else{
- char **pzErr = &pTab->p.base.zErrMsg;
- rc = sqlite3Fts5ExprNew(pConfig, iCol, zExpr, &pCsr->pExpr, pzErr);
- if( rc==SQLITE_OK ){
- if( bOrderByRank ){
- pCsr->ePlan = FTS5_PLAN_SORTED_MATCH;
- rc = fts5CursorFirstSorted(pTab, pCsr, bDesc);
- }else{
- pCsr->ePlan = FTS5_PLAN_MATCH;
- rc = fts5CursorFirst(pTab, pCsr, bDesc);
- }
- }
+ pCsr->ePlan = FTS5_PLAN_MATCH;
+ rc = fts5CursorFirst(pTab, pCsr, bDesc);
}
}
}else if( pConfig->zContent==0 ){
@@ -217620,7 +218888,7 @@ static int fts5FilterMethod(
);
if( rc==SQLITE_OK ){
if( pCsr->ePlan==FTS5_PLAN_ROWID ){
- sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]);
+ sqlite3_bind_value(pCsr->pStmt, 1, pRowidEq);
}else{
sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iFirstRowid);
sqlite3_bind_int64(pCsr->pStmt, 2, pCsr->iLastRowid);
@@ -217629,6 +218897,8 @@ static int fts5FilterMethod(
}
}
+ filter_out:
+ sqlite3Fts5ExprFree(pExpr);
pConfig->pzErrmsg = pzErrmsg;
return rc;
}
@@ -218599,7 +219869,7 @@ static void fts5ApiCallback(
iCsrId = sqlite3_value_int64(argv[0]);
pCsr = fts5CursorFromCsrid(pAux->pGlobal, iCsrId);
- if( pCsr==0 ){
+ if( pCsr==0 || pCsr->ePlan==0 ){
char *zErr = sqlite3_mprintf("no such cursor: %lld", iCsrId);
sqlite3_result_error(context, zErr, -1);
sqlite3_free(zErr);
@@ -219015,7 +220285,7 @@ static void fts5SourceIdFunc(
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
- sqlite3_result_text(pCtx, "fts5: 2019-07-10 17:32:03 fc82b73eaac8b36950e527f12c4b5dc1e147e6f4ad2217ae43ad82882a88bfa6", -1, SQLITE_TRANSIENT);
+ sqlite3_result_text(pCtx, "fts5: 2019-10-10 20:19:45 18db032d058f1436ce3dea84081f4ee5a0f2259ad97301d43c426bc7f3df1b0b", -1, SQLITE_TRANSIENT);
}
/*
@@ -219287,7 +220557,9 @@ static int fts5StorageGetStmt(
}else{
int f = SQLITE_PREPARE_PERSISTENT;
if( eStmt>FTS5_STMT_LOOKUP ) f |= SQLITE_PREPARE_NO_VTAB;
+ p->pConfig->bLock++;
rc = sqlite3_prepare_v3(pC->db, zSql, -1, f, &p->aStmt[eStmt], 0);
+ p->pConfig->bLock--;
sqlite3_free(zSql);
if( rc!=SQLITE_OK && pzErrMsg ){
*pzErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pC->db));
@@ -223781,9 +225053,9 @@ SQLITE_API int sqlite3_stmt_init(
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
/************** End of stmt.c ************************************************/
-#if __LINE__!=223781
+#if __LINE__!=225056
#undef SQLITE_SOURCE_ID
-#define SQLITE_SOURCE_ID "2019-07-10 17:32:03 fc82b73eaac8b36950e527f12c4b5dc1e147e6f4ad2217ae43ad82882a88alt2"
+#define SQLITE_SOURCE_ID "2019-10-10 20:19:45 18db032d058f1436ce3dea84081f4ee5a0f2259ad97301d43c426bc7f3dfalt2"
#endif
/* Return the source-id for this library */
SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
diff --git a/src/3rdparty/sqlite/sqlite3.h b/src/3rdparty/sqlite/sqlite3.h
index a4bab0ad6b..37bfac5289 100644
--- a/src/3rdparty/sqlite/sqlite3.h
+++ b/src/3rdparty/sqlite/sqlite3.h
@@ -123,9 +123,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.29.0"
-#define SQLITE_VERSION_NUMBER 3029000
-#define SQLITE_SOURCE_ID "2019-07-10 17:32:03 fc82b73eaac8b36950e527f12c4b5dc1e147e6f4ad2217ae43ad82882a88bfa6"
+#define SQLITE_VERSION "3.30.1"
+#define SQLITE_VERSION_NUMBER 3030001
+#define SQLITE_SOURCE_ID "2019-10-10 20:19:45 18db032d058f1436ce3dea84081f4ee5a0f2259ad97301d43c426bc7f3df1b0b"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -2093,6 +2093,17 @@ struct sqlite3_mem_methods {
** following this call. The second parameter may be a NULL pointer, in
** which case the trigger setting is not reported back. </dd>
**
+** [[SQLITE_DBCONFIG_ENABLE_VIEW]]
+** <dt>SQLITE_DBCONFIG_ENABLE_VIEW</dt>
+** <dd> ^This option is used to enable or disable [CREATE VIEW | views].
+** There should be two additional arguments.
+** The first argument is an integer which is 0 to disable views,
+** positive to enable views or negative to leave the setting unchanged.
+** The second parameter is a pointer to an integer into which
+** is written 0 or 1 to indicate whether views are disabled or enabled
+** following this call. The second parameter may be a NULL pointer, in
+** which case the view setting is not reported back. </dd>
+**
** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
** <dd> ^This option is used to enable or disable the
@@ -2265,7 +2276,8 @@ struct sqlite3_mem_methods {
#define SQLITE_DBCONFIG_LEGACY_ALTER_TABLE 1012 /* int int* */
#define SQLITE_DBCONFIG_DQS_DML 1013 /* int int* */
#define SQLITE_DBCONFIG_DQS_DDL 1014 /* int int* */
-#define SQLITE_DBCONFIG_MAX 1014 /* Largest DBCONFIG */
+#define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */
+#define SQLITE_DBCONFIG_MAX 1015 /* Largest DBCONFIG */
/*
** CAPI3REF: Enable Or Disable Extended Result Codes
@@ -3814,7 +3826,7 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
** ^The specific value of WHERE-clause [parameter] might influence the
** choice of query plan if the parameter is the left-hand side of a [LIKE]
** or [GLOB] operator or if the parameter is compared to an indexed column
-** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled.
+** and the [SQLITE_ENABLE_STAT4] compile-time option is enabled.
** </li>
** </ol>
**
@@ -4849,6 +4861,12 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
** perform additional optimizations on deterministic functions, so use
** of the [SQLITE_DETERMINISTIC] flag is recommended where possible.
**
+** ^The fourth parameter may also optionally include the [SQLITE_DIRECTONLY]
+** flag, which if present prevents the function from being invoked from
+** within VIEWs or TRIGGERs. For security reasons, the [SQLITE_DIRECTONLY]
+** flag is recommended for any application-defined SQL function that has
+** side-effects.
+**
** ^(The fifth parameter is an arbitrary pointer. The implementation of the
** function can gain access to this pointer using [sqlite3_user_data()].)^
**
@@ -4965,8 +4983,30 @@ SQLITE_API int sqlite3_create_window_function(
** [SQLITE_UTF8 | preferred text encoding] as the fourth argument
** to [sqlite3_create_function()], [sqlite3_create_function16()], or
** [sqlite3_create_function_v2()].
+**
+** The SQLITE_DETERMINISTIC flag means that the new function will always
+** maps the same inputs into the same output. The abs() function is
+** deterministic, for example, but randomblob() is not.
+**
+** The SQLITE_DIRECTONLY flag means that the function may only be invoked
+** from top-level SQL, and cannot be used in VIEWs or TRIGGERs. This is
+** a security feature which is recommended for all
+** [application-defined SQL functions] that have side-effects. This flag
+** prevents an attacker from adding triggers and views to a schema then
+** tricking a high-privilege application into causing unintended side-effects
+** while performing ordinary queries.
+**
+** The SQLITE_SUBTYPE flag indicates to SQLite that a function may call
+** [sqlite3_value_subtype()] to inspect the sub-types of its arguments.
+** Specifying this flag makes no difference for scalar or aggregate user
+** functions. However, if it is not specified for a user-defined window
+** function, then any sub-types belonging to arguments passed to the window
+** function may be discarded before the window function is called (i.e.
+** sqlite3_value_subtype() will always return 0).
*/
-#define SQLITE_DETERMINISTIC 0x800
+#define SQLITE_DETERMINISTIC 0x000000800
+#define SQLITE_DIRECTONLY 0x000080000
+#define SQLITE_SUBTYPE 0x000100000
/*
** CAPI3REF: Deprecated Functions
@@ -6612,6 +6652,12 @@ struct sqlite3_index_info {
** ^The sqlite3_create_module()
** interface is equivalent to sqlite3_create_module_v2() with a NULL
** destructor.
+**
+** ^If the third parameter (the pointer to the sqlite3_module object) is
+** NULL then no new module is create and any existing modules with the
+** same name are dropped.
+**
+** See also: [sqlite3_drop_modules()]
*/
SQLITE_API int sqlite3_create_module(
sqlite3 *db, /* SQLite connection to register module with */
@@ -6628,6 +6674,23 @@ SQLITE_API int sqlite3_create_module_v2(
);
/*
+** CAPI3REF: Remove Unnecessary Virtual Table Implementations
+** METHOD: sqlite3
+**
+** ^The sqlite3_drop_modules(D,L) interface removes all virtual
+** table modules from database connection D except those named on list L.
+** The L parameter must be either NULL or a pointer to an array of pointers
+** to strings where the array is terminated by a single NULL pointer.
+** ^If the L parameter is NULL, then all virtual table modules are removed.
+**
+** See also: [sqlite3_create_module()]
+*/
+SQLITE_API int sqlite3_drop_modules(
+ sqlite3 *db, /* Remove modules from this connection */
+ const char **azKeep /* Except, do not remove the ones named here */
+);
+
+/*
** CAPI3REF: Virtual Table Instance Object
** KEYWORDS: sqlite3_vtab
**
@@ -7335,7 +7398,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_FIRST 5
#define SQLITE_TESTCTRL_PRNG_SAVE 5
#define SQLITE_TESTCTRL_PRNG_RESTORE 6
-#define SQLITE_TESTCTRL_PRNG_RESET 7
+#define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */
#define SQLITE_TESTCTRL_BITVEC_TEST 8
#define SQLITE_TESTCTRL_FAULT_INSTALL 9
#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
@@ -7358,7 +7421,9 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_IMPOSTER 25
#define SQLITE_TESTCTRL_PARSER_COVERAGE 26
#define SQLITE_TESTCTRL_RESULT_INTREAL 27
-#define SQLITE_TESTCTRL_LAST 27 /* Largest TESTCTRL */
+#define SQLITE_TESTCTRL_PRNG_SEED 28
+#define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29
+#define SQLITE_TESTCTRL_LAST 29 /* Largest TESTCTRL */
/*
** CAPI3REF: SQL Keyword Checking