mirror of
https://github.com/qgis/QGIS.git
synced 2025-03-06 00:05:02 -05:00
247 lines
7.5 KiB
C++
247 lines
7.5 KiB
C++
|
//
|
||
|
// MemoryTest.cpp
|
||
|
//
|
||
|
// This demonstrates a use of RTree
|
||
|
//
|
||
|
|
||
|
// RTree
|
||
|
#include <RTree.h>
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <memory.h>
|
||
|
#ifdef WIN32
|
||
|
#include <crtdbg.h>
|
||
|
#endif //WIN32
|
||
|
|
||
|
#include <random>
|
||
|
|
||
|
// Use CRT Debug facility to dump memory leaks on app exit
|
||
|
#ifdef WIN32
|
||
|
// These two are for MSVS 2005 security consciousness until safe std lib funcs are available
|
||
|
#pragma warning(disable : 4996) // Deprecated functions
|
||
|
#define _CRT_SECURE_NO_DEPRECATE // Allow old unsecure standard library functions, Disable some 'warning C4996 - function was deprecated'
|
||
|
|
||
|
// The following macros set and clear, respectively, given bits
|
||
|
// of the C runtime library debug flag, as specified by a bitmask.
|
||
|
#ifdef _DEBUG
|
||
|
#define SET_CRT_DEBUG_FIELD(a) \
|
||
|
_CrtSetDbgFlag((a) | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG))
|
||
|
#define CLEAR_CRT_DEBUG_FIELD(a) \
|
||
|
_CrtSetDbgFlag(~(a) & _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG))
|
||
|
#else
|
||
|
#define SET_CRT_DEBUG_FIELD(a) ((void) 0)
|
||
|
#define CLEAR_CRT_DEBUG_FIELD(a) ((void) 0)
|
||
|
#endif
|
||
|
#endif //WIN32
|
||
|
|
||
|
//
|
||
|
// Get a random float b/n two values
|
||
|
// The returned value is >= min && < max (exclusive of max)
|
||
|
//
|
||
|
static std::random_device sRandomDevice;
|
||
|
static std::mt19937 sMT(sRandomDevice());
|
||
|
static float RandFloat(float a_min, float a_max)
|
||
|
{
|
||
|
std::uniform_real_distribution<float> distribution(a_min, a_max);
|
||
|
return distribution(sMT);
|
||
|
}
|
||
|
|
||
|
|
||
|
/// Simplify handling of 3 dimensional coordinate
|
||
|
struct Vec3
|
||
|
{
|
||
|
/// Default constructor
|
||
|
Vec3() noexcept = default;
|
||
|
|
||
|
/// Construct from three elements
|
||
|
constexpr Vec3(float a_x, float a_y, float a_z) noexcept
|
||
|
: fVal{ a_x, a_y, a_z }
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/// Add two vectors and return result
|
||
|
Vec3 operator+ (const Vec3& a_other) const
|
||
|
{
|
||
|
return Vec3(fVal[0] + a_other.fVal[0],
|
||
|
fVal[1] + a_other.fVal[1],
|
||
|
fVal[2] + a_other.fVal[2]);
|
||
|
}
|
||
|
|
||
|
float fVal[3] = { 0.0f, }; ///< 3 float components for axes or dimensions
|
||
|
};
|
||
|
|
||
|
|
||
|
static bool BoxesIntersect(const Vec3& a_boxMinA, const Vec3& a_boxMaxA,
|
||
|
const Vec3& a_boxMinB, const Vec3& a_boxMaxB)
|
||
|
{
|
||
|
if (a_boxMinA.fVal[0] > a_boxMaxB.fVal[0] || a_boxMaxA.fVal[0] < a_boxMinB.fVal[0])
|
||
|
return false;
|
||
|
if (a_boxMinA.fVal[1] > a_boxMaxB.fVal[1] || a_boxMaxA.fVal[1] < a_boxMinB.fVal[1])
|
||
|
return false;
|
||
|
if (a_boxMinA.fVal[2] > a_boxMaxB.fVal[2] || a_boxMaxA.fVal[2] < a_boxMinB.fVal[2])
|
||
|
return false;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/// A user type to test with, instead of a simple type such as an 'int'
|
||
|
struct SomeThing
|
||
|
{
|
||
|
SomeThing()
|
||
|
{
|
||
|
++s_outstandingAllocs;
|
||
|
}
|
||
|
~SomeThing()
|
||
|
{
|
||
|
--s_outstandingAllocs;
|
||
|
}
|
||
|
|
||
|
int m_creationCounter; ///< Just a number for identifying within test program
|
||
|
Vec3 m_min, m_max; ///< Minimal bounding rect, values must be known and constant in order to remove from RTree
|
||
|
|
||
|
static int s_outstandingAllocs; ///< Count how many outstanding objects remain
|
||
|
};
|
||
|
|
||
|
/// Init static
|
||
|
int SomeThing::s_outstandingAllocs = 0;
|
||
|
|
||
|
|
||
|
/// A callback function to obtain query results in this implementation
|
||
|
bool QueryResultCallback(SomeThing* a_data)
|
||
|
{
|
||
|
printf("search found %d\n", a_data->m_creationCounter);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
int main(int argc, char* argv[])
|
||
|
{
|
||
|
constexpr const int NUM_OBJECTS = 40; // Number of objects in test set
|
||
|
constexpr const int FRAC_OBJECTS = 4;
|
||
|
static_assert(NUM_OBJECTS > FRAC_OBJECTS, "NUM_OBJECTS must be bigger than FRAC_OBJECTS for this test");
|
||
|
|
||
|
constexpr const float MAX_WORLDSIZE = 10.0f;
|
||
|
constexpr const float FRAC_WORLDSIZE = MAX_WORLDSIZE / 2;
|
||
|
|
||
|
// typedef the RTree useage just for conveniance with iteration
|
||
|
using SomeThingTree = RTree<SomeThing*, float, 3>;
|
||
|
|
||
|
SomeThing* thingArray[NUM_OBJECTS * 2] = { nullptr, }; // Store objects in another container to test with, sized larger than we need
|
||
|
|
||
|
// Create intance of RTree
|
||
|
|
||
|
SomeThingTree tree;
|
||
|
|
||
|
|
||
|
// Add some nodes
|
||
|
int counter = 0;
|
||
|
for (int index = 0; index < NUM_OBJECTS; ++index)
|
||
|
{
|
||
|
auto newThing = new SomeThing{};
|
||
|
|
||
|
newThing->m_creationCounter = counter++;
|
||
|
newThing->m_min = Vec3(RandFloat(-MAX_WORLDSIZE, MAX_WORLDSIZE), RandFloat(-MAX_WORLDSIZE, MAX_WORLDSIZE), RandFloat(-MAX_WORLDSIZE, MAX_WORLDSIZE));
|
||
|
Vec3 extent = Vec3(RandFloat(0, FRAC_WORLDSIZE), RandFloat(0, FRAC_WORLDSIZE), RandFloat(0, FRAC_WORLDSIZE));
|
||
|
newThing->m_max = newThing->m_min + extent;
|
||
|
|
||
|
thingArray[counter - 1] = newThing;
|
||
|
|
||
|
tree.Insert(newThing->m_min.fVal, newThing->m_max.fVal, newThing);
|
||
|
printf("inserting %d\n", newThing->m_creationCounter);
|
||
|
}
|
||
|
|
||
|
printf("tree count = %d\n", tree.Count());
|
||
|
|
||
|
int numToDelete = NUM_OBJECTS / FRAC_OBJECTS;
|
||
|
int numToStep = FRAC_OBJECTS;
|
||
|
|
||
|
// Delete some nodes
|
||
|
for (int index = 0; index < NUM_OBJECTS; index += numToStep)
|
||
|
{
|
||
|
auto curThing = thingArray[index];
|
||
|
|
||
|
if (curThing)
|
||
|
{
|
||
|
tree.Remove(curThing->m_min.fVal, curThing->m_max.fVal, curThing);
|
||
|
printf("removing %d\n", curThing->m_creationCounter);
|
||
|
|
||
|
delete curThing;
|
||
|
thingArray[index] = nullptr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
printf("tree count = %d\n", tree.Count());
|
||
|
|
||
|
// Add some more nodes
|
||
|
for (int index = 0; index < numToDelete; ++index)
|
||
|
{
|
||
|
auto newThing = new SomeThing{};
|
||
|
|
||
|
newThing->m_creationCounter = counter++;
|
||
|
newThing->m_min = Vec3(RandFloat(-MAX_WORLDSIZE, MAX_WORLDSIZE), RandFloat(-MAX_WORLDSIZE, MAX_WORLDSIZE), RandFloat(-MAX_WORLDSIZE, MAX_WORLDSIZE));
|
||
|
Vec3 extent = Vec3(RandFloat(0, FRAC_WORLDSIZE), RandFloat(0, FRAC_WORLDSIZE), RandFloat(0, FRAC_WORLDSIZE));
|
||
|
newThing->m_max = newThing->m_min + extent;
|
||
|
|
||
|
thingArray[counter - 1] = newThing;
|
||
|
|
||
|
tree.Insert(newThing->m_min.fVal, newThing->m_max.fVal, newThing);
|
||
|
printf("inserting %d\n", newThing->m_creationCounter);
|
||
|
}
|
||
|
|
||
|
printf("tree count = %d\n", tree.Count());
|
||
|
|
||
|
Vec3 searchMin(0, 0, 0);
|
||
|
Vec3 searchMax(FRAC_WORLDSIZE, FRAC_WORLDSIZE, FRAC_WORLDSIZE);
|
||
|
tree.Search(searchMin.fVal, searchMax.fVal, &QueryResultCallback);
|
||
|
|
||
|
// NOTE: Even better than just dumping text, it would be nice to render the
|
||
|
// tree contents and search result for visualization.
|
||
|
|
||
|
|
||
|
// List values. Iterator is NOT delete safe
|
||
|
SomeThingTree::Iterator it;
|
||
|
for (tree.GetFirst(it); !tree.IsNull(it); tree.GetNext(it))
|
||
|
{
|
||
|
SomeThing* curThing = tree.GetAt(it);
|
||
|
|
||
|
if (BoxesIntersect(searchMin, searchMax, curThing->m_min, curThing->m_max))
|
||
|
{
|
||
|
printf("brute found %d\n", curThing->m_creationCounter);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Delete our nodes, NOTE, we are NOT deleting the tree nodes, just our data
|
||
|
// of course the tree will now contain invalid pointers that must not be used any more.
|
||
|
for (tree.GetFirst(it); !tree.IsNull(it); tree.GetNext(it))
|
||
|
{
|
||
|
SomeThing* removeElem = tree.GetAt(it);
|
||
|
if (removeElem)
|
||
|
{
|
||
|
printf("deleting %d\n", removeElem->m_creationCounter);
|
||
|
delete removeElem;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Remove all contents (This would have happened automatically during destructor)
|
||
|
tree.RemoveAll();
|
||
|
|
||
|
if (SomeThing::s_outstandingAllocs > 0)
|
||
|
{
|
||
|
printf("Memory leak!\n");
|
||
|
printf("s_outstandingAllocs = %d\n", SomeThing::s_outstandingAllocs);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
printf("No memory leaks detected by app\n");
|
||
|
}
|
||
|
|
||
|
#ifdef WIN32
|
||
|
// Use CRT Debug facility to dump memory leaks on app exit
|
||
|
SET_CRT_DEBUG_FIELD(_CRTDBG_LEAK_CHECK_DF);
|
||
|
#endif //WIN32
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|