| // basisu_containers_impl.h |
| // Do not include directly |
| |
| #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 |