// basisu_containers_impl.h // Do not include directly #include #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(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(new_capacity_u64); #else if (actual_size > desired_size) m_capacity = static_cast(actual_size / element_size); else m_capacity = static_cast(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(h - l); uint32_t rnd = urand32(); uint32_t rnd_range = static_cast((((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 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 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 > hm; hm q; basisu::vector a, b; a.push_back(1); b.push_back(2); b.push_back(3); basisu::vector 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 my_hash_map; my_hash_map m; counted_obj a, b; m.insert(std::move(a), std::move(b)); } { basisu::hash_map k; basisu::hash_map 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 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 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 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