| // basisu_containers_impl.h | |
| // Do not include directly | |
| #include <ctype.h> | |
| #include <exception> | |
| #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) | |
| { | |
| fprintf(stderr, "elemental_vector::increase_capacity: Allocation failed!\n"); | |
| 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) | |
| { | |
| fprintf(stderr, "elemental_vector::increase_capacity: Allocation failed!\n"); | |
| 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 |