Internal optimization in traversal of BlockMetadata_Generic::m_Suballocations
diff --git a/README.md b/README.md
index 572958e..9cbb89f 100644
--- a/README.md
+++ b/README.md
@@ -40,7 +40,7 @@
 
 # Prerequisites
 
-- Self-contained C++ library in single pair of H + CPP files. No external dependencies other than standard C, C++ library and Windows SDK. Some features of C++11 used. STL containers, C++ exceptions, and RTTI are not used.
+- Self-contained C++ library in single pair of H + CPP files. No external dependencies other than standard C, C++ library and Windows SDK. Some features of C++14 used. STL containers, C++ exceptions, and RTTI are not used.
 - Object-oriented interface in a convention similar to D3D12.
 - Error handling implemented by returning `HRESULT` error codes - same way as in D3D12.
 - Interface documented using Doxygen-style comments.
diff --git a/src/D3D12MemAlloc.cpp b/src/D3D12MemAlloc.cpp
index 7075fcb..04ddd44 100644
--- a/src/D3D12MemAlloc.cpp
+++ b/src/D3D12MemAlloc.cpp
@@ -1561,6 +1561,7 @@
 

     void Remove(Item* pItem);

 

+    class reverse_iterator;

     class iterator

     {

     public:

@@ -1570,6 +1571,12 @@
         {

         }

 

+        iterator(const reverse_iterator& src) :

+            m_pList(src.m_pList),

+            m_pItem(src.m_pItem)

+        {

+        }

+

         T& operator*() const

         {

             D3D12MA_HEAVY_ASSERT(m_pItem != NULL);

@@ -1636,8 +1643,93 @@
         }

 

         friend class List<T>;

+        friend class const_iterator;

     };

+    

+    class reverse_iterator

+    {

+    public:

+        reverse_iterator() :

+            m_pList(NULL),

+            m_pItem(NULL)

+        {

+        }

 

+        reverse_iterator(const iterator& src) :

+            m_pList(src.m_pList),

+            m_pItem(src.m_pItem)

+        {

+        }

+

+        T& operator*() const

+        {

+            D3D12MA_HEAVY_ASSERT(m_pItem != NULL);

+            return m_pItem->Value;

+        }

+        T* operator->() const

+        {

+            D3D12MA_HEAVY_ASSERT(m_pItem != NULL);

+            return &m_pItem->Value;

+        }

+

+        reverse_iterator& operator++()

+        {

+            D3D12MA_HEAVY_ASSERT(m_pItem != NULL);

+            m_pItem = m_pItem->pPrev;

+            return *this;

+        }

+        reverse_iterator& operator--()

+        {

+            if(m_pItem != NULL)

+            {

+                m_pItem = m_pItem->pNext;

+            }

+            else

+            {

+                D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty());

+                m_pItem = m_pList->Front();

+            }

+            return *this;

+        }

+

+        reverse_iterator operator++(int)

+        {

+            reverse_iterator result = *this;

+            ++*this;

+            return result;

+        }

+        reverse_iterator operator--(int)

+        {

+            reverse_iterator result = *this;

+            --*this;

+            return result;

+        }

+

+        bool operator==(const reverse_iterator& rhs) const

+        {

+            D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);

+            return m_pItem == rhs.m_pItem;

+        }

+        bool operator!=(const reverse_iterator& rhs) const

+        {

+            D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);

+            return m_pItem != rhs.m_pItem;

+        }

+

+    private:

+        List<T>* m_pList;

+        Item* m_pItem;

+

+        reverse_iterator(List<T>* pList, Item* pItem) :

+            m_pList(pList),

+            m_pItem(pItem)

+        {

+        }

+

+        friend class List<T>;

+        friend class const_reverse_iterator;

+    };

+    

     class const_iterator

     {

     public:

@@ -1653,6 +1745,23 @@
         {

         }

 

+        const_iterator(const reverse_iterator& src) :

+            m_pList(src.m_pList),

+            m_pItem(src.m_pItem)

+        {

+        }

+

+        const_iterator(const const_reverse_iterator& src) :

+            m_pList(src.m_pList),

+            m_pItem(src.m_pItem)

+        {

+        }

+

+        iterator dropConst() const

+        {

+            return iterator(const_cast<List<T>*>(m_pList), const_cast<Item*>(m_pItem));

+        }

+

         const T& operator*() const

         {

             D3D12MA_HEAVY_ASSERT(m_pItem != NULL);

@@ -1721,6 +1830,106 @@
         friend class List<T>;

     };

 

+    class const_reverse_iterator

+    {

+    public:

+        const_reverse_iterator() :

+            m_pList(NULL),

+            m_pItem(NULL)

+        {

+        }

+

+        const_reverse_iterator(const iterator& src) :

+            m_pList(src.m_pList),

+            m_pItem(src.m_pItem)

+        {

+        }

+

+        const_reverse_iterator(const reverse_iterator& src) :

+            m_pList(src.m_pList),

+            m_pItem(src.m_pItem)

+        {

+        }

+

+        const_reverse_iterator(const const_iterator& src) :

+            m_pList(src.m_pList),

+            m_pItem(src.m_pItem)

+        {

+        }

+

+        reverse_iterator dropConst() const

+        {

+            return reverse_iterator(const_cast<List<T>*>(m_pList), const_cast<Item*>(m_pItem));

+        }

+

+        const T& operator*() const

+        {

+            D3D12MA_HEAVY_ASSERT(m_pItem != NULL);

+            return m_pItem->Value;

+        }

+        const T* operator->() const

+        {

+            D3D12MA_HEAVY_ASSERT(m_pItem != NULL);

+            return &m_pItem->Value;

+        }

+

+        const_reverse_iterator& operator++()

+        {

+            D3D12MA_HEAVY_ASSERT(m_pItem != NULL);

+            m_pItem = m_pItem->pPrev;

+            return *this;

+        }

+        const_reverse_iterator& operator--()

+        {

+            if(m_pItem != NULL)

+            {

+                m_pItem = m_pItem->pNext;

+            }

+            else

+            {

+                D3D12MA_HEAVY_ASSERT(!m_pList->IsEmpty());

+                m_pItem = m_pList->Front();

+            }

+            return *this;

+        }

+

+        const_reverse_iterator operator++(int)

+        {

+            const_reverse_iterator result = *this;

+            ++*this;

+            return result;

+        }

+        const_reverse_iterator operator--(int)

+        {

+            const_reverse_iterator result = *this;

+            --*this;

+            return result;

+        }

+

+        bool operator==(const const_reverse_iterator& rhs) const

+        {

+            D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);

+            return m_pItem == rhs.m_pItem;

+        }

+        bool operator!=(const const_reverse_iterator& rhs) const

+        {

+            D3D12MA_HEAVY_ASSERT(m_pList == rhs.m_pList);

+            return m_pItem != rhs.m_pItem;

+        }

+

+    private:

+        const_reverse_iterator(const List<T>* pList, const Item* pItem) :

+            m_pList(pList),

+            m_pItem(pItem)

+        {

+        }

+

+        const List<T>* m_pList;

+        const Item* m_pItem;

+

+        friend class List<T>;

+    };

+

     bool empty() const { return IsEmpty(); }

     size_t size() const { return GetCount(); }

 

@@ -1733,6 +1942,15 @@
     const_iterator begin() const { return cbegin(); }

     const_iterator end() const { return cend(); }

 

+    reverse_iterator rbegin() { return reverse_iterator(this, Back()); }

+    reverse_iterator rend() { return reverse_iterator(this, NULL); }

+

+    const_reverse_iterator crbegin() const { return const_reverse_iterator(this, Back()); }

+    const_reverse_iterator crend() const { return const_reverse_iterator(this, NULL); }

+

+    const_reverse_iterator rbegin() const { return crbegin(); }

+    const_reverse_iterator rend() const { return crend(); }

+

     void clear() { Clear(); }

     void push_back(const T& value) { PushBack(value); }

     void erase(iterator it) { Remove(it.m_pItem); }

@@ -2420,6 +2638,7 @@
     Vector<SuballocationList::iterator> m_FreeSuballocationsBySize;

     ZeroInitializedRange m_ZeroInitializedRange;

 

+    SuballocationList::const_iterator FindAtOffset(UINT64 offset) const;

     bool ValidateFreeSuballocationList() const;

 

     // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.

@@ -3194,16 +3413,9 @@
 

 void BlockMetadata_Generic::GetAllocationInfo(UINT64 offset, VIRTUAL_ALLOCATION_INFO& outInfo) const

 {

-    for(const auto& suballoc : m_Suballocations)

-    {

-        if(suballoc.offset == offset)

-        {

-            outInfo.size = suballoc.size;

-            outInfo.pUserData = suballoc.userData;

-            return;

-        }

-    }

-    D3D12MA_ASSERT(0 && "Not found!");

+    Suballocation& suballoc = *FindAtOffset(offset).dropConst();

+    outInfo.size = suballoc.size;

+    outInfo.pUserData = suballoc.userData;

 }

 

 bool BlockMetadata_Generic::CreateAllocationRequest(

@@ -3319,18 +3531,7 @@
 

 void BlockMetadata_Generic::FreeAtOffset(UINT64 offset)

 {

-    for(SuballocationList::iterator suballocItem = m_Suballocations.begin();

-        suballocItem != m_Suballocations.end();

-        ++suballocItem)

-    {

-        Suballocation& suballoc = *suballocItem;

-        if(suballoc.offset == offset)

-        {

-            FreeSuballocation(suballocItem);

-            return;

-        }

-    }

-    D3D12MA_ASSERT(0 && "Not found!");

+    FreeSuballocation(FindAtOffset(offset).dropConst());

 }

 

 void BlockMetadata_Generic::Clear()

@@ -3349,6 +3550,38 @@
     m_FreeSuballocationsBySize.push_back(m_Suballocations.begin());

 }

 

+SuballocationList::const_iterator BlockMetadata_Generic::FindAtOffset(UINT64 offset) const

+{

+    const UINT64 last = m_Suballocations.crbegin()->offset;

+    if (last == offset)

+        return m_Suballocations.crbegin();

+    const UINT64 first = m_Suballocations.cbegin()->offset;

+    if (first == offset)

+        return m_Suballocations.cbegin();

+

+    const size_t suballocCount = m_Suballocations.size();

+    const UINT64 step = (last - first + m_Suballocations.cbegin()->size) / suballocCount;

+    auto findSuballocation = [&](auto begin, auto end) -> SuballocationList::const_iterator

+    {

+        for (auto suballocItem = begin;

+            suballocItem != end;

+            ++suballocItem)

+        {

+            const Suballocation& suballoc = *suballocItem;

+            if (suballoc.offset == offset)

+                return suballocItem;

+        }

+        D3D12MA_ASSERT(false && "Not found!");

+        return m_Suballocations.end();

+    };

+    // If requested offset is closer to the end of range, search from the end

+    if ((offset - first) > suballocCount * step / 2)

+    {

+        return findSuballocation(m_Suballocations.crbegin(), m_Suballocations.crend());

+    }

+    return findSuballocation(m_Suballocations.cbegin(), m_Suballocations.cend());

+}

+

 bool BlockMetadata_Generic::ValidateFreeSuballocationList() const

 {

     UINT64 lastSize = 0;

@@ -3548,15 +3781,8 @@
 

 void BlockMetadata_Generic::SetAllocationUserData(UINT64 offset, void* userData)

 {

-    for(auto& suballoc : m_Suballocations)

-    {

-        if(suballoc.offset == offset)

-        {

-            suballoc.userData = userData;

-            return;

-        }

-    }

-    D3D12MA_ASSERT(0 && "Not found!");

+    Suballocation& suballoc = *FindAtOffset(offset).dropConst();

+    suballoc.userData = userData;

 }

 

 void BlockMetadata_Generic::CalcAllocationStatInfo(StatInfo& outInfo) const