Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
815 lines
15 KiB
C++
815 lines
15 KiB
C++
// basisu_containers_impl.h
|
|
// Do not include directly
|
|
|
|
#include <ctype.h>
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma warning (disable:4127) // warning C4127: conditional expression is constant
|
|
#endif
|
|
|
|
namespace basisu
|
|
{
|
|
// A container operation has internally panicked in an unrecoverable way.
|
|
// Either an allocation has failed, or a range or consistency check has failed.
|
|
#ifdef _MSC_VER
|
|
__declspec(noreturn)
|
|
#else
|
|
[[noreturn]]
|
|
#endif
|
|
void container_abort(const char* pMsg, ...)
|
|
{
|
|
assert(0);
|
|
|
|
va_list args;
|
|
va_start(args, pMsg);
|
|
|
|
char buf[1024] = {};
|
|
|
|
#ifdef _MSC_VER
|
|
vsprintf_s(buf, sizeof(buf), pMsg, args);
|
|
#else
|
|
vsnprintf(buf, sizeof(buf), pMsg, args);
|
|
#endif
|
|
va_end(args);
|
|
|
|
fputs(buf, stderr);
|
|
|
|
std::terminate();
|
|
}
|
|
|
|
bool elemental_vector::increase_capacity(size_t min_new_capacity, bool grow_hint, size_t element_size, object_mover pMover, bool nofail_flag)
|
|
{
|
|
assert(m_size <= m_capacity);
|
|
assert(min_new_capacity >= m_size);
|
|
assert(element_size);
|
|
|
|
// Basic sanity check min_new_capacity
|
|
if (!can_fit_into_size_t((uint64_t)min_new_capacity * element_size))
|
|
{
|
|
assert(0);
|
|
|
|
if (nofail_flag)
|
|
return false;
|
|
|
|
container_abort("elemental_vector::increase_capacity: requesting too many elements\n");
|
|
}
|
|
|
|
// Check for sane library limits
|
|
if (sizeof(void*) == sizeof(uint64_t))
|
|
{
|
|
// 16 GB
|
|
assert(min_new_capacity < (0x400000000ULL / element_size));
|
|
}
|
|
else
|
|
{
|
|
// ~1.99 GB
|
|
assert(min_new_capacity < (0x7FFF0000U / element_size));
|
|
}
|
|
|
|
// If vector is already large enough just return.
|
|
if (m_capacity >= min_new_capacity)
|
|
return true;
|
|
|
|
uint64_t new_capacity_u64 = min_new_capacity;
|
|
|
|
if ((grow_hint) && (!helpers::is_power_of_2(new_capacity_u64)))
|
|
{
|
|
new_capacity_u64 = helpers::next_pow2(new_capacity_u64);
|
|
|
|
if (!can_fit_into_size_t(new_capacity_u64))
|
|
{
|
|
assert(0);
|
|
|
|
if (nofail_flag)
|
|
return false;
|
|
|
|
container_abort("elemental_vector::increase_capacity: vector too large\n");
|
|
}
|
|
}
|
|
|
|
const uint64_t desired_size_u64 = element_size * new_capacity_u64;
|
|
|
|
if (!can_fit_into_size_t(desired_size_u64))
|
|
{
|
|
assert(0);
|
|
|
|
if (nofail_flag)
|
|
return false;
|
|
|
|
container_abort("elemental_vector::increase_capacity: vector too large\n");
|
|
}
|
|
|
|
const size_t desired_size = static_cast<size_t>(desired_size_u64);
|
|
|
|
size_t actual_size = 0;
|
|
BASISU_NOTE_UNUSED(actual_size);
|
|
|
|
if (!pMover)
|
|
{
|
|
void* new_p = realloc(m_p, desired_size);
|
|
if (!new_p)
|
|
{
|
|
assert(0);
|
|
|
|
if (nofail_flag)
|
|
return false;
|
|
|
|
container_abort("elemental_vector::increase_capacity: realloc() failed allocating %zu bytes", desired_size);
|
|
}
|
|
|
|
#if BASISU_VECTOR_DETERMINISTIC
|
|
actual_size = desired_size;
|
|
#elif defined(_MSC_VER)
|
|
actual_size = _msize(new_p);
|
|
#elif HAS_MALLOC_USABLE_SIZE
|
|
actual_size = malloc_usable_size(new_p);
|
|
#else
|
|
actual_size = desired_size;
|
|
#endif
|
|
m_p = new_p;
|
|
}
|
|
else
|
|
{
|
|
void* new_p = malloc(desired_size);
|
|
if (!new_p)
|
|
{
|
|
assert(0);
|
|
if (nofail_flag)
|
|
return false;
|
|
|
|
container_abort("elemental_vector::increase_capacity: malloc() failed allocating %zu bytes", desired_size);
|
|
}
|
|
|
|
#if BASISU_VECTOR_DETERMINISTIC
|
|
actual_size = desired_size;
|
|
#elif defined(_MSC_VER)
|
|
actual_size = _msize(new_p);
|
|
#elif HAS_MALLOC_USABLE_SIZE
|
|
actual_size = malloc_usable_size(new_p);
|
|
#else
|
|
actual_size = desired_size;
|
|
#endif
|
|
|
|
(*pMover)(new_p, m_p, m_size);
|
|
|
|
if (m_p)
|
|
free(m_p);
|
|
|
|
m_p = new_p;
|
|
}
|
|
|
|
#if BASISU_VECTOR_DETERMINISTIC
|
|
m_capacity = static_cast<size_t>(new_capacity_u64);
|
|
#else
|
|
if (actual_size > desired_size)
|
|
m_capacity = static_cast<size_t>(actual_size / element_size);
|
|
else
|
|
m_capacity = static_cast<size_t>(new_capacity_u64);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
#if BASISU_HASHMAP_TEST
|
|
|
|
#define HASHMAP_TEST_VERIFY(c) do { if (!(c)) handle_hashmap_test_verify_failure(__LINE__); } while(0)
|
|
|
|
static void handle_hashmap_test_verify_failure(int line)
|
|
{
|
|
container_abort("HASHMAP_TEST_VERIFY() faild on line %i\n", line);
|
|
}
|
|
|
|
class counted_obj
|
|
{
|
|
public:
|
|
counted_obj(uint32_t v = 0) :
|
|
m_val(v)
|
|
{
|
|
m_count++;
|
|
}
|
|
|
|
counted_obj(const counted_obj& obj) :
|
|
m_val(obj.m_val)
|
|
{
|
|
if (m_val != UINT64_MAX)
|
|
m_count++;
|
|
}
|
|
|
|
counted_obj(counted_obj&& obj) :
|
|
m_val(obj.m_val)
|
|
{
|
|
obj.m_val = UINT64_MAX;
|
|
}
|
|
|
|
counted_obj& operator= (counted_obj&& rhs)
|
|
{
|
|
if (this != &rhs)
|
|
{
|
|
m_val = rhs.m_val;
|
|
rhs.m_val = UINT64_MAX;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
~counted_obj()
|
|
{
|
|
if (m_val != UINT64_MAX)
|
|
{
|
|
assert(m_count > 0);
|
|
m_count--;
|
|
}
|
|
}
|
|
|
|
static uint32_t m_count;
|
|
|
|
uint64_t m_val;
|
|
|
|
operator size_t() const { return (size_t)m_val; }
|
|
|
|
bool operator== (const counted_obj& rhs) const { return m_val == rhs.m_val; }
|
|
bool operator== (const uint32_t rhs) const { return m_val == rhs; }
|
|
|
|
};
|
|
|
|
uint32_t counted_obj::m_count;
|
|
|
|
static uint32_t urand32()
|
|
{
|
|
uint32_t a = rand();
|
|
uint32_t b = rand() << 15;
|
|
uint32_t c = rand() << (32 - 15);
|
|
return a ^ b ^ c;
|
|
}
|
|
|
|
static int irand32(int l, int h)
|
|
{
|
|
assert(l < h);
|
|
if (l >= h)
|
|
return l;
|
|
|
|
uint32_t range = static_cast<uint32_t>(h - l);
|
|
|
|
uint32_t rnd = urand32();
|
|
|
|
uint32_t rnd_range = static_cast<uint32_t>((((uint64_t)range) * ((uint64_t)rnd)) >> 32U);
|
|
|
|
int result = l + rnd_range;
|
|
assert((result >= l) && (result < h));
|
|
return result;
|
|
}
|
|
|
|
void hash_map_test()
|
|
{
|
|
{
|
|
basisu::hash_map<uint32_t> s;
|
|
uint_vec k;
|
|
|
|
for (uint32_t i = 0; i < 1000000; i++)
|
|
{
|
|
s.insert(i);
|
|
k.push_back(i);
|
|
}
|
|
|
|
for (uint32_t i = 0; i < k.size(); i++)
|
|
{
|
|
uint32_t r = rand() ^ (rand() << 15);
|
|
|
|
uint32_t j = i + (r % (k.size() - i));
|
|
|
|
std::swap(k[i], k[j]);
|
|
}
|
|
|
|
basisu::hash_map<uint32_t> s1(s);
|
|
|
|
for (uint32_t i = 0; i < 1000000; i++)
|
|
{
|
|
auto res = s.find(i);
|
|
HASHMAP_TEST_VERIFY(res != s.end());
|
|
HASHMAP_TEST_VERIFY(res->first == i);
|
|
s.erase(i);
|
|
}
|
|
|
|
for (uint32_t it = 0; it < 1000000; it++)
|
|
{
|
|
uint32_t i = k[it];
|
|
|
|
auto res = s1.find(i);
|
|
HASHMAP_TEST_VERIFY(res != s.end());
|
|
HASHMAP_TEST_VERIFY(res->first == i);
|
|
s1.erase(i);
|
|
}
|
|
|
|
for (uint32_t i = 0; i < 1000000; i++)
|
|
{
|
|
auto res = s.find(i);
|
|
HASHMAP_TEST_VERIFY(res == s.end());
|
|
|
|
auto res1 = s1.find(i);
|
|
HASHMAP_TEST_VERIFY(res1 == s1.end());
|
|
}
|
|
|
|
HASHMAP_TEST_VERIFY(s.empty());
|
|
HASHMAP_TEST_VERIFY(s1.empty());
|
|
}
|
|
|
|
{
|
|
typedef basisu::hash_map< uint32_t, basisu::vector<uint32_t> > hm;
|
|
hm q;
|
|
|
|
basisu::vector<uint32_t> a, b;
|
|
a.push_back(1);
|
|
b.push_back(2);
|
|
b.push_back(3);
|
|
|
|
basisu::vector<uint32_t> c(b);
|
|
|
|
hm::insert_result ir;
|
|
q.try_insert(ir, 1, std::move(a));
|
|
q.try_insert(ir, 2, std::move(b));
|
|
q.try_insert(ir, std::make_pair(3, c));
|
|
}
|
|
|
|
{
|
|
typedef basisu::hash_map<counted_obj, counted_obj> my_hash_map;
|
|
my_hash_map m;
|
|
counted_obj a, b;
|
|
m.insert(std::move(a), std::move(b));
|
|
}
|
|
|
|
{
|
|
basisu::hash_map<uint64_t, uint64_t> k;
|
|
basisu::hash_map<uint64_t, uint64_t> l;
|
|
std::swap(k, l);
|
|
|
|
k.begin();
|
|
k.end();
|
|
k.clear();
|
|
k.empty();
|
|
k.erase(0);
|
|
k.insert(0, 1);
|
|
k.find(0);
|
|
k.get_equals();
|
|
k.get_hasher();
|
|
k.get_table_size();
|
|
k.reset();
|
|
k.reserve(1);
|
|
k = l;
|
|
k.set_equals(l.get_equals());
|
|
k.set_hasher(l.get_hasher());
|
|
k.get_table_size();
|
|
}
|
|
|
|
uint32_t seed = 0;
|
|
for (; ; )
|
|
{
|
|
seed++;
|
|
|
|
typedef basisu::hash_map<counted_obj, counted_obj> my_hash_map;
|
|
my_hash_map m;
|
|
|
|
const uint32_t n = irand32(1, 100000);
|
|
|
|
printf("%u\n", n);
|
|
|
|
srand(seed); // r1.seed(seed);
|
|
|
|
basisu::vector<int> q;
|
|
|
|
uint32_t count = 0;
|
|
for (uint32_t i = 0; i < n; i++)
|
|
{
|
|
uint32_t v = urand32() & 0x7FFFFFFF;
|
|
my_hash_map::insert_result res = m.insert(counted_obj(v), counted_obj(v ^ 0xdeadbeef));
|
|
if (res.second)
|
|
{
|
|
count++;
|
|
q.push_back(v);
|
|
}
|
|
}
|
|
|
|
HASHMAP_TEST_VERIFY(m.size() == count);
|
|
|
|
srand(seed);
|
|
|
|
my_hash_map cm(m);
|
|
m.clear();
|
|
m = cm;
|
|
cm.reset();
|
|
|
|
for (uint32_t i = 0; i < n; i++)
|
|
{
|
|
uint32_t v = urand32() & 0x7FFFFFFF;
|
|
my_hash_map::const_iterator it = m.find(counted_obj(v));
|
|
HASHMAP_TEST_VERIFY(it != m.end());
|
|
HASHMAP_TEST_VERIFY(it->first == v);
|
|
HASHMAP_TEST_VERIFY(it->second == (v ^ 0xdeadbeef));
|
|
}
|
|
|
|
for (uint32_t t = 0; t < 2; t++)
|
|
{
|
|
const uint32_t nd = irand32(1, q.size_u32() + 1);
|
|
for (uint32_t i = 0; i < nd; i++)
|
|
{
|
|
uint32_t p = irand32(0, q.size_u32());
|
|
|
|
int k = q[p];
|
|
if (k >= 0)
|
|
{
|
|
q[p] = -k - 1;
|
|
|
|
bool s = m.erase(counted_obj(k));
|
|
HASHMAP_TEST_VERIFY(s);
|
|
}
|
|
}
|
|
|
|
typedef basisu::hash_map<uint32_t, empty_type> uint_hash_set;
|
|
uint_hash_set s;
|
|
|
|
for (uint32_t i = 0; i < q.size(); i++)
|
|
{
|
|
int v = q[i];
|
|
|
|
if (v >= 0)
|
|
{
|
|
my_hash_map::const_iterator it = m.find(counted_obj(v));
|
|
HASHMAP_TEST_VERIFY(it != m.end());
|
|
HASHMAP_TEST_VERIFY(it->first == (uint32_t)v);
|
|
HASHMAP_TEST_VERIFY(it->second == ((uint32_t)v ^ 0xdeadbeef));
|
|
|
|
s.insert(v);
|
|
}
|
|
else
|
|
{
|
|
my_hash_map::const_iterator it = m.find(counted_obj(-v - 1));
|
|
HASHMAP_TEST_VERIFY(it == m.end());
|
|
}
|
|
}
|
|
|
|
uint32_t found_count = 0;
|
|
for (my_hash_map::const_iterator it = m.begin(); it != m.end(); ++it)
|
|
{
|
|
HASHMAP_TEST_VERIFY(it->second == ((uint32_t)it->first ^ 0xdeadbeef));
|
|
|
|
uint_hash_set::const_iterator fit(s.find((uint32_t)it->first));
|
|
HASHMAP_TEST_VERIFY(fit != s.end());
|
|
|
|
HASHMAP_TEST_VERIFY(fit->first == it->first);
|
|
|
|
found_count++;
|
|
}
|
|
|
|
HASHMAP_TEST_VERIFY(found_count == s.size());
|
|
}
|
|
|
|
HASHMAP_TEST_VERIFY(counted_obj::m_count == m.size() * 2);
|
|
}
|
|
}
|
|
|
|
#endif // BASISU_HASHMAP_TEST
|
|
|
|
// String formatting
|
|
|
|
bool fmt_variant::to_string(std::string& res, std::string& fmt) const
|
|
{
|
|
res.resize(0);
|
|
|
|
// Scan for allowed formatting characters.
|
|
for (size_t i = 0; i < fmt.size(); i++)
|
|
{
|
|
const char c = fmt[i];
|
|
|
|
if (isdigit(c) || (c == '.') || (c == ' ') || (c == '#') || (c == '+') || (c == '-'))
|
|
continue;
|
|
|
|
if (isalpha(c))
|
|
{
|
|
if ((i + 1) == fmt.size())
|
|
continue;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
if (fmt.size() && (fmt.back() == 'c'))
|
|
{
|
|
if ((m_type == variant_type::cI32) || (m_type == variant_type::cU32))
|
|
{
|
|
if (m_u32 > 255)
|
|
return false;
|
|
|
|
// Explictly allowing caller to pass in a char of 0, which is ignored.
|
|
if (m_u32)
|
|
res.push_back((uint8_t)m_u32);
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
switch (m_type)
|
|
{
|
|
case variant_type::cInvalid:
|
|
{
|
|
return false;
|
|
}
|
|
case variant_type::cI32:
|
|
{
|
|
if (fmt.size())
|
|
{
|
|
int e = fmt.back();
|
|
if (isalpha(e))
|
|
{
|
|
if ((e != 'x') && (e != 'X') && (e != 'i') && (e != 'd') && (e != 'u'))
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
fmt += "i";
|
|
}
|
|
|
|
res = string_format((std::string("%") + fmt).c_str(), m_i32);
|
|
}
|
|
else
|
|
{
|
|
res = string_format("%i", m_i32);
|
|
}
|
|
break;
|
|
}
|
|
case variant_type::cU32:
|
|
{
|
|
if (fmt.size())
|
|
{
|
|
int e = fmt.back();
|
|
if (isalpha(e))
|
|
{
|
|
if ((e != 'x') && (e != 'X') && (e != 'i') && (e != 'd') && (e != 'u'))
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
fmt += "u";
|
|
}
|
|
|
|
res = string_format((std::string("%") + fmt).c_str(), m_u32);
|
|
}
|
|
else
|
|
{
|
|
res = string_format("%u", m_u32);
|
|
}
|
|
break;
|
|
}
|
|
case variant_type::cI64:
|
|
{
|
|
if (fmt.size())
|
|
{
|
|
int e = fmt.back();
|
|
if (isalpha(e))
|
|
{
|
|
if (e == 'x')
|
|
{
|
|
fmt.pop_back();
|
|
fmt += PRIx64;
|
|
}
|
|
else if (e == 'X')
|
|
{
|
|
fmt.pop_back();
|
|
fmt += PRIX64;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
fmt += PRId64;
|
|
}
|
|
|
|
res = string_format((std::string("%") + fmt).c_str(), m_i64);
|
|
}
|
|
else
|
|
{
|
|
res = string_format("%" PRId64, m_i64);
|
|
}
|
|
break;
|
|
}
|
|
case variant_type::cU64:
|
|
{
|
|
if (fmt.size())
|
|
{
|
|
int e = fmt.back();
|
|
if (isalpha(e))
|
|
{
|
|
if (e == 'x')
|
|
{
|
|
fmt.pop_back();
|
|
fmt += PRIx64;
|
|
}
|
|
else if (e == 'X')
|
|
{
|
|
fmt.pop_back();
|
|
fmt += PRIX64;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
fmt += PRIu64;
|
|
}
|
|
|
|
res = string_format((std::string("%") + fmt).c_str(), m_u64);
|
|
}
|
|
else
|
|
{
|
|
res = string_format("%" PRIu64, m_u64);
|
|
}
|
|
break;
|
|
}
|
|
case variant_type::cFlt:
|
|
{
|
|
if (fmt.size())
|
|
{
|
|
int e = fmt.back();
|
|
if (isalpha(e))
|
|
{
|
|
if ((e != 'f') && (e != 'g') && (e != 'e') && (e != 'E'))
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
fmt += "f";
|
|
}
|
|
|
|
res = string_format((std::string("%") + fmt).c_str(), m_flt);
|
|
}
|
|
else
|
|
{
|
|
res = string_format("%f", m_flt);
|
|
}
|
|
break;
|
|
}
|
|
case variant_type::cDbl:
|
|
{
|
|
if (fmt.size())
|
|
{
|
|
int e = fmt.back();
|
|
if (isalpha(e))
|
|
{
|
|
if ((e != 'f') && (e != 'g') && (e != 'e') && (e != 'E'))
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
fmt += "f";
|
|
}
|
|
|
|
res = string_format((std::string("%") + fmt).c_str(), m_dbl);
|
|
}
|
|
else
|
|
{
|
|
res = string_format("%f", m_dbl);
|
|
}
|
|
break;
|
|
}
|
|
case variant_type::cStrPtr:
|
|
{
|
|
if (fmt.size())
|
|
return false;
|
|
if (!m_pStr)
|
|
return false;
|
|
res = m_pStr;
|
|
break;
|
|
}
|
|
case variant_type::cBool:
|
|
{
|
|
if (fmt.size())
|
|
return false;
|
|
res = m_bool ? "true" : "false";
|
|
break;
|
|
}
|
|
case variant_type::cStdStr:
|
|
{
|
|
if (fmt.size())
|
|
return false;
|
|
res = m_str;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool fmt_variants(std::string& res, const char* pFmt, const fmt_variant_vec& variants)
|
|
{
|
|
res.resize(0);
|
|
|
|
// Must specify a format string
|
|
if (!pFmt)
|
|
{
|
|
assert(0);
|
|
return false;
|
|
}
|
|
|
|
// Check format string's length
|
|
const size_t fmt_len = strlen(pFmt);
|
|
if (!fmt_len)
|
|
{
|
|
if (variants.size())
|
|
{
|
|
assert(0);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Wildly estimate output length
|
|
res.reserve(fmt_len + 32);
|
|
|
|
std::string var_fmt;
|
|
var_fmt.reserve(16);
|
|
|
|
std::string tmp;
|
|
tmp.reserve(16);
|
|
|
|
size_t variant_index = 0;
|
|
bool inside_brackets = false;
|
|
const char* p = pFmt;
|
|
|
|
while (*p)
|
|
{
|
|
const uint8_t c = *p++;
|
|
|
|
if (inside_brackets)
|
|
{
|
|
if (c == '}')
|
|
{
|
|
inside_brackets = false;
|
|
|
|
if (variant_index >= variants.size())
|
|
{
|
|
assert(0);
|
|
return false;
|
|
}
|
|
|
|
if (!variants[variant_index].to_string(tmp, var_fmt))
|
|
{
|
|
assert(0);
|
|
return false;
|
|
}
|
|
|
|
res += tmp;
|
|
|
|
variant_index++;
|
|
}
|
|
else
|
|
{
|
|
// Check for forbidden formatting characters.
|
|
if ((c == '*') || (c == 'n') || (c == '%'))
|
|
{
|
|
assert(0);
|
|
return false;
|
|
}
|
|
|
|
var_fmt.push_back(c);
|
|
}
|
|
}
|
|
else if (c == '{')
|
|
{
|
|
// Check for escaped '{'
|
|
if (*p == '{')
|
|
{
|
|
res.push_back((char)c);
|
|
p++;
|
|
}
|
|
else
|
|
{
|
|
inside_brackets = true;
|
|
var_fmt.resize(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
res.push_back((char)c);
|
|
}
|
|
}
|
|
|
|
if (inside_brackets)
|
|
{
|
|
assert(0);
|
|
return false;
|
|
}
|
|
|
|
if (variant_index != variants.size())
|
|
{
|
|
assert(0);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace basisu
|