Skip to content

Commit d5a51dc

Browse files
committed
test
1 parent 81cd02b commit d5a51dc

File tree

1 file changed

+209
-8
lines changed

1 file changed

+209
-8
lines changed

include/nanoflann.hpp

Lines changed: 209 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,19 @@
5959
#include <stdexcept>
6060
#include <unordered_set>
6161
#include <vector>
62+
#include <chrono> // for std::chrono
6263

6364
/** Library version: 0xMmP (M=Major,m=minor,P=patch) */
6465
#define NANOFLANN_VERSION 0x171
6566

67+
/** Returns the current time in seconds since epoch */
68+
inline double getCurrentTime()
69+
{
70+
auto now = std::chrono::high_resolution_clock::now();
71+
auto duration = now.time_since_epoch();
72+
return std::chrono::duration_cast<std::chrono::duration<double>>(duration).count();
73+
}
74+
6675
// Avoid conflicting declaration of min/max macros in Windows headers
6776
#if !defined(NOMINMAX) && \
6877
(defined(_WIN32) || defined(_WIN32_) || defined(WIN32) || defined(_WIN64))
@@ -77,8 +86,142 @@
7786
#undef None
7887
#endif
7988

89+
// Macros to control exception handling and timeout features
90+
#ifndef NANOFLANN_DISABLE_EXCEPTIONS
91+
#define NANOFLANN_ENABLE_EXCEPTIONS 1
92+
#endif
93+
94+
#ifndef NANOFLANN_DISABLE_TIMEOUT
95+
#define NANOFLANN_ENABLE_TIMEOUT 1
96+
#endif
97+
8098
namespace nanoflann
8199
{
100+
/** @addtogroup exceptions_grp Exception classes
101+
* @{ */
102+
103+
/** Base class for all nanoflann exceptions */
104+
class Exception : public std::exception
105+
{
106+
protected:
107+
std::string message_;
108+
109+
public:
110+
explicit Exception(const std::string& msg) : message_(msg) {}
111+
112+
~Exception() noexcept override = default;
113+
114+
const char* what() const noexcept override
115+
{
116+
return message_.c_str();
117+
}
118+
119+
const std::string& getMessage() const noexcept
120+
{
121+
return message_;
122+
}
123+
};
124+
125+
/** Exception thrown when a null pointer is encountered */
126+
class NullPointerException : public Exception
127+
{
128+
public:
129+
explicit NullPointerException(const std::string& msg) : Exception(msg) {}
130+
};
131+
132+
/** Exception thrown when invalid data is encountered */
133+
class InvalidDataException : public Exception
134+
{
135+
public:
136+
explicit InvalidDataException(const std::string& msg) : Exception(msg) {}
137+
};
138+
139+
/** Exception thrown when memory allocation fails */
140+
class MemoryAllocationException : public Exception
141+
{
142+
public:
143+
explicit MemoryAllocationException(const std::string& msg) : Exception(msg) {}
144+
};
145+
146+
/** Exception thrown when an invalid parameter is provided */
147+
class InvalidParameterException : public Exception
148+
{
149+
public:
150+
explicit InvalidParameterException(const std::string& msg) : Exception(msg) {}
151+
};
152+
153+
/** Exception thrown when a search operation times out */
154+
class SearchTimeoutException : public Exception
155+
{
156+
private:
157+
size_t searchedPoints_;
158+
size_t foundResults_;
159+
160+
public:
161+
SearchTimeoutException(const std::string& msg, size_t searchedPoints, size_t foundResults)
162+
: Exception(msg), searchedPoints_(searchedPoints), foundResults_(foundResults) {}
163+
164+
size_t getSearchedPoints() const noexcept
165+
{
166+
return searchedPoints_;
167+
}
168+
169+
size_t getFoundResults() const noexcept
170+
{
171+
return foundResults_;
172+
}
173+
};
174+
175+
/** Exception thrown when concurrent modification is detected */
176+
class ConcurrentModificationException : public Exception
177+
{
178+
public:
179+
explicit ConcurrentModificationException(const std::string& msg) : Exception(msg) {}
180+
};
181+
182+
/** Exception thrown when invalid point data (NaN or infinity) is encountered */
183+
class InvalidPointDataException : public Exception
184+
{
185+
public:
186+
explicit InvalidPointDataException(const std::string& msg) : Exception(msg) {}
187+
};
188+
189+
/** @} */
190+
191+
/** @addtogroup logging_grp Logging interface
192+
* @{ */
193+
194+
/** Log level enum */
195+
enum class LogLevel
196+
{
197+
DEBUG,
198+
INFO,
199+
WARNING,
200+
ERROR
201+
};
202+
203+
/** Log callback function type */
204+
typedef std::function<void(LogLevel level, const std::string& message)> LogCallback;
205+
206+
/** Set a global log callback function */
207+
inline void setLogCallback(const LogCallback& callback)
208+
{
209+
static LogCallback globalLogCallback;
210+
globalLogCallback = callback;
211+
}
212+
213+
/** Log a message using the global log callback */
214+
inline void logMessage(LogLevel level, const std::string& message)
215+
{
216+
static LogCallback globalLogCallback;
217+
if (globalLogCallback)
218+
{
219+
globalLogCallback(level, message);
220+
}
221+
}
222+
223+
/** @}
224+
82225
/** @addtogroup nanoflann_grp nanoflann C++ library for KD-trees
83226
* @{ */
84227

@@ -837,6 +980,18 @@ struct SearchParameters
837980
bool sorted; //!< only for radius search, require neighbours sorted by
838981
//!< distance (default: true)
839982
};
983+
984+
/** Search options for KDTreeSingleIndexAdaptor::findNeighbors() with timeout support */
985+
struct SearchParametersEx : public SearchParameters
986+
{
987+
SearchParametersEx(float eps_ = 0, bool sorted_ = true, double timeout_seconds_ = 0)
988+
: SearchParameters(eps_, sorted_), timeout_seconds(timeout_seconds_), start_time(0)
989+
{
990+
}
991+
992+
double timeout_seconds; //!< maximum time allowed for search in seconds (0 = no timeout)
993+
double start_time; //!< start time of the search (internal use)
994+
};
840995
/** @} */
841996

842997
/** @addtogroup memalloc_grp Memory allocation
@@ -1723,7 +1878,16 @@ class KDTreeSingleIndexAdaptor
17231878
auto zero = static_cast<typename RESULTSET::DistanceType>(0);
17241879
assign(dists, (DIM > 0 ? DIM : Base::dim_), zero);
17251880
DistanceType dist = this->computeInitialDistances(*this, vec, dists);
1726-
searchLevel(result, vec, Base::root_node_, dist, dists, epsError);
1881+
1882+
// Check if searchParams is actually a SearchParametersEx
1883+
const SearchParametersEx* searchParamsEx = dynamic_cast<const SearchParametersEx*>(&searchParams);
1884+
double startTime = 0;
1885+
if (searchParamsEx && searchParamsEx->timeout_seconds > 0)
1886+
{
1887+
startTime = getCurrentTime();
1888+
}
1889+
1890+
searchLevel(result, vec, Base::root_node_, dist, dists, epsError, startTime, searchParamsEx ? searchParamsEx->timeout_seconds : 0);
17271891

17281892
if (searchParams.sorted) result.sort();
17291893

@@ -1885,8 +2049,15 @@ class KDTreeSingleIndexAdaptor
18852049
bool searchLevel(
18862050
RESULTSET& result_set, const ElementType* vec, const NodePtr node,
18872051
DistanceType mindist, distance_vector_t& dists,
1888-
const float epsError) const
2052+
const float epsError, const double startTime = 0, const double timeoutSeconds = 0) const
18892053
{
2054+
// Check for timeout
2055+
if (timeoutSeconds > 0 && getCurrentTime() - startTime > timeoutSeconds)
2056+
{
2057+
// Timeout reached, stop searching
2058+
return false;
2059+
}
2060+
18902061
// If this is a leaf node, then do check and return.
18912062
// If they are equal, both pointers are nullptr.
18922063
if (node->child1 == node->child2)
@@ -1895,6 +2066,13 @@ class KDTreeSingleIndexAdaptor
18952066
for (Offset i = node->node_type.lr.left;
18962067
i < node->node_type.lr.right; ++i)
18972068
{
2069+
// Check for timeout in loop
2070+
if (timeoutSeconds > 0 && getCurrentTime() - startTime > timeoutSeconds)
2071+
{
2072+
// Timeout reached, stop searching
2073+
return false;
2074+
}
2075+
18982076
const IndexType accessor = Base::vAcc_[i]; // reorder... : i;
18992077
DistanceType dist = distance_.evalMetric(
19002078
vec, accessor, (DIM > 0 ? DIM : Base::dim_));
@@ -1936,7 +2114,7 @@ class KDTreeSingleIndexAdaptor
19362114
}
19372115

19382116
/* Call recursively to search next level down. */
1939-
if (!searchLevel(result_set, vec, bestChild, mindist, dists, epsError))
2117+
if (!searchLevel(result_set, vec, bestChild, mindist, dists, epsError, startTime, timeoutSeconds))
19402118
{
19412119
// the resultset doesn't want to receive any more points, we're done
19422120
// searching!
@@ -1949,7 +2127,7 @@ class KDTreeSingleIndexAdaptor
19492127
if (mindist * epsError <= result_set.worstDist())
19502128
{
19512129
if (!searchLevel(
1952-
result_set, vec, otherChild, mindist, dists, epsError))
2130+
result_set, vec, otherChild, mindist, dists, epsError, startTime, timeoutSeconds))
19532131
{
19542132
// the resultset doesn't want to receive any more points, we're
19552133
// done searching!
@@ -2194,7 +2372,16 @@ class KDTreeSingleIndexDynamicAdaptor_
21942372
dists, (DIM > 0 ? DIM : Base::dim_),
21952373
static_cast<typename distance_vector_t::value_type>(0));
21962374
DistanceType dist = this->computeInitialDistances(*this, vec, dists);
2197-
searchLevel(result, vec, Base::root_node_, dist, dists, epsError);
2375+
2376+
// Check if searchParams is actually a SearchParametersEx
2377+
const SearchParametersEx* searchParamsEx = dynamic_cast<const SearchParametersEx*>(&searchParams);
2378+
double startTime = 0;
2379+
if (searchParamsEx && searchParamsEx->timeout_seconds > 0)
2380+
{
2381+
startTime = getCurrentTime();
2382+
}
2383+
2384+
searchLevel(result, vec, Base::root_node_, dist, dists, epsError, startTime, searchParamsEx ? searchParamsEx->timeout_seconds : 0);
21982385
return result.full();
21992386
}
22002387

@@ -2313,8 +2500,15 @@ class KDTreeSingleIndexDynamicAdaptor_
23132500
void searchLevel(
23142501
RESULTSET& result_set, const ElementType* vec, const NodePtr node,
23152502
DistanceType mindist, distance_vector_t& dists,
2316-
const float epsError) const
2503+
const float epsError, const double startTime = 0, const double timeoutSeconds = 0) const
23172504
{
2505+
// Check for timeout
2506+
if (timeoutSeconds > 0 && getCurrentTime() - startTime > timeoutSeconds)
2507+
{
2508+
// Timeout reached, stop searching
2509+
return;
2510+
}
2511+
23182512
// If this is a leaf node, then do check and return.
23192513
// If they are equal, both pointers are nullptr.
23202514
if (node->child1 == node->child2)
@@ -2323,6 +2517,13 @@ class KDTreeSingleIndexDynamicAdaptor_
23232517
for (Offset i = node->node_type.lr.left;
23242518
i < node->node_type.lr.right; ++i)
23252519
{
2520+
// Check for timeout in loop
2521+
if (timeoutSeconds > 0 && getCurrentTime() - startTime > timeoutSeconds)
2522+
{
2523+
// Timeout reached, stop searching
2524+
return;
2525+
}
2526+
23262527
const IndexType index = Base::vAcc_[i]; // reorder... : i;
23272528
if (treeIndex_[index] == -1) continue;
23282529
DistanceType dist = distance_.evalMetric(
@@ -2368,14 +2569,14 @@ class KDTreeSingleIndexDynamicAdaptor_
23682569
}
23692570

23702571
/* Call recursively to search next level down. */
2371-
searchLevel(result_set, vec, bestChild, mindist, dists, epsError);
2572+
searchLevel(result_set, vec, bestChild, mindist, dists, epsError, startTime, timeoutSeconds);
23722573

23732574
DistanceType dst = dists[idx];
23742575
mindist = mindist + cut_dist - dst;
23752576
dists[idx] = cut_dist;
23762577
if (mindist * epsError <= result_set.worstDist())
23772578
{
2378-
searchLevel(result_set, vec, otherChild, mindist, dists, epsError);
2579+
searchLevel(result_set, vec, otherChild, mindist, dists, epsError, startTime, timeoutSeconds);
23792580
}
23802581
dists[idx] = dst;
23812582
}

0 commit comments

Comments
 (0)