diff --git a/TightDB.vcxproj b/TightDB.vcxproj index 48ef777c353..ca315a9dead 100644 --- a/TightDB.vcxproj +++ b/TightDB.vcxproj @@ -1,4 +1,4 @@ - + @@ -222,7 +222,7 @@ Level3 ProgramDatabase CompileAsCpp - /DPTW32_STATIC_LIB /DTIGHTDB_DEBUG %(AdditionalOptions) + /DPTW32_STATIC_LIB /DTIGHTDB_DEBUG /DMAX_LIST_SIZE=1000 %(AdditionalOptions) false false 4355;4996 @@ -277,6 +277,7 @@ ProgramDatabase /DPTW32_STATIC_LIB %(AdditionalOptions) 4355;4996 + true Release\UnitTest++.lib;%(AdditionalDependencies) @@ -287,6 +288,9 @@ MachineX86 WS2_32.lib %(AdditionalOptions) + + true + @@ -439,6 +443,7 @@ true /DUSE_SSE42 %(AdditionalOptions) + true true @@ -457,6 +462,12 @@ true true + + true + true + true + true + true true @@ -481,6 +492,9 @@ true true + + true + true true @@ -541,8 +555,18 @@ true true - - + + true + true + true + true + + + true + true + true + true + @@ -551,6 +575,10 @@ + + + CppHeader + @@ -558,7 +586,10 @@ + + + @@ -620,4 +651,4 @@ - + \ No newline at end of file diff --git a/TightDB.vcxproj.filters b/TightDB.vcxproj.filters index 8ca0f85538c..067a6a11565 100644 --- a/TightDB.vcxproj.filters +++ b/TightDB.vcxproj.filters @@ -109,18 +109,12 @@ src - - src - src src - - src - src @@ -148,9 +142,6 @@ src - - src - test @@ -160,9 +151,22 @@ test + + test + + + test + test + + src + + + + test + @@ -210,9 +214,6 @@ src\include - - src\include - src\include @@ -282,12 +283,6 @@ src\include - - src\include - - - src\include - src\include @@ -342,6 +337,21 @@ test + + src\include + + + src\include + + + src\include + + + src\include + + + src\include + @@ -360,4 +370,4 @@ {4436af4e-59db-4e36-9b8c-acb6e5d85aea} - + \ No newline at end of file diff --git a/changelog.txt b/changelog.txt index 8dfe84db8f9..903b8b523e3 100644 --- a/changelog.txt +++ b/changelog.txt @@ -25,7 +25,6 @@ Format: - Group::set_shared() removed. - SharedGroup::is_valid() removed. Errors are now reported as exceptions from the constructor. - 2013-01-11 (Kristian Spangsege) + Simplified open-mode for Group constructor. - Group::is_valid() removed. Errors are now reported as exceptions from the constructor. @@ -38,6 +37,9 @@ Format: + Mixed::set_int() added (same for other value types except subtables). + Removed two-argument Mixed constructor for binary data since its signature is expected to be used for strings that are not zero-terminated. +2013-01-08 (Brian Munkholm) +---------- ++ New: Added a bunch of methods to support two new column types: float and double. 2012-12-16 (Kristian Spangsege) ---------- diff --git a/doc/development/query_engine.docx b/doc/development/query_engine.docx new file mode 100644 index 00000000000..f98b1bba0a3 Binary files /dev/null and b/doc/development/query_engine.docx differ diff --git a/doc/development/query_engine.pdf b/doc/development/query_engine.pdf new file mode 100644 index 00000000000..b3f3227d930 Binary files /dev/null and b/doc/development/query_engine.pdf differ diff --git a/src/tightdb/alloc_slab.cpp b/src/tightdb/alloc_slab.cpp index 1aeddcf37dc..0e7463962e8 100644 --- a/src/tightdb/alloc_slab.cpp +++ b/src/tightdb/alloc_slab.cpp @@ -27,7 +27,6 @@ size_t GetSizeFromHeader(void* p) const size_t count = (header[1] << 16) + (header[2] << 8) + header[3]; const size_t wt = (header[0] & 0x18) >> 3; // Array::WidthType - // Calculate bytes used by array size_t bytes = 0; if (wt == 0) { // TDB_BITS const size_t bits = (count * width); diff --git a/src/tightdb/alloc_slab.hpp b/src/tightdb/alloc_slab.hpp index f8f2e32173d..36656e2907e 100644 --- a/src/tightdb/alloc_slab.hpp +++ b/src/tightdb/alloc_slab.hpp @@ -147,7 +147,7 @@ inline SlabAlloc::SlabAlloc() inline bool SlabAlloc::is_attached() const TIGHTDB_NOEXCEPT { - return m_data; + return (m_data != NULL); } } // namespace tightdb diff --git a/src/tightdb/array.cpp b/src/tightdb/array.cpp index 55c2ccf4ca7..05b9ed8eecc 100644 --- a/src/tightdb/array.cpp +++ b/src/tightdb/array.cpp @@ -18,103 +18,9 @@ using namespace std; -namespace { - -const size_t initial_capacity = 128; - -inline void set_header_isnode(bool value, void* header) -{ - uint8_t* const header2 = reinterpret_cast(header); - header2[0] = (header2[0] & ~0x80) | uint8_t(value << 7); -} - -inline void set_header_hasrefs(bool value, void* header) -{ - uint8_t* const header2 = reinterpret_cast(header); - header2[0] = (header2[0] & ~0x40) | uint8_t(value << 6); -} - -inline void set_header_indexflag(bool value, void* header) -{ - uint8_t* const header2 = reinterpret_cast(header); - header2[0] = (header2[0] & ~0x20) | uint8_t(value << 5); -} - -inline void set_header_wtype(int value, void* header) -{ - // Indicates how to calculate size in bytes based on width - // 0: bits (width/8) * length - // 1: multiply width * length - // 2: ignore 1 * length - uint8_t* const header2 = reinterpret_cast(header); - header2[0] = (header2[0] & ~0x18) | uint8_t(value << 3); -} - -inline void set_header_width(size_t value, void* header) -{ - // Pack width in 3 bits (log2) - size_t w = 0; - size_t b = size_t(value); - while (b) {++w; b >>= 1;} - TIGHTDB_ASSERT(w < 8); - - uint8_t* const header2 = reinterpret_cast(header); - header2[0] = (header2[0] & ~0x7) | uint8_t(w); -} - -inline void set_header_len(size_t value, void* header) -{ - TIGHTDB_ASSERT(value <= 0xFFFFFF); - uint8_t* const header2 = reinterpret_cast(header); - header2[1] = (value >> 16) & 0x000000FF; - header2[2] = (value >> 8) & 0x000000FF; - header2[3] = value & 0x000000FF; -} - -inline void set_header_capacity(size_t value, void* header) -{ - TIGHTDB_ASSERT(value <= 0xFFFFFF); - uint8_t* const header2 = reinterpret_cast(header); - header2[4] = (value >> 16) & 0x000000FF; - header2[5] = (value >> 8) & 0x000000FF; - header2[6] = value & 0x000000FF; -} - - -inline void init_header(void* header, bool is_node, bool has_refs, int width_type, - size_t width, size_t length, size_t capacity) -{ - // Note: Since the header layout contains unallocated - // bit and/or bytes, it is important that we put the - // entire 8 byte header into a well defined state - // initially. Note also: The C++ standard does not - // guarantee that int64_t is extactly 8 bytes wide. It - // may be more, and it may be less. That is why we - // need the static assert. - TIGHTDB_STATIC_ASSERT(sizeof(int64_t) == 8, - "Trouble if int64_t is not 8 bytes wide"); - *reinterpret_cast(header) = 0; - set_header_isnode(is_node, header); - set_header_hasrefs(has_refs, header); - set_header_wtype(width_type, header); - set_header_width(width, header); - set_header_len(length, header); - set_header_capacity(capacity, header); -} - - -} // anonymous namespace - - namespace tightdb { -bool tightdb_dummy (int64_t t) -{ - (void)t; - return true; -} - // Header format (8 bytes): // |--------|--------|--------|--------|--------|--------|--------|--------| // |12344555| length | capacity |reserved| @@ -130,37 +36,37 @@ bool IsArrayIndexNode(size_t ref, const Allocator& alloc) void Array::set_header_isnode(bool value) { - ::set_header_isnode(value, m_data - 8); + set_header_isnode(value, m_data - 8); } void Array::set_header_hasrefs(bool value) { - ::set_header_hasrefs(value, m_data - 8); + set_header_hasrefs(value, m_data - 8); } void Array::set_header_indexflag(bool value) { - ::set_header_indexflag(value, m_data - 8); + set_header_indexflag(value, m_data - 8); } void Array::set_header_wtype(WidthType value) { - ::set_header_wtype(value, m_data - 8); + set_header_wtype(value, m_data - 8); } void Array::set_header_width(size_t value) { - ::set_header_width(value, m_data - 8); + set_header_width(value, m_data - 8); } void Array::set_header_len(size_t value) { - ::set_header_len(value, m_data - 8); + set_header_len(value, m_data - 8); } void Array::set_header_capacity(size_t value) { - ::set_header_capacity(value, m_data - 8); + set_header_capacity(value, m_data - 8); } bool Array::get_header_isnode(const void* header) const TIGHTDB_NOEXCEPT @@ -351,7 +257,7 @@ void Array::Preset(int64_t min, int64_t max, size_t count) Preset(w, count); } -void Array::SetParent(ArrayParent *parent, size_t pndx) +void Array::SetParent(ArrayParent *parent, size_t pndx) TIGHTDB_NOEXCEPT { m_parent = parent; m_parentNdx = pndx; @@ -713,6 +619,81 @@ size_t Array::FindPos2(int64_t target) const return high; } +// return first element E for which E >= target or return -1 if none. Array must be sorted +size_t Array::FindGTE(int64_t target, size_t start) const +{ +#if TIGHTDB_DEBUG + // Reference implementation to illustrate and test behaviour + size_t ref = 0; + size_t idx; + for (idx = start; idx < m_len; ++idx) { + if (Get(idx) >= target) { + ref = idx; + break; + } + } + if (idx == m_len) + ref = not_found; +#endif + + size_t ret; + + if (start >= m_len) {ret = not_found; goto exit;} + + if (start + 2 < m_len) { + if (Get(start) >= target) {ret = start; goto exit;} else ++start; + if (Get(start) >= target) {ret = start; goto exit;} else ++start; + } + + // Todo, use templated Get from this point for performance + if (target > Get(m_len - 1)) {ret = not_found; goto exit;} + + size_t add; + add = 1; + + for(;;) { + if (start + add < m_len && Get(start + add) < target) + start += add; + else + break; + add *= 2; + } + + size_t high; + high = start + add + 1; + + if (high > m_len) + high = m_len; + + // if (start > 0) + start--; + + //start og high + + size_t orig_high; + orig_high = high; + + while (high - start > 1) { + const size_t probe = (start + high) / 2; + const int64_t v = Get(probe); + if (v < target) + start = probe; + else + high = probe; + } + if (high == orig_high) + ret = not_found; + else + ret = high; + +exit: + +#if TIGHTDB_DEBUG + TIGHTDB_ASSERT(ref == ret); +#endif + + return ret; +} size_t Array::FirstSetBit(unsigned int v) const { @@ -852,7 +833,7 @@ template bool Array::minmax(int64_t& result, size_t st ++start; #ifdef TIGHTDB_COMPILER_SSE - if(cpuid_sse<42>()) { + if (cpuid_sse<42>()) { // Test manually until 128 bit aligned for (; (start < end) && ((((size_t)m_data & 0xf) * 8 + start * w) % (128) != 0); start++) { if (find_max ? Get(start) > m : Get(start) < m) @@ -862,7 +843,7 @@ template bool Array::minmax(int64_t& result, size_t st if ((w == 8 || w == 16 || w == 32) && end - start > 2 * sizeof(__m128i) * 8 / NO0(w)) { __m128i *data = (__m128i *)(m_data + start * w / 8); __m128i state = data[0]; - __m128i state2; // FIXME: gcc-4.7 says that this one is undedfined if chunks is zero - can chunks ever be zero? + __m128i state2; size_t chunks = (end - start) * w / 8 / sizeof(__m128i); for (size_t t = 0; t < chunks; t++) { @@ -877,7 +858,7 @@ template bool Array::minmax(int64_t& result, size_t st } // prevent taking address of 'state' to make the compiler keep it in SSE register in above loop (vc2010/gcc4.6) - state2 = state; + state2 = state; for (size_t t = 0; t < sizeof(__m128i) * 8 / NO0(w); ++t) { const int64_t v = GetUniversal(((const char *)&state2), t); if (find_max ? v > m : v < m) { @@ -985,7 +966,7 @@ template int64_t Array::sum(size_t start, size_t end) const } #ifdef TIGHTDB_COMPILER_SSE - if(cpuid_sse<42>()) { + if (cpuid_sse<42>()) { // 2000 items summed 500000 times, 8/16/32 bits, miliseconds: // Naive, templated Get<>: 391 371 374 @@ -1086,7 +1067,7 @@ size_t Array::count(int64_t value) const const size_t end = m_len; size_t i = 0; - // staiic values needed for fast population count + // static values needed for fast population count const uint64_t m1 = 0x5555555555555555ULL; const uint64_t m2 = 0x3333333333333333ULL; const uint64_t m4 = 0x0f0f0f0f0f0f0f0fULL; @@ -1269,7 +1250,7 @@ size_t Array::GetByteSize(bool align) const size_t Array::CalcByteLen(size_t count, size_t width) const { - // FIXME: This arithemtic could overflow. Consider using + // FIXME: This arithemtic could overflow. Consider using const size_t bits = count * width; const size_t bytes = (bits+7) / 8; // round up return bytes + 8; // add room for 8 byte header @@ -1367,12 +1348,15 @@ bool Array::CopyOnWrite() size_t Array::create_empty_array(ColumnDef type, WidthType width_type, Allocator& alloc) { bool is_node = false, has_refs = false; - if (type == COLUMN_NODE) is_node = has_refs = true; - else if (type == COLUMN_HASREFS) has_refs = true; + if (type == COLUMN_NODE) + is_node = has_refs = true; + else if (type == COLUMN_HASREFS) + has_refs = true; const size_t capacity = initial_capacity; - MemRef mem_ref = alloc.Alloc(capacity); - if (!mem_ref.pointer) return 0; + const MemRef mem_ref = alloc.Alloc(capacity); + if (!mem_ref.pointer) + return 0; init_header(mem_ref.pointer, is_node, has_refs, width_type, 0, 0, capacity); @@ -1408,9 +1392,9 @@ bool Array::Alloc(size_t count, size_t width) else { mem_ref = m_alloc.ReAlloc(m_ref, m_data-8, capacity_bytes); if (!mem_ref.pointer) return false; - ::set_header_width(width, mem_ref.pointer); - ::set_header_len(count, mem_ref.pointer); - ::set_header_capacity(capacity_bytes, mem_ref.pointer); + set_header_width(width, mem_ref.pointer); + set_header_len(count, mem_ref.pointer); + set_header_capacity(capacity_bytes, mem_ref.pointer); } // Update wrapper objects @@ -2060,141 +2044,119 @@ size_t FindPos2Direct_32(const uint8_t* const header, const char* const data, in namespace tightdb { -void Array::state_init(ACTION action, state_state *state, Array* akku) -{ - if (action == TDB_MAX) { - state->state = -0x7fffffffffffffffLL - 1LL; - state->match_count = 0; - } - if (action == TDB_MIN) { - state->state = 0x7fffffffffffffffLL; - state->match_count = 0; - } - if (action == TDB_RETURN_FIRST) - state->state = not_found; - if (action == TDB_SUM) { - state->state = 0; - state->match_count = 0; - } - if (action == TDB_COUNT) - state->state = 0; - if (action == TDB_FINDALL) - state->state = (int64_t)akku; -} - void Array::find_all(Array& result, int64_t value, size_t colOffset, size_t start, size_t end) const { if (end == (size_t)-1) end = m_len; TIGHTDB_ASSERT(start < m_len && end <= m_len && start < end); - state_state state; + QueryState state; state.state = (int64_t)&result; - TEMPEX3(find, EQUAL, TDB_FINDALL, m_width, (value, start, end, colOffset, &state, &tightdb_dummy)); + TEMPEX3(find, EQUAL, TDB_FINDALL, m_width, (value, start, end, colOffset, &state, CallbackDummy())); return; } -void Array::find(int cond, ACTION action, int64_t value, size_t start, size_t end, size_t baseindex, state_state *state) const +void Array::find(int cond, ACTION action, int64_t value, size_t start, size_t end, size_t baseindex, QueryState *state) const { if (cond == COND_EQUAL) { if (action == TDB_SUM) { - TEMPEX3(find, EQUAL, TDB_SUM, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, EQUAL, TDB_SUM, m_width, (value, start, end, baseindex, state, CallbackDummy())) } else if (action == TDB_MIN) { - TEMPEX3(find, EQUAL, TDB_MIN, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, EQUAL, TDB_MIN, m_width, (value, start, end, baseindex, state, CallbackDummy())) } else if (action == TDB_MAX) { - TEMPEX3(find, EQUAL, TDB_MAX, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, EQUAL, TDB_MAX, m_width, (value, start, end, baseindex, state, CallbackDummy())) } else if (action == TDB_COUNT) { - TEMPEX3(find, EQUAL, TDB_COUNT, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, EQUAL, TDB_COUNT, m_width, (value, start, end, baseindex, state, CallbackDummy())) } else if (action == TDB_FINDALL) { - TEMPEX3(find, EQUAL, TDB_FINDALL, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, EQUAL, TDB_FINDALL, m_width, (value, start, end, baseindex, state, CallbackDummy())) } else if (action == TDB_CALLBACK_IDX) { - TEMPEX3(find, EQUAL, TDB_CALLBACK_IDX, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, EQUAL, TDB_CALLBACK_IDX, m_width, (value, start, end, baseindex, state, CallbackDummy())) } } if (cond == COND_NOTEQUAL) { if (action == TDB_SUM) { - TEMPEX3(find, NOTEQUAL, TDB_SUM, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, NOTEQUAL, TDB_SUM, m_width, (value, start, end, baseindex, state, CallbackDummy())) } else if (action == TDB_MIN) { - TEMPEX3(find, NOTEQUAL, TDB_MIN, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, NOTEQUAL, TDB_MIN, m_width, (value, start, end, baseindex, state, CallbackDummy())) } else if (action == TDB_MAX) { - TEMPEX3(find, NOTEQUAL, TDB_MAX, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, NOTEQUAL, TDB_MAX, m_width, (value, start, end, baseindex, state, CallbackDummy())) } else if (action == TDB_COUNT) { - TEMPEX3(find, NOTEQUAL, TDB_COUNT, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, NOTEQUAL, TDB_COUNT, m_width, (value, start, end, baseindex, state, CallbackDummy())) } else if (action == TDB_FINDALL) { - TEMPEX3(find, NOTEQUAL, TDB_FINDALL, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, NOTEQUAL, TDB_FINDALL, m_width, (value, start, end, baseindex, state, CallbackDummy())) } else if (action == TDB_CALLBACK_IDX) { - TEMPEX3(find, NOTEQUAL, TDB_CALLBACK_IDX, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, NOTEQUAL, TDB_CALLBACK_IDX, m_width, (value, start, end, baseindex, state, CallbackDummy())) } } if (cond == COND_GREATER) { if (action == TDB_SUM) { - TEMPEX3(find, GREATER, TDB_SUM, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, GREATER, TDB_SUM, m_width, (value, start, end, baseindex, state, CallbackDummy())) } else if (action == TDB_MIN) { - TEMPEX3(find, GREATER, TDB_MIN, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, GREATER, TDB_MIN, m_width, (value, start, end, baseindex, state, CallbackDummy())) } else if (action == TDB_MAX) { - TEMPEX3(find, GREATER, TDB_MAX, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, GREATER, TDB_MAX, m_width, (value, start, end, baseindex, state, CallbackDummy())) } else if (action == TDB_COUNT) { - TEMPEX3(find, GREATER, TDB_COUNT, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, GREATER, TDB_COUNT, m_width, (value, start, end, baseindex, state, CallbackDummy())) } else if (action == TDB_FINDALL) { - TEMPEX3(find, GREATER, TDB_FINDALL, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, GREATER, TDB_FINDALL, m_width, (value, start, end, baseindex, state, CallbackDummy())) } else if (action == TDB_CALLBACK_IDX) { - TEMPEX3(find, GREATER, TDB_CALLBACK_IDX, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, GREATER, TDB_CALLBACK_IDX, m_width, (value, start, end, baseindex, state, CallbackDummy())) } } if (cond == COND_LESS) { if (action == TDB_SUM) { - TEMPEX3(find, LESS, TDB_SUM, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, LESS, TDB_SUM, m_width, (value, start, end, baseindex, state, CallbackDummy())) } else if (action == TDB_MIN) { - TEMPEX3(find, LESS, TDB_MIN, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, LESS, TDB_MIN, m_width, (value, start, end, baseindex, state, CallbackDummy())) } else if (action == TDB_MAX) { - TEMPEX3(find, LESS, TDB_MAX, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, LESS, TDB_MAX, m_width, (value, start, end, baseindex, state, CallbackDummy())) } else if (action == TDB_COUNT) { - TEMPEX3(find, LESS, TDB_COUNT, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, LESS, TDB_COUNT, m_width, (value, start, end, baseindex, state, CallbackDummy())) } else if (action == TDB_FINDALL) { - TEMPEX3(find, LESS, TDB_FINDALL, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, LESS, TDB_FINDALL, m_width, (value, start, end, baseindex, state, CallbackDummy())) } else if (action == TDB_CALLBACK_IDX) { - TEMPEX3(find, LESS, TDB_CALLBACK_IDX, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, LESS, TDB_CALLBACK_IDX, m_width, (value, start, end, baseindex, state, CallbackDummy())) } } if (cond == COND_NONE) { if (action == TDB_SUM) { - TEMPEX3(find, NONE, TDB_SUM, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, NONE, TDB_SUM, m_width, (value, start, end, baseindex, state, CallbackDummy())) } else if (action == TDB_MIN) { - TEMPEX3(find, NONE, TDB_MIN, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, NONE, TDB_MIN, m_width, (value, start, end, baseindex, state, CallbackDummy())) } else if (action == TDB_MAX) { - TEMPEX3(find, NONE, TDB_MAX, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, NONE, TDB_MAX, m_width, (value, start, end, baseindex, state, CallbackDummy())) } else if (action == TDB_COUNT) { - TEMPEX3(find, NONE, TDB_COUNT, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, NONE, TDB_COUNT, m_width, (value, start, end, baseindex, state, CallbackDummy())) } else if (action == TDB_FINDALL) { - TEMPEX3(find, NONE, TDB_FINDALL, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, NONE, TDB_FINDALL, m_width, (value, start, end, baseindex, state, CallbackDummy())) } else if (action == TDB_CALLBACK_IDX) { - TEMPEX3(find, NONE, TDB_CALLBACK_IDX, m_width, (value, start, end, baseindex, state, &tightdb_dummy)) + TEMPEX3(find, NONE, TDB_CALLBACK_IDX, m_width, (value, start, end, baseindex, state, CallbackDummy())) } } } @@ -2296,6 +2258,7 @@ int64_t Array::ColumnGet(size_t ndx) const TIGHTDB_NOEXCEPT return GetDirect(data, width, ndx); } +// FIXME: Shouldn't ColumnStringGet() be locaterd in ColumnString? const char* Array::ColumnStringGet(size_t ndx) const TIGHTDB_NOEXCEPT { const char* data = reinterpret_cast(m_data); diff --git a/src/tightdb/array.hpp b/src/tightdb/array.hpp index 76f7f8c27ff..c49a7afdcf3 100644 --- a/src/tightdb/array.hpp +++ b/src/tightdb/array.hpp @@ -20,15 +20,15 @@ /* Searching: The main finding function is: - template void find(int64_t value, size_t start, size_t end, size_t baseindex, state_state *state, Callback callback) const + template void find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState *state, Callback callback) const cond: One of EQUAL, NOTEQUAL, GREATER, etc. classes ACTION: One of TDB_RETURN_FIRST, TDB_FINDALL, TDB_MAX, TDB_CALLBACK_IDX, etc, constants Callback: Optional function to call for each search result. Will be called if action == TDB_CALLBACK_IDX - - find() will call FIND_ACTION_PATTERN() or FIND_ACTION() that again calls state_match() for each search result which optionally calls callback(): - - find() -> FIND_ACTION() -------> bool state_match() -> bool callback() + + find() will call FIND_ACTION_PATTERN() or FIND_ACTION() that again calls match() for each search result which optionally calls callback(): + + find() -> FIND_ACTION() -------> bool match() -> bool callback() | ^ +-> FIND_ACTION_PATTERN()----+ @@ -50,6 +50,7 @@ Searching: The main finding function is: #include #include #include +#include /* MMX: mmintrin.h @@ -61,7 +62,6 @@ Searching: The main finding function is: SSE4.1: smmintrin.h SSE4.2: nmmintrin.h */ - #ifdef TIGHTDB_COMPILER_SSE #include // SSE2 #include // SSE42 @@ -81,8 +81,6 @@ enum ACTION {TDB_RETURN_FIRST, TDB_SUM, TDB_MAX, TDB_MIN, TDB_COUNT, TDB_FINDALL namespace tightdb { -bool tightdb_dummy (int64_t t); - #define NO0(v) ((v) == 0 ? 1 : (v)) const size_t not_found = size_t(-1); @@ -137,11 +135,9 @@ const size_t not_found = size_t(-1); class Array; class AdaptiveStringColumn; class GroupWriter; +class Column; +template class QueryState; -struct state_state { - int64_t state; - size_t match_count; -}; #ifdef TIGHTDB_DEBUG class MemStats { @@ -175,6 +171,7 @@ enum ColumnDef { bool IsArrayIndexNode(size_t ref, const Allocator& alloc); + class ArrayParent { public: @@ -204,8 +201,8 @@ class ArrayParent class Array: public ArrayParent { public: -// void state_init(int action, state_state *state); -// bool state_match(int action, size_t index, int64_t value, state_state *state); +// void state_init(int action, QueryState *state); +// bool match(int action, size_t index, int64_t value, QueryState *state); /// Create a new array, and if \a parent and \a ndx_in_parent are /// specified, update the parent to point to this new array. @@ -223,8 +220,8 @@ class Array: public ArrayParent { Array(const Array&) TIGHTDB_NOEXCEPT; // Fastest way to instantiate an array, if you just want to utilize its methods - // FIXME: Using a type tag here instead of a boolean argument would be a lot safer. - explicit Array(bool); + struct no_prealloc_tag {}; + explicit Array(no_prealloc_tag) TIGHTDB_NOEXCEPT; virtual ~Array() TIGHTDB_NOEXCEPT {} @@ -252,7 +249,7 @@ class Array: public ArrayParent { // Parent tracking bool HasParent() const TIGHTDB_NOEXCEPT {return m_parent != NULL;} - void SetParent(ArrayParent *parent, size_t ndx_in_parent); + void SetParent(ArrayParent *parent, size_t ndx_in_parent) TIGHTDB_NOEXCEPT; void UpdateParentNdx(int diff) {m_parentNdx += diff;} ArrayParent *GetParent() const TIGHTDB_NOEXCEPT {return m_parent;} size_t GetParentNdx() const {return m_parentNdx;} @@ -299,6 +296,7 @@ class Array: public ArrayParent { size_t FindPos(int64_t value) const TIGHTDB_NOEXCEPT; size_t FindPos2(int64_t value) const; + size_t FindGTE(int64_t target, size_t start) const; void Preset(int64_t min, int64_t max, size_t count); void Preset(size_t bitwidth, size_t count); @@ -330,33 +328,29 @@ class Array: public ArrayParent { /// Compare two arrays for equality. bool Compare(const Array&) const; - // Main finding function - used for find_first, find_all, sum, max, min, etc. - void find(int cond, ACTION action, int64_t value, size_t start, size_t end, size_t baseindex, state_state *state) const; - - template - void find(int64_t value, size_t start, size_t end, size_t baseindex, state_state *state, Callback callback) const; - - template - void find(int64_t value, size_t start, size_t end, size_t baseindex, state_state *state) const; - - template - void find(int64_t value, size_t start, size_t end, size_t baseindex, state_state *state, Callback callback) const; - + // Main finding function - used for find_first, find_all, sum, max, min, etc. + void find(int cond, ACTION action, int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state) const; + + template + void find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, Callback callback) const; + + template + void find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state) const; + + template + void find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, Callback callback) const; + // Optimized implementation for release mode template - void find_optimized(int64_t value, size_t start, size_t end, size_t baseindex, state_state *state, Callback callback) const; + void find_optimized(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, Callback callback) const; // Reference implementation of find() - verifies result from optimized version if debug mode template - int64_t find_reference(int64_t value, size_t start, size_t end, size_t baseindex, state_state *state, Callback callback) const; - - // Initialize state before calling find() - void state_init(ACTION action, state_state *state, Array* akku); + int64_t find_reference(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, Callback callback) const; // Called for each search result - template bool state_match(size_t index, uint64_t indexpattern, int64_t value, state_state *state, Callback callback) const; - template bool FIND_ACTION(size_t index, int64_t value, state_state *state, Callback callback) const; - template bool FIND_ACTION_PATTERN(size_t index, uint64_t pattern, state_state *state, Callback callback) const; + template bool FIND_ACTION(size_t index, int64_t value, QueryState* state, Callback callback) const; + template bool FIND_ACTION_PATTERN(size_t index, uint64_t pattern, QueryState* state, Callback callback) const; // Wrappers for backwards compatibility and for simple use without setting up state initialization etc template size_t find_first(int64_t value, size_t start = 0, size_t end = size_t(-1)) const; @@ -365,25 +359,24 @@ class Array: public ArrayParent { // Non-SSE find for the four functions EQUAL/NOTEQUAL/LESS/GREATER template - bool Compare(int64_t value, size_t start, size_t end, size_t baseindex, state_state *state, Callback callback) const; + bool Compare(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, Callback callback) const; // Non-SSE find for EQUAL/NOTEQUAL template - inline bool CompareEquality(int64_t value, size_t start, size_t end, size_t baseindex, state_state *state, Callback callback) const; + inline bool CompareEquality(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, Callback callback) const; // Non-SSE find for LESS/GREATER template - bool CompareRelation(int64_t value, size_t start, size_t end, size_t baseindex, state_state *state, Callback callback) const; + bool CompareRelation(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, Callback callback) const; // SSE find for the four functions EQUAL/NOTEQUAL/LESS/GREATER #ifdef TIGHTDB_COMPILER_SSE template - size_t FindSSE(int64_t value, __m128i *data, size_t items, state_state *state, size_t baseindex, Callback callback) const; + size_t FindSSE(int64_t value, __m128i *data, size_t items, QueryState* state, size_t baseindex, Callback callback) const; #endif template inline bool TestZero(uint64_t value) const; // Tests value for 0-elements template size_t FindZero(uint64_t v) const; // Finds position of 0/non-zero element - template bool USES_VAL(void); // Helps compiler knowing that a Get()'ed value is unused template uint64_t cascade(uint64_t a) const; // Sets uppermost bits of non-zero elements template int64_t FindGTLT_Magic(int64_t v) const; // Compute magic constant needed for searching for value 'v' using bit hacks template inline int64_t LowerBits(void) const; // Return chunk with lower bit set in each element @@ -393,35 +386,12 @@ class Array: public ArrayParent { // Find value greater/less in 64-bit chunk - only works for positive values template - bool FindGTLT_Fast(uint64_t chunk, uint64_t magic, state_state *state, size_t baseindex, Callback callback) const; + bool FindGTLT_Fast(uint64_t chunk, uint64_t magic, QueryState* state, size_t baseindex, Callback callback) const; // Find value greater/less in 64-bit chunk - no constraints template - bool FindGTLT(int64_t v, uint64_t chunk, state_state *state, size_t baseindex, Callback callback) const; + bool FindGTLT(int64_t v, uint64_t chunk, QueryState* state, size_t baseindex, Callback callback) const; - - // popcount - #if defined(_MSC_VER) && _MSC_VER >= 1500 - inline int fast_popcount32(uint32_t x); - #if defined(_M_X64) - inline int fast_popcount64(unsigned __int64 x) const; - #else - inline int fast_popcount64(unsigned __int64 x) const; - #endif - #elif defined(__GNUC__) && __GNUC__ >= 4 || defined(__INTEL_COMPILER) && __INTEL_COMPILER >= 900 - #if ULONG_MAX == 0xffffffff - inline int fast_popcount64(unsigned long long x) const; - #else - inline int fast_popcount64(unsigned long long x) const; - #endif - #else - // Masking away bits might be faster than bit shifting (which can be slow). Note that the compiler may optimize this automatically. Todo, investigate. - inline int fast_popcount32(uint32_t x) const; - - inline int fast_popcount64(uint64_t x) const; - #endif // select best popcount implementations - - // Debug size_t GetBitWidth() const {return m_width;} @@ -460,11 +430,6 @@ class Array: public ArrayParent { void CreateFromHeaderDirect(uint8_t* header, size_t ref=0) TIGHTDB_NOEXCEPT; void update_ref_in_parent(); - // Getters and Setters for adaptive-packed arrays - typedef int64_t(Array::*Getter)(size_t) const; // Note: getters must not throw - typedef void(Array::*Setter)(size_t, int64_t); - typedef void (Array::*Finder)(int64_t, size_t, size_t, size_t, state_state*) const; - enum WidthType { TDB_BITS = 0, TDB_MULTIPLY = 1, @@ -482,34 +447,41 @@ class Array: public ArrayParent { void set_header_width(size_t value); void set_header_len(size_t value); void set_header_capacity(size_t value); - bool get_header_isnode(const void* header=0) const TIGHTDB_NOEXCEPT; - bool get_header_hasrefs(const void* header=0) const TIGHTDB_NOEXCEPT; - bool get_header_indexflag(const void* header=0) const TIGHTDB_NOEXCEPT; - WidthType get_header_wtype(const void* header=0) const TIGHTDB_NOEXCEPT; - size_t get_header_width(const void* header=0) const TIGHTDB_NOEXCEPT; - size_t get_header_len(const void* header=0) const TIGHTDB_NOEXCEPT; - size_t get_header_capacity(const void* header=0) const TIGHTDB_NOEXCEPT; - - template void SetWidth() TIGHTDB_NOEXCEPT; - void SetWidth(size_t) TIGHTDB_NOEXCEPT; + + bool get_header_isnode(const void* header=NULL) const TIGHTDB_NOEXCEPT; + bool get_header_hasrefs(const void* header=NULL) const TIGHTDB_NOEXCEPT; + bool get_header_indexflag(const void* header=NULL) const TIGHTDB_NOEXCEPT; + WidthType get_header_wtype(const void* header=NULL) const TIGHTDB_NOEXCEPT; + size_t get_header_width(const void* header=NULL) const TIGHTDB_NOEXCEPT; + size_t get_header_len(const void* header=NULL) const TIGHTDB_NOEXCEPT; + size_t get_header_capacity(const void* header=NULL) const TIGHTDB_NOEXCEPT; + + static void set_header_isnode(bool value, void* header); + static void set_header_hasrefs(bool value, void* header); + static void set_header_indexflag(bool value, void* header); + static void set_header_wtype(int value, void* header); + static void set_header_width(size_t value, void* header); + static void set_header_len(size_t value, void* header); + static void set_header_capacity(size_t value, void* header); + static void init_header(void* header, bool is_node, bool has_refs, int width_type, + size_t width, size_t length, size_t capacity); + + template void SetWidth(void) TIGHTDB_NOEXCEPT; + void SetWidth(size_t width) TIGHTDB_NOEXCEPT; bool Alloc(size_t count, size_t width); bool CopyOnWrite(); - // Member variables - Getter m_getter; - Setter m_setter; - Finder m_finder[COND_COUNT]; // one for each COND_XXX enum - private: size_t m_ref; template bool minmax(int64_t& result, size_t start, size_t end) const; protected: - size_t m_len; - size_t m_capacity; - size_t m_width; // FIXME: Should be an 'int' - bool m_isNode; - bool m_hasRefs; + size_t m_len; // items currently stored + size_t m_capacity; // max item capacity +// FIXME: m_width Should be an 'int' + size_t m_width; // size of an item in bits or bytes depending on + bool m_isNode; // is it a Node or Leaf array + bool m_hasRefs; // private: ArrayParent *m_parent; @@ -518,21 +490,169 @@ class Array: public ArrayParent { Allocator& m_alloc; protected: - int64_t m_lbound; - int64_t m_ubound; - + static const size_t initial_capacity = 128; static size_t create_empty_array(ColumnDef, WidthType, Allocator&); // Overriding methods in ArrayParent virtual void update_child_ref(size_t child_ndx, size_t new_ref); virtual size_t get_child_ref(size_t child_ndx) const; +// FIXME: below should be moved to a specific ArrayNumber class +protected: + // Getters and Setters for adaptive-packed arrays + typedef int64_t(Array::*Getter)(size_t) const; // Note: getters must not throw + typedef void(Array::*Setter)(size_t, int64_t); + typedef void (Array::*Finder)(int64_t, size_t, size_t, size_t, QueryState*) const; + + Getter m_getter; + Setter m_setter; + Finder m_finder[COND_COUNT]; // one for each COND_XXX enum + + int64_t m_lbound; // min number that can be stored with current m_width + int64_t m_ubound; // max number that can be stored with current m_width }; + + // Implementation: -inline Array::Array(size_t ref, ArrayParent* parent, size_t pndx, - Allocator& alloc) TIGHTDB_NOEXCEPT: +class QueryStateParent {}; + +template <> class QueryState : public QueryStateParent { +public: + + int64_t state; + size_t match_count; + size_t m_limit; + + template bool uses_val(void) + { + if (action == TDB_MAX || action == TDB_MIN || action == TDB_SUM) + return true; + else + return false; + } + + void init(ACTION action, Array* akku, size_t limit) + { + match_count = 0; + m_limit = limit; + + if (action == TDB_MAX) + state = -0x7fffffffffffffffLL - 1LL; + else if (action == TDB_MIN) + state = 0x7fffffffffffffffLL; + else if (action == TDB_RETURN_FIRST) + state = not_found; + else if (action == TDB_SUM) + state = 0; + else if (action == TDB_COUNT) + state = 0; + else if (action == TDB_FINDALL) + state = (int64_t)akku; + else + TIGHTDB_ASSERT(false); + } + + template + inline bool match(size_t index, uint64_t indexpattern, int64_t value, Callback callback) + { + if (pattern) { + if (action == TDB_COUNT) { + state += fast_popcount64(indexpattern); + match_count = size_t(state); + return true; + } + // Other aggregates cannot (yet) use bit pattern for anything. Make Array-finder call with pattern = false instead + return false; + } + + ++match_count; + + if (action == TDB_CALLBACK_IDX) + return callback(index); + else if (action == TDB_MAX) { + if(value > state) + state = value; + } + else if (action == TDB_MIN) { + if(value < state) + state = value; + } + else if (action == TDB_SUM) + state += value; + else if (action == TDB_COUNT) { + state++; + match_count = size_t(state); + } + else if (action == TDB_FINDALL) + ((Array*)state)->add(index); + else if (action == TDB_RETURN_FIRST) { + state = index; + return false; + } + else + TIGHTDB_ASSERT(false); + return true; + } + +}; + +template class QueryState : public QueryStateParent { +public: + + T state; + size_t match_count; + size_t m_limit; + + template bool uses_val(void) + { + return (action == TDB_MAX || action == TDB_MIN || action == TDB_SUM); + } + + void init(ACTION action, Array*, size_t limit) + { + match_count = 0; + m_limit = limit; + + if (action == TDB_MAX) + state = -std::numeric_limits::infinity(); + else if (action == TDB_MIN) + state = std::numeric_limits::infinity(); + else if (action == TDB_SUM) + state = 0.0; + else + TIGHTDB_ASSERT(false); + } + + template + inline bool match(size_t /*index*/, uint64_t /*indexpattern*/, resulttype value, Callback /*callback*/) + { + if(pattern) + return false; + + TIGHTDB_STATIC_ASSERT(action == TDB_SUM || action == TDB_MAX || action == TDB_MIN, ""); + ++match_count; + + if (action == TDB_MAX) { + if(value > state) + state = value; + } + else if (action == TDB_MIN) { + if(value < state) + state = value; + } + else if (action == TDB_SUM) + state += value; + else + TIGHTDB_ASSERT(false); + return true; + } +}; + + + +inline Array::Array(size_t ref, ArrayParent* parent, size_t pndx, Allocator& alloc) TIGHTDB_NOEXCEPT: m_data(NULL), m_len(0), m_capacity(0), m_width(0), m_isNode(false), m_hasRefs(false), m_parent(parent), m_parentNdx(pndx), m_alloc(alloc), m_lbound(0), m_ubound(0) { @@ -568,7 +688,7 @@ inline Array::Array(const Array& src) TIGHTDB_NOEXCEPT: // Fastest way to instantiate an Array. For use with GetDirect() that only fills out m_width, m_data // and a few other basic things needed for read-only access. Or for use if you just want a way to call // some methods written in Array.* -inline Array::Array(bool) : m_alloc(Allocator::get_default()) {} +inline Array::Array(no_prealloc_tag) TIGHTDB_NOEXCEPT: m_alloc(Allocator::get_default()) {} inline int64_t Array::back() const TIGHTDB_NOEXCEPT @@ -590,7 +710,94 @@ inline Array Array::GetSubArray(std::size_t ndx) const TIGHTDB_NOEXCEPT } -template inline size_t Array::Write(S& out, bool recurse, bool persist) const + +//------------------------------------------------- + +inline void Array::set_header_isnode(bool value, void* header) +{ + uint8_t* const header2 = reinterpret_cast(header); + header2[0] = (header2[0] & ~0x80) | uint8_t(value << 7); +} + +inline void Array::set_header_hasrefs(bool value, void* header) +{ + uint8_t* const header2 = reinterpret_cast(header); + header2[0] = (header2[0] & ~0x40) | uint8_t(value << 6); +} + +inline void Array::set_header_indexflag(bool value, void* header) +{ + uint8_t* const header2 = reinterpret_cast(header); + header2[0] = (header2[0] & ~0x20) | uint8_t(value << 5); +} + +inline void Array::set_header_wtype(int value, void* header) +{ + // Indicates how to calculate size in bytes based on width + // 0: bits (width/8) * length + // 1: multiply width * length + // 2: ignore 1 * length + uint8_t* const header2 = reinterpret_cast(header); + header2[0] = (header2[0] & ~0x18) | uint8_t(value << 3); +} + +inline void Array::set_header_width(size_t value, void* header) +{ + // Pack width in 3 bits (log2) + size_t w = 0; + size_t b = size_t(value); + while (b) {++w; b >>= 1;} + TIGHTDB_ASSERT(w < 8); + + uint8_t* const header2 = reinterpret_cast(header); + header2[0] = (header2[0] & ~0x7) | uint8_t(w); +} + +inline void Array::set_header_len(size_t value, void* header) +{ + TIGHTDB_ASSERT(value <= 0xFFFFFF); + uint8_t* const header2 = reinterpret_cast(header); + header2[1] = (value >> 16) & 0x000000FF; + header2[2] = (value >> 8) & 0x000000FF; + header2[3] = value & 0x000000FF; +} + +inline void Array::set_header_capacity(size_t value, void* header) +{ + TIGHTDB_ASSERT(value <= 0xFFFFFF); + uint8_t* const header2 = reinterpret_cast(header); + header2[4] = (value >> 16) & 0x000000FF; + header2[5] = (value >> 8) & 0x000000FF; + header2[6] = value & 0x000000FF; +} + + +inline void Array::init_header(void* header, bool is_node, bool has_refs, int width_type, + size_t width, size_t length, size_t capacity) +{ + // Note: Since the header layout contains unallocated + // bit and/or bytes, it is important that we put the + // entire 8 byte header into a well defined state + // initially. Note also: The C++ standard does not + // guarantee that int64_t is extactly 8 bytes wide. It + // may be more, and it may be less. That is why we + // need the static assert. + TIGHTDB_STATIC_ASSERT(sizeof(int64_t) == 8, + "Trouble if int64_t is not 8 bytes wide"); + *reinterpret_cast(header) = 0; + set_header_isnode(is_node, header); + set_header_hasrefs(has_refs, header); + set_header_wtype(width_type, header); + set_header_width(width, header); + set_header_len(length, header); + set_header_capacity(capacity, header); +} + + +//------------------------------------------------- + + +template size_t Array::Write(S& out, bool recurse, bool persist) const { TIGHTDB_ASSERT(IsValid()); @@ -733,980 +940,892 @@ inline size_t Array::get_child_ref(size_t child_ndx) const - //************************************************************************************* - // Finding code * - //************************************************************************************* +//************************************************************************************* +// Finding code * +//************************************************************************************* - template int64_t Array::GetUniversal(const char* const data, const size_t ndx) const - { - if (w == 0) { - return 0; - } - else if (w == 1) { - const size_t offset = ndx >> 3; - return (data[offset] >> (ndx & 7)) & 0x01; - } - else if (w == 2) { - const size_t offset = ndx >> 2; - return (data[offset] >> ((ndx & 3) << 1)) & 0x03; - } - else if (w == 4) { - const size_t offset = ndx >> 1; - return (data[offset] >> ((ndx & 1) << 2)) & 0x0F; - } - else if (w == 8) { - return *((const signed char*)(data + ndx)); - } - else if (w == 16) { - const size_t offset = ndx * 2; - return *(const int16_t*)(data + offset); - } - else if (w == 32) { - const size_t offset = ndx * 4; - return *(const int32_t*)(data + offset); - } - else if (w == 64) { - const size_t offset = ndx * 8; - return *((const int64_t*)(data + offset)); - } - else { - TIGHTDB_ASSERT(false); - return int64_t(-1); - } +template int64_t Array::GetUniversal(const char* const data, const size_t ndx) const +{ + if (w == 0) { + return 0; + } + else if (w == 1) { + const size_t offset = ndx >> 3; + return (data[offset] >> (ndx & 7)) & 0x01; + } + else if (w == 2) { + const size_t offset = ndx >> 2; + return (data[offset] >> ((ndx & 3) << 1)) & 0x03; + } + else if (w == 4) { + const size_t offset = ndx >> 1; + return (data[offset] >> ((ndx & 1) << 2)) & 0x0F; + } + else if (w == 8) { + return *((const signed char*)(data + ndx)); + } + else if (w == 16) { + const size_t offset = ndx * 2; + return *(const int16_t*)(data + offset); + } + else if (w == 32) { + const size_t offset = ndx * 4; + return *(const int32_t*)(data + offset); } + else if (w == 64) { + const size_t offset = ndx * 8; + return *((const int64_t*)(data + offset)); + } + else { + TIGHTDB_ASSERT(false); + return int64_t(-1); + } +} - /* - find() (calls find_optimized()) will call state_match() for each search result. +/* +find() (calls find_optimized()) will call match() for each search result. - If pattern == true: - 'indexpattern' contains a 64-bit chunk of elements, each of 'width' bits in size where each element indicates a match if its lower bit is set, otherwise - it indicates a non-match. 'index' tells the database row index of the first element. You must return true if you chose to 'consume' the chunk or false - if not. If not, then Array-finder will afterwards call state_match() successive times with pattern == false. +If pattern == true: + 'indexpattern' contains a 64-bit chunk of elements, each of 'width' bits in size where each element indicates a match if its lower bit is set, otherwise + it indicates a non-match. 'index' tells the database row index of the first element. You must return true if you chose to 'consume' the chunk or false + if not. If not, then Array-finder will afterwards call match() successive times with pattern == false. - If pattern == false: - 'index' tells the row index of a single match and 'value' tells its value. Return false to make Array-finder break its search or return true to let it continue until - 'end' or 'limit'. +If pattern == false: + 'index' tells the row index of a single match and 'value' tells its value. Return false to make Array-finder break its search or return true to let it continue until + 'end' or 'limit'. - Array-finder decides itself if - and when - it wants to pass you an indexpattern. It depends on array bit width, match frequency, and wether the arithemetic and - computations for the given search criteria makes it feasible to construct such a pattern. - */ +Array-finder decides itself if - and when - it wants to pass you an indexpattern. It depends on array bit width, match frequency, and wether the arithemetic and +computations for the given search criteria makes it feasible to construct such a pattern. +*/ - template bool Array::state_match(size_t index, uint64_t indexpattern, int64_t value, state_state *state, Callback callback) const - { - if (pattern) { - if (action == TDB_COUNT) { - size_t c = fast_popcount64(indexpattern); - state->state += c; - state->match_count += c; - return true; - } - // Other aggregates cannot (yet) use bit pattern for anything. Make Array-finder call with pattern = false instead - return false; - } +// These wrapper functions only exist to enable a possibility to make the compiler see that 'value' and/or 'index' are unused, such that caller's +// computation of these values will not be made. Only works if FIND_ACTION and FIND_ACTION_PATTERN rewritten as macros. Note: This problem has been fixed in +// next upcoming array.hpp version +template bool Array::FIND_ACTION(size_t index, int64_t value, QueryState* state, Callback callback) const +{ + return state->match(index, 0, value, callback); +} +template bool Array::FIND_ACTION_PATTERN(size_t index, uint64_t pattern, QueryState* state, Callback callback) const +{ + return state->match(index, pattern, 0, callback); +} - state->match_count++; - if (action == TDB_CALLBACK_IDX) - return callback(index); - if (action == TDB_MAX && value > *(int64_t*)state) - state->state = value; - if (action == TDB_MIN && value < *(int64_t*)state) - state->state = value; - if (action == TDB_SUM) - state->state += value; - if (action == TDB_COUNT) - state->state++; - if (action == TDB_FINDALL) - ((Array*)state->state)->add(index); - if (action == TDB_RETURN_FIRST) { - state->state = index; - return false; - } - return true; - } +template uint64_t Array::cascade(uint64_t a) const +{ + // Takes a chunk of values as argument and sets the uppermost bit for each element which is 0. Example: + // width == 4 and v = 01000000 00001000 10000001 00001000 00000000 10100100 00001100 00111110 01110100 00010000 00000000 00000001 10000000 01111110 + // will return: 00001000 00010000 00010000 00010000 00010001 00000000 00010000 00000000 00000000 00000001 00010001 00010000 00000001 00000000 - template bool Array::USES_VAL(void) - { - if (action == TDB_MAX || action == TDB_MIN || action == TDB_SUM) - return true; - else - return false; - } + // static values needed for fast population count + const uint64_t m1 = 0x5555555555555555ULL; - // These wrapper functions only exist to enable a possibility to make the compiler see that 'value' and/or 'index' are unused, such that caller's - // computation of these values will not be made. Only works if FIND_ACTION and FIND_ACTION_PATTERN rewritten as macros. Note: This problem has been fixed in - // next upcoming array.hpp version - template bool Array::FIND_ACTION(size_t index, int64_t value, state_state *state, Callback callback) const - { - return state_match(index, 0, value, state, callback); - } - template bool Array::FIND_ACTION_PATTERN(size_t index, uint64_t pattern, state_state *state, Callback callback) const - { - return state_match(index, pattern, 0, state, callback); + if (width == 1) { + return ~a; } + else if (width == 2) { + // Masks to avoid spillover between segments in cascades + const uint64_t c1 = ~0ULL/0x3 * 0x1; + a |= (a >> 1) & c1; // cascade ones in non-zeroed segments + a &= m1; // isolate single bit in each segment + a ^= m1; // reverse isolated bits - template uint64_t Array::cascade(uint64_t a) const - { - // Takes a chunk of values as argument and sets the uppermost bit for each element which is 0. Example: - // width == 4 and v = 01000000 00001000 10000001 00001000 00000000 10100100 00001100 00111110 01110100 00010000 00000000 00000001 10000000 01111110 - // will return: 00001000 00010000 00010000 00010000 00010001 00000000 00010000 00000000 00000000 00000001 00010001 00010000 00000001 00000000 - - // static values needed for fast population count - const uint64_t m1 = 0x5555555555555555ULL; - - if (width == 1) { - return ~a; - } - else if (width == 2) { - // Masks to avoid spillover between segments in cascades - const uint64_t c1 = ~0ULL/0x3 * 0x1; - - a |= (a >> 1) & c1; // cascade ones in non-zeroed segments - a &= m1; // isolate single bit in each segment - a ^= m1; // reverse isolated bits - - return a; - } - else if (width == 4) { - const uint64_t m = ~0ULL/0xF * 0x1; + return a; + } + else if (width == 4) { + const uint64_t m = ~0ULL/0xF * 0x1; - // Masks to avoid spillover between segments in cascades - const uint64_t c1 = ~0ULL/0xF * 0x7; - const uint64_t c2 = ~0ULL/0xF * 0x3; + // Masks to avoid spillover between segments in cascades + const uint64_t c1 = ~0ULL/0xF * 0x7; + const uint64_t c2 = ~0ULL/0xF * 0x3; - a |= (a >> 1) & c1; // cascade ones in non-zeroed segments - a |= (a >> 2) & c2; - a &= m; // isolate single bit in each segment - a ^= m; // reverse isolated bits + a |= (a >> 1) & c1; // cascade ones in non-zeroed segments + a |= (a >> 2) & c2; + a &= m; // isolate single bit in each segment + a ^= m; // reverse isolated bits - return a; - } - else if (width == 8) { - const uint64_t m = ~0ULL/0xFF * 0x1; + return a; + } + else if (width == 8) { + const uint64_t m = ~0ULL/0xFF * 0x1; - // Masks to avoid spillover between segments in cascades - const uint64_t c1 = ~0ULL/0xFF * 0x7F; - const uint64_t c2 = ~0ULL/0xFF * 0x3F; - const uint64_t c3 = ~0ULL/0xFF * 0x0F; + // Masks to avoid spillover between segments in cascades + const uint64_t c1 = ~0ULL/0xFF * 0x7F; + const uint64_t c2 = ~0ULL/0xFF * 0x3F; + const uint64_t c3 = ~0ULL/0xFF * 0x0F; - a |= (a >> 1) & c1; // cascade ones in non-zeroed segments - a |= (a >> 2) & c2; - a |= (a >> 4) & c3; - a &= m; // isolate single bit in each segment - a ^= m; // reverse isolated bits + a |= (a >> 1) & c1; // cascade ones in non-zeroed segments + a |= (a >> 2) & c2; + a |= (a >> 4) & c3; + a &= m; // isolate single bit in each segment + a ^= m; // reverse isolated bits - return a; - } - else if (width == 16) { - const uint64_t m = ~0ULL/0xFFFF * 0x1; - - // Masks to avoid spillover between segments in cascades - const uint64_t c1 = ~0ULL/0xFFFF * 0x7FFF; - const uint64_t c2 = ~0ULL/0xFFFF * 0x3FFF; - const uint64_t c3 = ~0ULL/0xFFFF * 0x0FFF; - const uint64_t c4 = ~0ULL/0xFFFF * 0x00FF; - - a |= (a >> 1) & c1; // cascade ones in non-zeroed segments - a |= (a >> 2) & c2; - a |= (a >> 4) & c3; - a |= (a >> 8) & c4; - a &= m; // isolate single bit in each segment - a ^= m; // reverse isolated bits - - return a; - } + return a; + } + else if (width == 16) { + const uint64_t m = ~0ULL/0xFFFF * 0x1; + + // Masks to avoid spillover between segments in cascades + const uint64_t c1 = ~0ULL/0xFFFF * 0x7FFF; + const uint64_t c2 = ~0ULL/0xFFFF * 0x3FFF; + const uint64_t c3 = ~0ULL/0xFFFF * 0x0FFF; + const uint64_t c4 = ~0ULL/0xFFFF * 0x00FF; + + a |= (a >> 1) & c1; // cascade ones in non-zeroed segments + a |= (a >> 2) & c2; + a |= (a >> 4) & c3; + a |= (a >> 8) & c4; + a &= m; // isolate single bit in each segment + a ^= m; // reverse isolated bits + + return a; + } - else if (width == 32) { - const uint64_t m = ~0ULL/0xFFFFFFFF * 0x1; - - // Masks to avoid spillover between segments in cascades - const uint64_t c1 = ~0ULL/0xFFFFFFFF * 0x7FFFFFFF; - const uint64_t c2 = ~0ULL/0xFFFFFFFF * 0x3FFFFFFF; - const uint64_t c3 = ~0ULL/0xFFFFFFFF * 0x0FFFFFFF; - const uint64_t c4 = ~0ULL/0xFFFFFFFF * 0x00FFFFFF; - const uint64_t c5 = ~0ULL/0xFFFFFFFF * 0x0000FFFF; - - a |= (a >> 1) & c1; // cascade ones in non-zeroed segments - a |= (a >> 2) & c2; - a |= (a >> 4) & c3; - a |= (a >> 8) & c4; - a |= (a >> 16) & c5; - a &= m; // isolate single bit in each segment - a ^= m; // reverse isolated bits - - return a; - } - else if (width == 64) { - return a == 0 ? 1 : 0; - } - else { - TIGHTDB_ASSERT(false); - return uint64_t(-1); - } + else if (width == 32) { + const uint64_t m = ~0ULL/0xFFFFFFFF * 0x1; + + // Masks to avoid spillover between segments in cascades + const uint64_t c1 = ~0ULL/0xFFFFFFFF * 0x7FFFFFFF; + const uint64_t c2 = ~0ULL/0xFFFFFFFF * 0x3FFFFFFF; + const uint64_t c3 = ~0ULL/0xFFFFFFFF * 0x0FFFFFFF; + const uint64_t c4 = ~0ULL/0xFFFFFFFF * 0x00FFFFFF; + const uint64_t c5 = ~0ULL/0xFFFFFFFF * 0x0000FFFF; + + a |= (a >> 1) & c1; // cascade ones in non-zeroed segments + a |= (a >> 2) & c2; + a |= (a >> 4) & c3; + a |= (a >> 8) & c4; + a |= (a >> 16) & c5; + a &= m; // isolate single bit in each segment + a ^= m; // reverse isolated bits + + return a; + } + else if (width == 64) { + return a == 0 ? 1 : 0; + } + else { + TIGHTDB_ASSERT(false); + return uint64_t(-1); } +} - // This is the main finding function for Array. Other finding functions are just wrappers around this one. - // Search for 'value' using condition cond2 (EQUAL, NOTEQUAL, LESS, etc) and call FIND_ACTION() or FIND_ACTION_PATTERN() for each match. Break and return if FIND_ACTION returns false or 'end' is reached. - template void Array::find_optimized(int64_t value, size_t start, size_t end, size_t baseindex, state_state *state, Callback callback) const - { - cond2 C; - TIGHTDB_ASSERT(start <= m_len && (end <= m_len || end == (size_t)-1) && start <= end); - - // Test first few items with no initial time overhead - if (start > 0) { - if (m_len > start && C(Get(start), value) && start < end) { if (!FIND_ACTION(start + baseindex, Get(start), state, callback)) return;} ++start; - if (m_len > start && C(Get(start), value) && start < end) { if (!FIND_ACTION(start + baseindex, Get(start), state, callback)) return;} ++start; - if (m_len > start && C(Get(start), value) && start < end) { if (!FIND_ACTION(start + baseindex, Get(start), state, callback)) return;} ++start; - if (m_len > start && C(Get(start), value) && start < end) { if (!FIND_ACTION(start + baseindex, Get(start), state, callback)) return;} ++start; - } +// This is the main finding function for Array. Other finding functions are just wrappers around this one. +// Search for 'value' using condition cond2 (EQUAL, NOTEQUAL, LESS, etc) and call FIND_ACTION() or FIND_ACTION_PATTERN() for each match. Break and return if FIND_ACTION returns false or 'end' is reached. +template void Array::find_optimized(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, Callback callback) const +{ + cond2 C; + TIGHTDB_ASSERT(start <= m_len && (end <= m_len || end == (size_t)-1) && start <= end); + + // Test first few items with no initial time overhead + if (start > 0) { + if (m_len > start && C(Get(start), value) && start < end) { if (!FIND_ACTION(start + baseindex, Get(start), state, callback)) return;} ++start; + if (m_len > start && C(Get(start), value) && start < end) { if (!FIND_ACTION(start + baseindex, Get(start), state, callback)) return;} ++start; + if (m_len > start && C(Get(start), value) && start < end) { if (!FIND_ACTION(start + baseindex, Get(start), state, callback)) return;} ++start; + if (m_len > start && C(Get(start), value) && start < end) { if (!FIND_ACTION(start + baseindex, Get(start), state, callback)) return;} ++start; + } - if (!(m_len > start && start < end)) - return; + if (!(m_len > start && start < end)) + return; - if (end == (size_t)-1) end = m_len; + if (end == (size_t)-1) end = m_len; - // Return immediately if no items in array can match (such as if cond2 == GREATER and value == 100 and m_ubound == 15). - if (!C.can_match(value, m_lbound, m_ubound)) - return; + // Return immediately if no items in array can match (such as if cond2 == GREATER and value == 100 and m_ubound == 15). + if (!C.can_match(value, m_lbound, m_ubound)) + return; - // call FIND_ACTION on all items in array if all items are guaranteed to match (such as cond2 == NOTEQUAL and value == 100 and m_ubound == 15) - if (C.will_match(value, m_lbound, m_ubound)) { - if (action == TDB_SUM || action == TDB_MAX || action == TDB_MIN) { - int64_t res; - if (action == TDB_SUM) - res = Array::sum(start, end); - if (action == TDB_MAX) - Array::maximum(res, start, end); - if (action == TDB_MIN) - Array::minimum(res, start, end); + // call FIND_ACTION on all items in array if all items are guaranteed to match (such as cond2 == NOTEQUAL and value == 100 and m_ubound == 15) + if (C.will_match(value, m_lbound, m_ubound)) { + if (action == TDB_SUM || action == TDB_MAX || action == TDB_MIN) { + int64_t res; + if (action == TDB_SUM) + res = Array::sum(start, end); + if (action == TDB_MAX) + Array::maximum(res, start, end); + if (action == TDB_MIN) + Array::minimum(res, start, end); - FIND_ACTION(start + baseindex, res, state, callback); - } - else if (action == TDB_COUNT) { - state->state += end - start; - } - else { - for (; start < end; start++) - if (!FIND_ACTION(start + baseindex, Get(start), state, callback)) - return; - } - return; + FIND_ACTION(start + baseindex, res, state, callback); + } + else if (action == TDB_COUNT) { + state->state += end - start; } + else { + for (; start < end; start++) + if (!FIND_ACTION(start + baseindex, Get(start), state, callback)) + return; + } + return; + } - // finder cannot handle this bitwidth - TIGHTDB_ASSERT(m_width != 0); + // finder cannot handle this bitwidth + TIGHTDB_ASSERT(m_width != 0); #if defined(TIGHTDB_COMPILER_SSE) - if ((cpuid_sse<42>() && (end - start >= sizeof(__m128i) && m_width >= 8)) - || (cpuid_sse<30>() && (SameType::value && end - start >= sizeof(__m128i) && m_width >= 8 && m_width < 64))) { + if ((cpuid_sse<42>() && (end - start >= sizeof(__m128i) && m_width >= 8)) + || (cpuid_sse<30>() && (SameType::value && end - start >= sizeof(__m128i) && m_width >= 8 && m_width < 64))) { - // FindSSE() must start at 16-byte boundary, so search area before that using CompareEquality() - __m128i* const a = (__m128i *)round_up(m_data + start * bitwidth / 8, sizeof(__m128i)); - __m128i* const b = (__m128i *)round_down(m_data + end * bitwidth / 8, sizeof(__m128i)); + // FindSSE() must start at 16-byte boundary, so search area before that using CompareEquality() + __m128i* const a = (__m128i *)round_up(m_data + start * bitwidth / 8, sizeof(__m128i)); + __m128i* const b = (__m128i *)round_down(m_data + end * bitwidth / 8, sizeof(__m128i)); - if (!Compare(value, start, ((unsigned char *)a - m_data) * 8 / NO0(bitwidth), baseindex, state, callback)) - return; + if (!Compare(value, start, ((unsigned char *)a - m_data) * 8 / NO0(bitwidth), baseindex, state, callback)) + return; - // Search aligned area with SSE - if (b > a) { - if(cpuid_sse<42>()) { - if (!FindSSE(value, a, b - a, state, baseindex + (((unsigned char *)a - m_data) * 8 / NO0(bitwidth)), callback)) - return; - } - else if(cpuid_sse<30>()) { - - if (!FindSSE(value, a, b - a, state, baseindex + (((unsigned char *)a - m_data) * 8 / NO0(bitwidth)), callback)) - return; - } - } - - // Search remainder with CompareEquality() - if (!Compare(value, ((unsigned char *)b - m_data) * 8 / NO0(bitwidth), end, baseindex, state, callback)) - return; + // Search aligned area with SSE + if (b > a) { + if (cpuid_sse<42>()) { + if (!FindSSE(value, a, b - a, state, baseindex + (((unsigned char *)a - m_data) * 8 / NO0(bitwidth)), callback)) + return; + } + else if (cpuid_sse<30>()) { - return; + if (!FindSSE(value, a, b - a, state, baseindex + (((unsigned char *)a - m_data) * 8 / NO0(bitwidth)), callback)) + return; + } } - else { - Compare(value, start, end, baseindex, state, callback); + + // Search remainder with CompareEquality() + if (!Compare(value, ((unsigned char *)b - m_data) * 8 / NO0(bitwidth), end, baseindex, state, callback)) return; - } + + return; + } + else { + Compare(value, start, end, baseindex, state, callback); + return; + } #else - Compare(value, start, end, baseindex, state, callback); - return; +Compare(value, start, end, baseindex, state, callback); +return; #endif - } - - // popcount - #if defined(_MSC_VER) && _MSC_VER >= 1500 - #include - inline int Array::fast_popcount32(uint32_t x) - { - return __popcnt(x); - } - #if defined(_M_X64) - inline int Array::fast_popcount64(unsigned __int64 x) const - { - return (int)__popcnt64(x); - } - #else - inline int Array::fast_popcount64(unsigned __int64 x) const - { - return __popcnt((unsigned)(x)) + __popcnt((unsigned)(x >> 32)); - } - #endif - #elif defined(__GNUC__) && __GNUC__ >= 4 || defined(__INTEL_COMPILER) && __INTEL_COMPILER >= 900 - #define fast_popcount32 __builtin_popcount - #if ULONG_MAX == 0xffffffff - inline int Array::fast_popcount64(unsigned long long x) const - { - return __builtin_popcount((unsigned)(x)) + __builtin_popcount((unsigned)(x >> 32)); - } - #else - inline int Array::fast_popcount64(unsigned long long x) const - { - return __builtin_popcountll(x); - } - #endif - #else - static const char a_popcount_bits[256] = { - 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, - }; - - // Masking away bits might be faster than bit shifting (which can be slow). Note that the compiler may optimize this automatically. Todo, investigate. - inline int Array::fast_popcount32(uint32_t x) const - { - return a_popcount_bits[255 & x] + a_popcount_bits[255 & x>> 8] + a_popcount_bits[255 & x>>16] + a_popcount_bits[255 & x>>24]; - } - inline int Array::fast_popcount64(uint64_t x) const - { - return fast_popcount32(x) + fast_popcount32(x >> 32); - } - - #endif // select best popcount implementations +} - template inline int64_t Array::LowerBits(void) const - { - if (width == 1) - return 0xFFFFFFFFFFFFFFFFULL; - else if (width == 2) - return 0x5555555555555555ULL; - else if (width == 4) - return 0x1111111111111111ULL; - else if (width == 8) - return 0x0101010101010101ULL; - else if (width == 16) - return 0x0001000100010001ULL; - else if (width == 32) - return 0x0000000100000001ULL; - else if (width == 64) - return 0x0000000000000001ULL; - else { - TIGHTDB_ASSERT(false); - return int64_t(-1); - } +template inline int64_t Array::LowerBits(void) const +{ + if (width == 1) + return 0xFFFFFFFFFFFFFFFFULL; + else if (width == 2) + return 0x5555555555555555ULL; + else if (width == 4) + return 0x1111111111111111ULL; + else if (width == 8) + return 0x0101010101010101ULL; + else if (width == 16) + return 0x0001000100010001ULL; + else if (width == 32) + return 0x0000000100000001ULL; + else if (width == 64) + return 0x0000000000000001ULL; + else { + TIGHTDB_ASSERT(false); + return int64_t(-1); } +} - // Tests if any chunk in 'value' is 0 - template inline bool Array::TestZero(uint64_t value) const { - uint64_t hasZeroByte; - uint64_t lower = LowerBits(); - uint64_t upper = LowerBits() * 1ULL << (width == 0 ? 0 : (width - 1ULL)); - hasZeroByte = (value - lower) & ~value & upper; - return hasZeroByte != 0; - } +// Tests if any chunk in 'value' is 0 +template inline bool Array::TestZero(uint64_t value) const +{ + uint64_t hasZeroByte; + uint64_t lower = LowerBits(); + uint64_t upper = LowerBits() * 1ULL << (width == 0 ? 0 : (width - 1ULL)); + hasZeroByte = (value - lower) & ~value & upper; + return hasZeroByte != 0; +} - // Finds first zero (if eq == true) or non-zero (if eq == false) element in v and returns its position. - // IMPORTANT: This function assumes that at least 1 item matches (test this with TestZero() or other means first)! - template size_t Array::FindZero(uint64_t v) const - { - size_t start = 0; - uint64_t hasZeroByte; - uint64_t mask = (width == 64 ? ~0ULL : ((1ULL << (width == 64 ? 0 : width)) - 1ULL)); // Warning free way of computing (1ULL << width) - 1 +// Finds first zero (if eq == true) or non-zero (if eq == false) element in v and returns its position. +// IMPORTANT: This function assumes that at least 1 item matches (test this with TestZero() or other means first)! +template size_t Array::FindZero(uint64_t v) const +{ + size_t start = 0; + uint64_t hasZeroByte; + uint64_t mask = (width == 64 ? ~0ULL : ((1ULL << (width == 64 ? 0 : width)) - 1ULL)); // Warning free way of computing (1ULL << width) - 1 - if (eq == (((v >> (width * start)) & mask) == 0)) { - return 0; - } + if (eq == (((v >> (width * start)) & mask) == 0)) { + return 0; + } - // Bisection optimization, speeds up small bitwidths with high match frequency. More partions than 2 do NOT pay off because - // the work done by TestZero() is wasted for the cases where the value exists in first half, but useful if it exists in last - // half. Sweet spot turns out to be the widths and partitions below. - if (width <= 8) { - hasZeroByte = TestZero(v | 0xffffffff00000000ULL); - if (eq ? !hasZeroByte : (v & 0x00000000ffffffffULL) == 0) { - // 00?? -> increasing - start += 64 / NO0(width) / 2; - if (width <= 4) { - hasZeroByte = TestZero(v | 0xffff000000000000ULL); - if (eq ? !hasZeroByte : (v & 0x0000ffffffffffffULL) == 0) { - // 000? - start += 64 / NO0(width) / 4; - } + // Bisection optimization, speeds up small bitwidths with high match frequency. More partions than 2 do NOT pay off because + // the work done by TestZero() is wasted for the cases where the value exists in first half, but useful if it exists in last + // half. Sweet spot turns out to be the widths and partitions below. + if (width <= 8) { + hasZeroByte = TestZero(v | 0xffffffff00000000ULL); + if (eq ? !hasZeroByte : (v & 0x00000000ffffffffULL) == 0) { + // 00?? -> increasing + start += 64 / NO0(width) / 2; + if (width <= 4) { + hasZeroByte = TestZero(v | 0xffff000000000000ULL); + if (eq ? !hasZeroByte : (v & 0x0000ffffffffffffULL) == 0) { + // 000? + start += 64 / NO0(width) / 4; } } - else { - if (width <= 4) { - // ??00 - hasZeroByte = TestZero(v | 0xffffffffffff0000ULL); - if (eq ? !hasZeroByte : (v & 0x000000000000ffffULL) == 0) { - // 0?00 - start += 64 / NO0(width) / 4; - } + } + else { + if (width <= 4) { + // ??00 + hasZeroByte = TestZero(v | 0xffffffffffff0000ULL); + if (eq ? !hasZeroByte : (v & 0x000000000000ffffULL) == 0) { + // 0?00 + start += 64 / NO0(width) / 4; } } } - - while (eq == (((v >> (width * start)) & mask) != 0)) { - TIGHTDB_ASSERT(start <= 8 * sizeof(v)); // You must only call FindZero() if you are sure that at least 1 item matches - start++; - } - - return start; } - // Generate a magic constant used for later bithacks - template int64_t Array::FindGTLT_Magic(int64_t v) const - { - uint64_t mask1 = (width == 64 ? ~0ULL : ((1ULL << (width == 64 ? 0 : width)) - 1ULL)); // Warning free way of computing (1ULL << width) - 1 - uint64_t mask2 = mask1 >> 1; - uint64_t magic = gt ? (~0ULL / NO0(mask1) * (mask2 - v)) : (~0ULL / NO0(mask1) * v); - return magic; + while (eq == (((v >> (width * start)) & mask) != 0)) { + TIGHTDB_ASSERT(start <= 8 * sizeof(v)); // You must only call FindZero() if you are sure that at least 1 item matches + start++; } - template bool Array::FindGTLT_Fast(uint64_t chunk, uint64_t magic, state_state *state, size_t baseindex, Callback callback) const - { - // Tests if a a chunk of values contains values that are greater (if gt == true) or less (if gt == false) than v. - // Fast, but limited to work when all values in the chunk are positive. - - uint64_t mask1 = (width == 64 ? ~0ULL : ((1ULL << (width == 64 ? 0 : width)) - 1ULL)); // Warning free way of computing (1ULL << width) - 1 - uint64_t mask2 = mask1 >> 1; - uint64_t m = gt ? (((chunk + magic) | chunk) & ~0ULL / NO0(mask1) * (mask2 + 1)) : ((chunk - magic) & ~chunk&~0ULL/NO0(mask1)*(mask2+1)); - size_t p = 0; - while(m) { - if (FIND_ACTION_PATTERN(baseindex, m >> (NO0(width) - 1), state, callback)) - break; // consumed, so do not call FIND_ACTION() - - size_t t = FirstSetBit64(m) / NO0(width); - p += t; - if (!FIND_ACTION(p + baseindex, (chunk >> (p * width)) & mask1, state, callback)) - return false; + return start; +} - if ((t + 1) * width == 64) - m = 0; - else - m >>= (t + 1) * width; - p++; - } +// Generate a magic constant used for later bithacks +template int64_t Array::FindGTLT_Magic(int64_t v) const +{ + uint64_t mask1 = (width == 64 ? ~0ULL : ((1ULL << (width == 64 ? 0 : width)) - 1ULL)); // Warning free way of computing (1ULL << width) - 1 + uint64_t mask2 = mask1 >> 1; + uint64_t magic = gt ? (~0ULL / NO0(mask1) * (mask2 - v)) : (~0ULL / NO0(mask1) * v); + return magic; +} - return true; +template bool Array::FindGTLT_Fast(uint64_t chunk, uint64_t magic, QueryState* state, size_t baseindex, Callback callback) const +{ + // Tests if a a chunk of values contains values that are greater (if gt == true) or less (if gt == false) than v. + // Fast, but limited to work when all values in the chunk are positive. + + uint64_t mask1 = (width == 64 ? ~0ULL : ((1ULL << (width == 64 ? 0 : width)) - 1ULL)); // Warning free way of computing (1ULL << width) - 1 + uint64_t mask2 = mask1 >> 1; + uint64_t m = gt ? (((chunk + magic) | chunk) & ~0ULL / NO0(mask1) * (mask2 + 1)) : ((chunk - magic) & ~chunk&~0ULL/NO0(mask1)*(mask2+1)); + size_t p = 0; + while(m) { + if (FIND_ACTION_PATTERN(baseindex, m >> (NO0(width) - 1), state, callback)) + break; // consumed, so do not call FIND_ACTION() + + size_t t = FirstSetBit64(m) / NO0(width); + p += t; + if (!FIND_ACTION(p + baseindex, (chunk >> (p * width)) & mask1, state, callback)) + return false; + + if ((t + 1) * width == 64) + m = 0; + else + m >>= (t + 1) * width; + p++; } + return true; +} - template bool Array::FindGTLT(int64_t v, uint64_t chunk, state_state *state, size_t baseindex, Callback callback) const - { - // Fínd items in 'chunk' that are greater (if gt == true) or smaller (if gt == false) than 'v'. Fixme, __forceinline can make it crash in vS2010 - find out why - if (width == 1) { - for (size_t t = 0; t < 64; t++) { - if (gt ? (int64_t)(chunk & 0x1) > v : (int64_t)(chunk & 0x1) < v) {if (!FIND_ACTION( t + baseindex, (int64_t)(chunk & 0x1), state, callback)) return false;} chunk >>= 1; - } - } - else if (width == 2) { - // Alot (50% +) faster than loop/compiler-unrolled loop - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 0 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 1 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 2 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 3 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 4 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 5 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 6 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 7 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 8 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 9 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 10 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 11 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 12 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 13 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 14 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 15 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 16 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 17 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 18 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 19 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 20 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 21 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 22 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 23 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 24 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 25 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 26 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 27 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 28 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 29 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 30 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 31 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; - } - else if (width == 4) { - // 128 ms: - if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 0 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; - if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 1 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; - if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 2 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; - if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 3 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; - if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 4 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; - if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 5 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; - if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 6 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; - if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 7 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; - - if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 8 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; - if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 9 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; - if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 10 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; - if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 11 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; - if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 12 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; - if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 13 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; - if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 14 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; - if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 15 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; - - // 187 ms: - // if (gt ? (int64_t)(chunk >> 0*4) & 0xf > v : (int64_t)(chunk >> 0*4) & 0xf < v) return 0; + +template bool Array::FindGTLT(int64_t v, uint64_t chunk, QueryState* state, size_t baseindex, Callback callback) const +{ + // Fínd items in 'chunk' that are greater (if gt == true) or smaller (if gt == false) than 'v'. Fixme, __forceinline can make it crash in vS2010 - find out why + if (width == 1) { + for (size_t t = 0; t < 64; t++) { + if (gt ? (int64_t)(chunk & 0x1) > v : (int64_t)(chunk & 0x1) < v) {if (!FIND_ACTION( t + baseindex, (int64_t)(chunk & 0x1), state, callback)) return false;} chunk >>= 1; } - else if (width == 8) { - // 88 ms: - if (gt ? (char)chunk > v : (char)chunk < v) {if (!FIND_ACTION( 0 + baseindex, (char)chunk, state, callback)) return false;} chunk >>= 8; - if (gt ? (char)chunk > v : (char)chunk < v) {if (!FIND_ACTION( 1 + baseindex, (char)chunk, state, callback)) return false;} chunk >>= 8; - if (gt ? (char)chunk > v : (char)chunk < v) {if (!FIND_ACTION( 2 + baseindex, (char)chunk, state, callback)) return false;} chunk >>= 8; - if (gt ? (char)chunk > v : (char)chunk < v) {if (!FIND_ACTION( 3 + baseindex, (char)chunk, state, callback)) return false;} chunk >>= 8; - if (gt ? (char)chunk > v : (char)chunk < v) {if (!FIND_ACTION( 4 + baseindex, (char)chunk, state, callback)) return false;} chunk >>= 8; - if (gt ? (char)chunk > v : (char)chunk < v) {if (!FIND_ACTION( 5 + baseindex, (char)chunk, state, callback)) return false;} chunk >>= 8; - if (gt ? (char)chunk > v : (char)chunk < v) {if (!FIND_ACTION( 6 + baseindex, (char)chunk, state, callback)) return false;} chunk >>= 8; - if (gt ? (char)chunk > v : (char)chunk < v) {if (!FIND_ACTION( 7 + baseindex, (char)chunk, state, callback)) return false;} chunk >>= 8; + } + else if (width == 2) { + // Alot (50% +) faster than loop/compiler-unrolled loop + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 0 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 1 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 2 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 3 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 4 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 5 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 6 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 7 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 8 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 9 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 10 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 11 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 12 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 13 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 14 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 15 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 16 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 17 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 18 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 19 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 20 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 21 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 22 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 23 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 24 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 25 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 26 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 27 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 28 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 29 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 30 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + if (gt ? (int64_t)(chunk & 0x3) > v : (int64_t)(chunk & 0x3) < v) {if (!FIND_ACTION( 31 + baseindex, (int64_t)(chunk & 0x3), state, callback)) return false;} chunk >>= 2; + } + else if (width == 4) { + // 128 ms: + if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 0 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; + if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 1 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; + if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 2 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; + if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 3 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; + if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 4 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; + if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 5 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; + if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 6 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; + if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 7 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; + + if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 8 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; + if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 9 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; + if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 10 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; + if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 11 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; + if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 12 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; + if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 13 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; + if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 14 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; + if (gt ? (int64_t)(chunk & 0xf) > v : (int64_t)(chunk & 0xf) < v) {if (!FIND_ACTION( 15 + baseindex, (int64_t)(chunk & 0xf), state, callback)) return false;} chunk >>= 4; + + // 187 ms: + // if (gt ? (int64_t)(chunk >> 0*4) & 0xf > v : (int64_t)(chunk >> 0*4) & 0xf < v) return 0; + } + else if (width == 8) { + // 88 ms: + if (gt ? (char)chunk > v : (char)chunk < v) {if (!FIND_ACTION( 0 + baseindex, (char)chunk, state, callback)) return false;} chunk >>= 8; + if (gt ? (char)chunk > v : (char)chunk < v) {if (!FIND_ACTION( 1 + baseindex, (char)chunk, state, callback)) return false;} chunk >>= 8; + if (gt ? (char)chunk > v : (char)chunk < v) {if (!FIND_ACTION( 2 + baseindex, (char)chunk, state, callback)) return false;} chunk >>= 8; + if (gt ? (char)chunk > v : (char)chunk < v) {if (!FIND_ACTION( 3 + baseindex, (char)chunk, state, callback)) return false;} chunk >>= 8; + if (gt ? (char)chunk > v : (char)chunk < v) {if (!FIND_ACTION( 4 + baseindex, (char)chunk, state, callback)) return false;} chunk >>= 8; + if (gt ? (char)chunk > v : (char)chunk < v) {if (!FIND_ACTION( 5 + baseindex, (char)chunk, state, callback)) return false;} chunk >>= 8; + if (gt ? (char)chunk > v : (char)chunk < v) {if (!FIND_ACTION( 6 + baseindex, (char)chunk, state, callback)) return false;} chunk >>= 8; + if (gt ? (char)chunk > v : (char)chunk < v) {if (!FIND_ACTION( 7 + baseindex, (char)chunk, state, callback)) return false;} chunk >>= 8; - //97 ms ms: - // if (gt ? (char)(chunk >> 0*8) > v : (char)(chunk >> 0*8) < v) return 0; - } - else if (width == 16) { - - if (gt ? (short int)(chunk >> 0*16) > v : (short int)(chunk >> 0*16) < v) {if (!FIND_ACTION( 0 + baseindex, (short int)(chunk >> 0*16), state, callback)) return false;}; - if (gt ? (short int)(chunk >> 1*16) > v : (short int)(chunk >> 1*16) < v) {if (!FIND_ACTION( 1 + baseindex, (short int)(chunk >> 1*16), state, callback)) return false;}; - if (gt ? (short int)(chunk >> 2*16) > v : (short int)(chunk >> 2*16) < v) {if (!FIND_ACTION( 2 + baseindex, (short int)(chunk >> 2*16), state, callback)) return false;}; - if (gt ? (short int)(chunk >> 3*16) > v : (short int)(chunk >> 3*16) < v) {if (!FIND_ACTION( 3 + baseindex, (short int)(chunk >> 3*16), state, callback)) return false;}; - - /* - // Faster but disabled due to bug in VC2010 compiler (fixed in 2012 toolchain) where last 'if' is errorneously optimized away - if (gt ? (short int)chunk > v : (short int)chunk < v) {if (!state->AddPositiveLocal(0 + baseindex); else return 0;} chunk >>= 16; - if (gt ? (short int)chunk > v : (short int)chunk < v) {if (!state->AddPositiveLocal(1 + baseindex); else return 1;} chunk >>= 16; - if (gt ? (short int)chunk > v : (short int)chunk < v) {if (!state->AddPositiveLocal(2 + baseindex); else return 2;} chunk >>= 16; - if (gt ? (short int)chunk > v : (short int)chunk < v) {if (!state->AddPositiveLocal(3 + baseindex); else return 3;} chunk >>= 16; - - // Following illustrates it: - #include - #include - #include - - size_t bug(int64_t v, uint64_t chunk) - { - bool gt = true; - - if (gt ? (short int)chunk > v : (short int)chunk < v) {return 0;} chunk >>= 16; - if (gt ? (short int)chunk > v : (short int)chunk < v) {return 1;} chunk >>= 16; - if (gt ? (short int)chunk > v : (short int)chunk < v) {return 2;} chunk >>= 16; - if (gt ? (short int)chunk > v : (short int)chunk < v) {return 3;} chunk >>= 16; - - return -1; - } + //97 ms ms: + // if (gt ? (char)(chunk >> 0*8) > v : (char)(chunk >> 0*8) < v) return 0; + } + else if (width == 16) { + + if (gt ? (short int)(chunk >> 0*16) > v : (short int)(chunk >> 0*16) < v) {if (!FIND_ACTION( 0 + baseindex, (short int)(chunk >> 0*16), state, callback)) return false;}; + if (gt ? (short int)(chunk >> 1*16) > v : (short int)(chunk >> 1*16) < v) {if (!FIND_ACTION( 1 + baseindex, (short int)(chunk >> 1*16), state, callback)) return false;}; + if (gt ? (short int)(chunk >> 2*16) > v : (short int)(chunk >> 2*16) < v) {if (!FIND_ACTION( 2 + baseindex, (short int)(chunk >> 2*16), state, callback)) return false;}; + if (gt ? (short int)(chunk >> 3*16) > v : (short int)(chunk >> 3*16) < v) {if (!FIND_ACTION( 3 + baseindex, (short int)(chunk >> 3*16), state, callback)) return false;}; + + /* + // Faster but disabled due to bug in VC2010 compiler (fixed in 2012 toolchain) where last 'if' is errorneously optimized away + if (gt ? (short int)chunk > v : (short int)chunk < v) {if (!state->AddPositiveLocal(0 + baseindex); else return 0;} chunk >>= 16; + if (gt ? (short int)chunk > v : (short int)chunk < v) {if (!state->AddPositiveLocal(1 + baseindex); else return 1;} chunk >>= 16; + if (gt ? (short int)chunk > v : (short int)chunk < v) {if (!state->AddPositiveLocal(2 + baseindex); else return 2;} chunk >>= 16; + if (gt ? (short int)chunk > v : (short int)chunk < v) {if (!state->AddPositiveLocal(3 + baseindex); else return 3;} chunk >>= 16; + + // Following illustrates it: + #include + #include + #include + + size_t bug(int64_t v, uint64_t chunk) + { + bool gt = true; - int main(int argc, char const *const argv[]) - { - int64_t v; - if (rand()*rand() == 3) { - v = rand()*rand()*rand()*rand()*rand(); - printf("Change '3' to something else and run test again\n"); - } - else { - v = 0x2222000000000000ULL; - } + if (gt ? (short int)chunk > v : (short int)chunk < v) {return 0;} chunk >>= 16; + if (gt ? (short int)chunk > v : (short int)chunk < v) {return 1;} chunk >>= 16; + if (gt ? (short int)chunk > v : (short int)chunk < v) {return 2;} chunk >>= 16; + if (gt ? (short int)chunk > v : (short int)chunk < v) {return 3;} chunk >>= 16; - size_t idx; - - idx = bug(200, v); - if (idx != 3) - printf("Compiler failed: idx == %d (expected idx == 3)\n", idx); + return -1; + } - v = 0x2222000000000000ULL; - idx = bug(200, v); - if (idx == 3) - printf("Touching v made it work\n", idx); + int main(int argc, char const *const argv[]) + { + int64_t v; + if (rand()*rand() == 3) { + v = rand()*rand()*rand()*rand()*rand(); + printf("Change '3' to something else and run test again\n"); } - */ - } - else if (width == 32) { - if (gt ? (int)chunk > v : (int)chunk < v) {if (!FIND_ACTION( 0 + baseindex, (int)chunk, state, callback)) return false;} chunk >>= 32; - if (gt ? (int)chunk > v : (int)chunk < v) {if (!FIND_ACTION( 1 + baseindex, (int)chunk, state, callback)) return false;} chunk >>= 32; - } - else if (width == 64) { - if (gt ? (int64_t)v > v : (int64_t)(v) < v) {if (!FIND_ACTION( 0 + baseindex, (int64_t)v, state, callback)) return false;}; - } + else { + v = 0x2222000000000000ULL; + } - return true; + size_t idx; + + idx = bug(200, v); + if (idx != 3) + printf("Compiler failed: idx == %d (expected idx == 3)\n", idx); + + v = 0x2222000000000000ULL; + idx = bug(200, v); + if (idx == 3) + printf("Touching v made it work\n", idx); + } + */ + } + else if (width == 32) { + if (gt ? (int)chunk > v : (int)chunk < v) {if (!FIND_ACTION( 0 + baseindex, (int)chunk, state, callback)) return false;} chunk >>= 32; + if (gt ? (int)chunk > v : (int)chunk < v) {if (!FIND_ACTION( 1 + baseindex, (int)chunk, state, callback)) return false;} chunk >>= 32; + } + else if (width == 64) { + if (gt ? (int64_t)v > v : (int64_t)(v) < v) {if (!FIND_ACTION( 0 + baseindex, (int64_t)v, state, callback)) return false;}; } + return true; +} - template inline bool Array::CompareEquality(int64_t value, size_t start, size_t end, size_t baseindex, state_state *state, Callback callback) const { - // Find items in this Array that are equal (eq == true) or different (eq = false) from 'value' - TIGHTDB_ASSERT(start <= m_len && (end <= m_len || end == (size_t)-1) && start <= end); +template inline bool Array::CompareEquality(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, Callback callback) const +{ + // Find items in this Array that are equal (eq == true) or different (eq = false) from 'value' - size_t ee = round_up(start, 64 / NO0(width)); - ee = ee > end ? end : ee; - for (; start < ee; ++start) - if (eq ? (Get(start) == value) : (Get(start) != value)) { - if (!FIND_ACTION(start + baseindex, Get(start), state, callback)) - return false; - } + TIGHTDB_ASSERT(start <= m_len && (end <= m_len || end == (size_t)-1) && start <= end); + + size_t ee = round_up(start, 64 / NO0(width)); + ee = ee > end ? end : ee; + for (; start < ee; ++start) + if (eq ? (Get(start) == value) : (Get(start) != value)) { + if (!FIND_ACTION(start + baseindex, Get(start), state, callback)) + return false; + } - if (start >= end) - return true; + if (start >= end) + return true; - if (width != 32 && width != 64) { - const int64_t* p = (const int64_t*)(m_data + (start * width / 8)); - const int64_t* const e = (int64_t*)(m_data + (end * width / 8)) - 1; - const uint64_t mask = (width == 64 ? ~0ULL : ((1ULL << (width == 64 ? 0 : width)) - 1ULL)); // Warning free way of computing (1ULL << width) - 1 - const uint64_t valuemask = ~0ULL / NO0(mask) * (value & mask); // the "== ? :" is to avoid division by 0 compiler error + if (width != 32 && width != 64) { + const int64_t* p = (const int64_t*)(m_data + (start * width / 8)); + const int64_t* const e = (int64_t*)(m_data + (end * width / 8)) - 1; + const uint64_t mask = (width == 64 ? ~0ULL : ((1ULL << (width == 64 ? 0 : width)) - 1ULL)); // Warning free way of computing (1ULL << width) - 1 + const uint64_t valuemask = ~0ULL / NO0(mask) * (value & mask); // the "== ? :" is to avoid division by 0 compiler error - while (p < e) { - uint64_t chunk = *p; - uint64_t v2 = chunk ^ valuemask; - start = (p - (int64_t *)m_data) * 8 * 8 / NO0(width); - size_t a = 0; + while (p < e) { + uint64_t chunk = *p; + uint64_t v2 = chunk ^ valuemask; + start = (p - (int64_t *)m_data) * 8 * 8 / NO0(width); + size_t a = 0; - while (eq ? TestZero(v2) : v2) { + while (eq ? TestZero(v2) : v2) { - if (FIND_ACTION_PATTERN(start + baseindex, cascade(eq ? v2 : ~v2), state, callback)) - break; // consumed + if (FIND_ACTION_PATTERN(start + baseindex, cascade(eq ? v2 : ~v2), state, callback)) + break; // consumed - size_t t = FindZero(v2); - a += t; + size_t t = FindZero(v2); + a += t; - if (a >= 64 / NO0(width)) - break; + if (a >= 64 / NO0(width)) + break; - if (!FIND_ACTION(a + start + baseindex, Get(start + t), state, callback)) - return false; - v2 >>= (t + 1) * width; - a += 1; + if (!FIND_ACTION(a + start + baseindex, Get(start + t), state, callback)) + return false; + v2 >>= (t + 1) * width; + a += 1; - } - - ++p; } - // Loop ended because we are near end or end of array. No need to optimize search in remainder in this case because end of array means that - // lots of search work has taken place prior to ending here. So time spent searching remainder is relatively tiny - start = (p - (int64_t *)m_data) * 8 * 8 / NO0(width); + ++p; } - while (start < end) { - if (eq ? Get(start) == value : Get(start) != value) { - if (!FIND_ACTION( start + baseindex, Get(start), state, callback)) - return false; - } - ++start; - } + // Loop ended because we are near end or end of array. No need to optimize search in remainder in this case because end of array means that + // lots of search work has taken place prior to ending here. So time spent searching remainder is relatively tiny + start = (p - (int64_t *)m_data) * 8 * 8 / NO0(width); + } - return true; + while (start < end) { + if (eq ? Get(start) == value : Get(start) != value) { + if (!FIND_ACTION( start + baseindex, Get(start), state, callback)) + return false; + } + ++start; } - // There exists a couple of find() functions that take more or less template arguments. Always call the one that takes as most as possible to get - // best performance. + return true; +} + +// There exists a couple of find() functions that take more or less template arguments. Always call the one that takes as most as possible to get +// best performance. - template void Array::find(int64_t value, size_t start, size_t end, size_t baseindex, state_state *state) const - { - find(value, start, end, baseindex, state, CallbackDummy()); - } +template void Array::find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state) const +{ + find(value, start, end, baseindex, state, CallbackDummy()); +} - template void Array::find(int64_t value, size_t start, size_t end, size_t baseindex, state_state *state, Callback callback) const - { - TEMPEX4(find, cond, action, m_width, Callback, (value, start, end, baseindex, state, callback)); - } +template void Array::find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, Callback callback) const +{ + TEMPEX4(find, cond, action, m_width, Callback, (value, start, end, baseindex, state, callback)); +} - template void Array::find(int64_t value, size_t start, size_t end, size_t baseindex, state_state *state, Callback callback) const - { - #ifdef TIGHTDB_DEBUG - Array r_arr; - state_state r_state; - Array *akku = (Array*)state->state; - r_state.state = (int64_t)&r_arr; - - if (action == TDB_FINDALL) { - for (size_t t = 0; t < akku->Size(); t++) - r_arr.add(akku->Get(t)); - } - else { - r_state.state = state->state; - } - #endif - find_optimized(value, start, end, baseindex, state, callback); +template void Array::find(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, Callback callback) const +{ +#ifdef TIGHTDB_DEBUG + Array r_arr; + QueryState r_state; + Array *akku = (Array*)state->state; + r_state.state = (int64_t)&r_arr; + + if (action == TDB_FINDALL) { + for (size_t t = 0; t < akku->Size(); t++) + r_arr.add(akku->Get(t)); + } + else { + r_state.state = state->state; + } +#endif + find_optimized(value, start, end, baseindex, state, callback); - #ifdef TIGHTDB_DEBUG - - if (action == TDB_MAX || action == TDB_MIN || action == TDB_SUM || action == TDB_COUNT || action == TDB_RETURN_FIRST || action == TDB_COUNT) { - find_reference(value, start, end, baseindex, &r_state, callback); - if (action == TDB_FINDALL) - TIGHTDB_ASSERT(akku->Compare(r_arr)); - else - TIGHTDB_ASSERT(state->state == r_state.state); - } - r_arr.Destroy(); - #endif +#ifdef TIGHTDB_DEBUG + if (action == TDB_MAX || action == TDB_MIN || action == TDB_SUM || action == TDB_COUNT || action == TDB_RETURN_FIRST || action == TDB_COUNT) { + find_reference(value, start, end, baseindex, &r_state, callback); + if (action == TDB_FINDALL) + TIGHTDB_ASSERT(akku->Compare(r_arr)); + else + TIGHTDB_ASSERT(state->state == r_state.state); } + r_arr.Destroy(); +#endif - template int64_t Array::find_reference(int64_t value, size_t start, size_t end, size_t baseindex, state_state *state, Callback callback) const - { - // Reference implementation of find_optimized for bug testing - (void)callback; +} + +template int64_t Array::find_reference(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, Callback callback) const +{ + // Reference implementation of find_optimized for bug testing + (void)callback; - if (end > Size()) - end = Size(); + if (end > Size()) + end = Size(); - for (size_t t = start; t < end; t++) { - int64_t v = Get(t); + for (size_t t = start; t < end; t++) { + int64_t v = Get(t); - if (SameType::value || (SameType::value && v == value) || (SameType::value && v != value) || (SameType::value && v > value) || (SameType::value && v < value)) { - if (action == TDB_RETURN_FIRST) { - state->state = t; - return false; - } - else if (action == TDB_SUM) - state->state += v; - else if (action == TDB_MAX && v > state->state) - state->state = v; - else if (action == TDB_MIN && v < state->state) - state->state = v; - else if (action == TDB_COUNT) - state->state++; - else if (action == TDB_FINDALL) - ((Array*)state->state)->add(t + baseindex); + if (SameType::value || (SameType::value && v == value) || (SameType::value && v != value) || (SameType::value && v > value) || (SameType::value && v < value)) { + if (action == TDB_RETURN_FIRST) { + state->state = t; + return false; } - + else if (action == TDB_SUM) + state->state += v; + else if (action == TDB_MAX && v > state->state) + state->state = v; + else if (action == TDB_MIN && v < state->state) + state->state = v; + else if (action == TDB_COUNT) + state->state++; + else if (action == TDB_FINDALL) + ((Array*)state->state)->add(t + baseindex); } - if (action == TDB_RETURN_FIRST) - return false; - else - return true; } + if (action == TDB_RETURN_FIRST) + return false; + else + return true; +} + #ifdef TIGHTDB_COMPILER_SSE - // 'items' is the number of 16-byte SSE chunks. Returns index of packed element relative to first integer of first chunk - template size_t Array::FindSSE(int64_t value, __m128i *data, size_t items, state_state *state, size_t baseindex, Callback callback) const - { - cond2 C; - int cond = C.condition(); - - (void) baseindex; - (void) state; - - int resmask; - __m128i search = {0}; - __m128i compare = {0}; - size_t i = 0; - - if (width == 8) - search = _mm_set1_epi8((char)value); - else if (width == 16) - search = _mm_set1_epi16((short int)value); - else if (width == 32) - search = _mm_set1_epi32((int)value); - else if (width == 64) { - search = _mm_set_epi64x(value, value); - } +// 'items' is the number of 16-byte SSE chunks. Returns index of packed element relative to first integer of first chunk +template size_t Array::FindSSE(int64_t value, __m128i *data, size_t items, QueryState* state, size_t baseindex, Callback callback) const +{ + cond2 C; + int cond = C.condition(); + + (void) baseindex; + (void) state; + + int resmask; + __m128i search = {0}; + __m128i compare = {0}; + size_t i = 0; + + if (width == 8) + search = _mm_set1_epi8((char)value); + else if (width == 16) + search = _mm_set1_epi16((short int)value); + else if (width == 32) + search = _mm_set1_epi32((int)value); + else if (width == 64) { + search = _mm_set_epi64x(value, value); + } - // Search loop. Unrolling it has been tested to NOT increase performance (apparently mem bound) - for (i = 0; i < items; ++i) { - // equal / not-equal - if (cond == COND_EQUAL || cond == COND_NOTEQUAL) { - if (width == 8) - compare = _mm_cmpeq_epi8(data[i], search); - if (width == 16) - compare = _mm_cmpeq_epi16(data[i], search); - if (width == 32) - compare = _mm_cmpeq_epi32(data[i], search); - if (width == 64) { - compare = _mm_cmpeq_epi64(data[i], search); // SSE 4.2 only - } + // Search loop. Unrolling it has been tested to NOT increase performance (apparently mem bound) + for (i = 0; i < items; ++i) { + // equal / not-equal + if (cond == COND_EQUAL || cond == COND_NOTEQUAL) { + if (width == 8) + compare = _mm_cmpeq_epi8(data[i], search); + if (width == 16) + compare = _mm_cmpeq_epi16(data[i], search); + if (width == 32) + compare = _mm_cmpeq_epi32(data[i], search); + if (width == 64) { + compare = _mm_cmpeq_epi64(data[i], search); // SSE 4.2 only } + } - // greater - else if (cond == COND_GREATER) { - if (width == 8) - compare = _mm_cmpgt_epi8(data[i], search); - if (width == 16) - compare = _mm_cmpgt_epi16(data[i], search); - if (width == 32) - compare = _mm_cmpgt_epi32(data[i], search); - if (width == 64) - compare = _mm_cmpgt_epi64(data[i], search); - } - // less - else if (cond == COND_LESS) { - if (width == 8) - compare = _mm_cmplt_epi8(data[i], search); - if (width == 16) - compare = _mm_cmplt_epi16(data[i], search); - if (width == 32) - compare = _mm_cmplt_epi32(data[i], search); - if (width == 64){ - // There exists no _mm_cmplt_epi64 instruction, so emulate it. _mm_set1_epi8(0xff) is pre-calculated by compiler. - compare = _mm_cmpeq_epi64(data[i], search); - compare = _mm_andnot_si128(compare, _mm_set1_epi32(0xffffffff)); - } + // greater + else if (cond == COND_GREATER) { + if (width == 8) + compare = _mm_cmpgt_epi8(data[i], search); + if (width == 16) + compare = _mm_cmpgt_epi16(data[i], search); + if (width == 32) + compare = _mm_cmpgt_epi32(data[i], search); + if (width == 64) + compare = _mm_cmpgt_epi64(data[i], search); + } + // less + else if (cond == COND_LESS) { + if (width == 8) + compare = _mm_cmplt_epi8(data[i], search); + if (width == 16) + compare = _mm_cmplt_epi16(data[i], search); + if (width == 32) + compare = _mm_cmplt_epi32(data[i], search); + if (width == 64){ + // There exists no _mm_cmplt_epi64 instruction, so emulate it. _mm_set1_epi8(0xff) is pre-calculated by compiler. + compare = _mm_cmpeq_epi64(data[i], search); + compare = _mm_andnot_si128(compare, _mm_set1_epi32(0xffffffff)); } + } - resmask = _mm_movemask_epi8(compare); + resmask = _mm_movemask_epi8(compare); - if (cond == COND_NOTEQUAL) - resmask = ~resmask & 0x0000ffff; + if (cond == COND_NOTEQUAL) + resmask = ~resmask & 0x0000ffff; - size_t s = i * sizeof(__m128i) * 8 / NO0(width); + size_t s = i * sizeof(__m128i) * 8 / NO0(width); - while (resmask != 0) { + while (resmask != 0) { - uint64_t upper = LowerBits() << (NO0(width / 8) - 1); - uint64_t pattern = resmask & upper; // fixme, bits at wrong offsets. Only OK because we only use them in 'count' aggregate - if (FIND_ACTION_PATTERN(s + baseindex, pattern, state, callback)) - break; + uint64_t upper = LowerBits() << (NO0(width / 8) - 1); + uint64_t pattern = resmask & upper; // fixme, bits at wrong offsets. Only OK because we only use them in 'count' aggregate + if (FIND_ACTION_PATTERN(s + baseindex, pattern, state, callback)) + break; - size_t idx = FirstSetBit(resmask) * 8 / NO0(width); - s += idx; - if (!FIND_ACTION( s + baseindex, GetUniversal((const char *)data, s), state, callback)) - return false; - resmask >>= (idx + 1) * NO0(width) / 8; - ++s; - } + size_t idx = FirstSetBit(resmask) * 8 / NO0(width); + s += idx; + if (!FIND_ACTION( s + baseindex, GetUniversal((const char *)data, s), state, callback)) + return false; + resmask >>= (idx + 1) * NO0(width) / 8; + ++s; } - - return true; } - #endif //TIGHTDB_COMPILER_SSE - - // If gt = true: Find element(s) which are greater than value - // If gt = false: Find element(s) which are smaller than value - template bool Array::Compare(int64_t value, size_t start, size_t end, size_t baseindex, state_state *state, Callback callback) const - { - cond2 C; - int cond = C.condition(); - bool ret = false; - - if (cond == COND_EQUAL) - ret = CompareEquality(value, start, end, baseindex, state, callback); - else if (cond == COND_NOTEQUAL) - ret = CompareEquality(value, start, end, baseindex, state, callback); - else if (cond == COND_GREATER) - ret = CompareRelation(value, start, end, baseindex, state, callback); - else if (cond == COND_LESS) - ret = CompareRelation(value, start, end, baseindex, state, callback); - else - TIGHTDB_ASSERT(false); - return ret; - } + return true; +} +#endif //TIGHTDB_COMPILER_SSE - // If gt = true: Find elements that are greater than value - // If gt = false: Find elements that are smaller than value - template bool Array::CompareRelation(int64_t value, size_t start, size_t end, size_t baseindex, state_state *state, Callback callback) const - { - TIGHTDB_ASSERT(start <= m_len && (end <= m_len || end == (size_t)-1) && start <= end); - uint64_t mask = (bitwidth == 64 ? ~0ULL : ((1ULL << (bitwidth == 64 ? 0 : bitwidth)) - 1ULL)); // Warning free way of computing (1ULL << width) - 1 +// If gt = true: Find element(s) which are greater than value +// If gt = false: Find element(s) which are smaller than value +template bool Array::Compare(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, Callback callback) const +{ + cond2 C; + int cond = C.condition(); + bool ret = false; + + if (cond == COND_EQUAL) + ret = CompareEquality(value, start, end, baseindex, state, callback); + else if (cond == COND_NOTEQUAL) + ret = CompareEquality(value, start, end, baseindex, state, callback); + else if (cond == COND_GREATER) + ret = CompareRelation(value, start, end, baseindex, state, callback); + else if (cond == COND_LESS) + ret = CompareRelation(value, start, end, baseindex, state, callback); + else + TIGHTDB_ASSERT(false); + + return ret; +} - size_t ee = round_up(start, 64 / NO0(bitwidth)); - ee = ee > end ? end : ee; - for (; start < ee; start++) { - if (gt ? (Get(start) > value) : (Get(start) < value)) { - if (!FIND_ACTION(start + baseindex, Get(start), state, callback)) - return false; - } +// If gt = true: Find elements that are greater than value +// If gt = false: Find elements that are smaller than value +template bool Array::CompareRelation(int64_t value, size_t start, size_t end, size_t baseindex, QueryState* state, Callback callback) const +{ + TIGHTDB_ASSERT(start <= m_len && (end <= m_len || end == (size_t)-1) && start <= end); + uint64_t mask = (bitwidth == 64 ? ~0ULL : ((1ULL << (bitwidth == 64 ? 0 : bitwidth)) - 1ULL)); // Warning free way of computing (1ULL << width) - 1 + + size_t ee = round_up(start, 64 / NO0(bitwidth)); + ee = ee > end ? end : ee; + for (; start < ee; start++) { + if (gt ? (Get(start) > value) : (Get(start) < value)) { + if (!FIND_ACTION(start + baseindex, Get(start), state, callback)) + return false; } + } - if (start >= end) - return true; // none found, continue (return true) regardless what FIND_ACTION would have returned on match + if (start >= end) + return true; // none found, continue (return true) regardless what FIND_ACTION would have returned on match - const int64_t* p = (const int64_t*)(m_data + (start * bitwidth / 8)); - const int64_t* const e = (int64_t*)(m_data + (end * bitwidth / 8)) - 1; + const int64_t* p = (const int64_t*)(m_data + (start * bitwidth / 8)); + const int64_t* const e = (int64_t*)(m_data + (end * bitwidth / 8)) - 1; - // Matches are rare enough to setup fast linear search for remaining items. We use - // bit hacks from http://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord + // Matches are rare enough to setup fast linear search for remaining items. We use + // bit hacks from http://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord - if (bitwidth == 1 || bitwidth == 2 || bitwidth == 4 || bitwidth == 8 || bitwidth == 16) { - uint64_t magic = FindGTLT_Magic(value); + if (bitwidth == 1 || bitwidth == 2 || bitwidth == 4 || bitwidth == 8 || bitwidth == 16) { + uint64_t magic = FindGTLT_Magic(value); - // Bit hacks only work if searched item <= 127 for 'greater than' and item <= 128 for 'less than' - if (value != int64_t((magic & mask)) && value >= 0 && bitwidth >= 2 && value <= (int64_t)((mask >> 1) - (gt ? 1 : 0))) { - // 15 ms - while (p < e) { - uint64_t upper = LowerBits() << (NO0(bitwidth) - 1); - - const int64_t v = *p; - size_t idx; - - // Bit hacks only works for positive items in chunk, so test their sign bits - upper = upper & v; - - if ((bitwidth > 4 ? !upper : true)) { - // Assert that all values in chunk are positive. - TIGHTDB_ASSERT(bitwidth <= 4 || ((LowerBits() << (NO0(bitwidth) - 1)) & value) == 0); - idx = FindGTLT_Fast(v, magic, state, (p - (int64_t *)m_data) * 8 * 8 / NO0(bitwidth) + baseindex, callback); - } - else - idx = FindGTLT(value, v, state, (p - (int64_t *)m_data) * 8 * 8 / NO0(bitwidth) + baseindex, callback); - - if (!idx) - return false; - ++p; - } - } - else { - // 24 ms - while (p < e) { - int64_t v = *p; - if (!FindGTLT(value, v, state, (p - (int64_t *)m_data) * 8 * 8 / NO0(bitwidth) + baseindex, callback)) - return false; - ++p; + // Bit hacks only work if searched item <= 127 for 'greater than' and item <= 128 for 'less than' + if (value != int64_t((magic & mask)) && value >= 0 && bitwidth >= 2 && value <= (int64_t)((mask >> 1) - (gt ? 1 : 0))) { + // 15 ms + while (p < e) { + uint64_t upper = LowerBits() << (NO0(bitwidth) - 1); + + const int64_t v = *p; + size_t idx; + + // Bit hacks only works for positive items in chunk, so test their sign bits + upper = upper & v; + + if ((bitwidth > 4 ? !upper : true)) { + // Assert that all values in chunk are positive. + TIGHTDB_ASSERT(bitwidth <= 4 || ((LowerBits() << (NO0(bitwidth) - 1)) & value) == 0); + idx = FindGTLT_Fast(v, magic, state, (p - (int64_t *)m_data) * 8 * 8 / NO0(bitwidth) + baseindex, callback); } + else + idx = FindGTLT(value, v, state, (p - (int64_t *)m_data) * 8 * 8 / NO0(bitwidth) + baseindex, callback); + + if (!idx) + return false; + ++p; } - start = (p - (int64_t *)m_data) * 8 * 8 / NO0(bitwidth); } - - // matchcount logic in SIMD no longer pays off for 32/64 bit ints because we have just 4/2 elements - - // Test unaligned end and/or values of width > 16 manually - while (start < end) { - if (gt ? Get(start) > value : Get(start) < value) { - if (!FIND_ACTION( start + baseindex, Get(start), state, callback)) + else { + // 24 ms + while (p < e) { + int64_t v = *p; + if (!FindGTLT(value, v, state, (p - (int64_t *)m_data) * 8 * 8 / NO0(bitwidth) + baseindex, callback)) return false; + ++p; } - ++start; } - return true; - + start = (p - (int64_t *)m_data) * 8 * 8 / NO0(bitwidth); } - template size_t Array::find_first(int64_t value, size_t start, size_t end) const - { - cond C; - TIGHTDB_ASSERT(start <= m_len && (end <= m_len || end == (size_t)-1) && start <= end); - state_state state; - state.state = not_found; - Finder finder = m_finder[C.condition()]; - (this->*finder)(value, start, end, 0, &state); - - return size_t(state.state); + // matchcount logic in SIMD no longer pays off for 32/64 bit ints because we have just 4/2 elements + + // Test unaligned end and/or values of width > 16 manually + while (start < end) { + if (gt ? Get(start) > value : Get(start) < value) { + if (!FIND_ACTION( start + baseindex, Get(start), state, callback)) + return false; + } + ++start; } + return true; + +} + +template size_t Array::find_first(int64_t value, size_t start, size_t end) const +{ + cond C; + TIGHTDB_ASSERT(start <= m_len && (end <= m_len || end == (size_t)-1) && start <= end); + QueryState state; + state.state = not_found; + Finder finder = m_finder[C.condition()]; + (this->*finder)(value, start, end, 0, &state); + + return size_t(state.state); +} - //************************************************************************************* - // Finding code ends * - //************************************************************************************* +//************************************************************************************* +// Finding code ends * +//************************************************************************************* diff --git a/src/tightdb/array_basic.hpp b/src/tightdb/array_basic.hpp new file mode 100644 index 00000000000..f462863cb13 --- /dev/null +++ b/src/tightdb/array_basic.hpp @@ -0,0 +1,72 @@ +/************************************************************************* + * + * TIGHTDB CONFIDENTIAL + * __________________ + * + * [2011] - [2012] TightDB Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of TightDB Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to TightDB Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from TightDB Incorporated. + * + **************************************************************************/ +#ifndef TIGHTDB_ARRAY_BASIC_HPP +#define TIGHTDB_ARRAY_BASIC_HPP + +#include + +namespace tightdb { + +template +class BasicArray : public Array { +public: + explicit BasicArray(ArrayParent* parent=NULL, size_t pndx=0, Allocator& alloc=Allocator::get_default()); + BasicArray(size_t ref, ArrayParent* parent, size_t pndx, Allocator& alloc=Allocator::get_default()) TIGHTDB_NOEXCEPT; + explicit BasicArray(no_prealloc_tag) TIGHTDB_NOEXCEPT; + + T Get(size_t ndx) const TIGHTDB_NOEXCEPT; + void add(T value); + void Set(size_t ndx, T value); + void Insert(size_t ndx, T value); + void Delete(size_t ndx); + void Clear(); + + size_t Find(T target, size_t start, size_t end) const; + size_t find_first(T value, size_t start=0 , size_t end=-1) const; + void find_all(Array& result, T value, size_t add_offset = 0, size_t start = 0, size_t end = -1); + + size_t count(T value, size_t start=0, size_t end=-1) const; + // Unused: double sum(size_t start=0, size_t end=-1) const; + bool maximum(T& result, size_t start=0, size_t end=-1) const; + bool minimum(T& result, size_t start=0, size_t end=-1) const; + + /// Compare two arrays for equality. + bool Compare(const BasicArray&) const; + + +private: + virtual size_t CalcByteLen(size_t count, size_t width) const; + virtual size_t CalcItemCount(size_t bytes, size_t width) const TIGHTDB_NOEXCEPT; + virtual WidthType GetWidthType() const {return TDB_MULTIPLY;} + + template bool minmax(T& result, size_t start, size_t end) const; + static size_t create_empty_basic_array(Allocator& alloc); +}; + + +// Class typedefs for BasicArray's: ArrayFloat and ArrayDouble +typedef BasicArray ArrayFloat; +typedef BasicArray ArrayDouble; + +} // namespace tightdb + +#include + +#endif // TIGHTDB_ARRAY_BASIC_HPP diff --git a/src/tightdb/array_basic_tpl.hpp b/src/tightdb/array_basic_tpl.hpp new file mode 100644 index 00000000000..c7f60b78e5c --- /dev/null +++ b/src/tightdb/array_basic_tpl.hpp @@ -0,0 +1,286 @@ +/************************************************************************* + * + * TIGHTDB CONFIDENTIAL + * __________________ + * + * [2011] - [2012] TightDB Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of TightDB Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to TightDB Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from TightDB Incorporated. + * + **************************************************************************/ +#ifndef TIGHTDB_ARRAY_BASIC_TPL_HPP +#define TIGHTDB_ARRAY_BASIC_TPL_HPP + +namespace tightdb { + +template +inline size_t BasicArray::create_empty_basic_array(Allocator& alloc) +{ + const size_t capacity = Array::initial_capacity; + const MemRef mem_ref = alloc.Alloc(capacity); + if (!mem_ref.pointer) + return 0; + + init_header(mem_ref.pointer, false, false, TDB_MULTIPLY, sizeof(T), 0, capacity); + + return mem_ref.ref; +} + +template +inline BasicArray::BasicArray(ArrayParent *parent, size_t ndx_in_parent, Allocator& alloc) + :Array(alloc) +{ + const size_t ref = create_empty_basic_array(alloc); + if (!ref) + throw_error(ERROR_OUT_OF_MEMORY); // FIXME: Check that this exception is handled properly in callers + init_from_ref(ref); + SetParent(parent, ndx_in_parent); + update_ref_in_parent(); +} + +template +inline BasicArray::BasicArray(size_t ref, ArrayParent *parent, size_t ndx_in_parent, + Allocator& alloc) TIGHTDB_NOEXCEPT: Array(alloc) +{ + // Manually create array as doing it in initializer list + // will not be able to call correct virtual functions + init_from_ref(ref); + SetParent(const_cast(parent), ndx_in_parent); +} + +template +inline BasicArray::BasicArray(no_prealloc_tag) TIGHTDB_NOEXCEPT : Array(no_prealloc_tag()) +{ +} + + +template +inline void BasicArray::Clear() +{ + CopyOnWrite(); + + // Truncate size to zero (but keep capacity and width) + m_len = 0; + set_header_len(0); +} + +template +inline void BasicArray::add(T value) +{ + Insert(m_len, value); +} + +template +inline T BasicArray::Get(size_t ndx) const TIGHTDB_NOEXCEPT +{ + T* dataPtr = (T *)m_data + ndx; + return *dataPtr; +} + +template +inline void BasicArray::Set(size_t ndx, T value) +{ + TIGHTDB_ASSERT(ndx < m_len); + + // Check if we need to copy before modifying + if (!CopyOnWrite()) + return; + + // Set the value + T* data = (T *)m_data + ndx; + *data = value; +} + +template +void BasicArray::Insert(size_t ndx, T value) +{ + TIGHTDB_ASSERT(ndx <= m_len); + + // Check if we need to copy before modifying + (void)CopyOnWrite(); + + // Make room for the new value + if (!Alloc(m_len+1, m_width)) + return; + + // Move values below insertion + if (ndx != m_len) { + unsigned char* src = m_data + (ndx * m_width); + unsigned char* dst = src + m_width; + const size_t count = (m_len - ndx) * m_width; + memmove(dst, src, count); + // fixme: Consider std::copy() or std::copy_backward() instead. + } + + // Set the value + T* data = (T *)m_data + ndx; + *data = value; + + ++m_len; +} + +template +void BasicArray::Delete(size_t ndx) +{ + TIGHTDB_ASSERT(ndx < m_len); + + // Check if we need to copy before modifying + CopyOnWrite(); + + --m_len; + + // move data under deletion up + if (ndx < m_len) { + unsigned char* src = m_data + ((ndx+1) * m_width); + unsigned char* dst = m_data + (ndx * m_width); + const size_t len = (m_len - ndx) * m_width; + memmove(dst, src, len); // FIXME: Use std::copy() or std::copy_backward() instead. + } + + // Update length in header + set_header_len(m_len); +} + +template +bool BasicArray::Compare(const BasicArray& c) const +{ + for (size_t i = 0; i < Size(); ++i) { + if (Get(i) != c.Get(i)) + return false; + } + return true; +} + + +template +size_t BasicArray::CalcByteLen(size_t count, size_t /*width*/) const +{ + // FIXME: This arithemtic could overflow. Consider using + return 8 + (count * sizeof(T)); +} + +template +size_t BasicArray::CalcItemCount(size_t bytes, size_t /*width*/) const TIGHTDB_NOEXCEPT +{ + // fixme: ??? what about width = 0? return -1? + + const size_t bytes_without_header = bytes - 8; + return bytes_without_header / sizeof(T); +} + +template +size_t BasicArray::Find(T target, size_t start, size_t end) const +{ + if (end == (size_t)-1) + end = m_len; + if (start >= end) + return not_found; + TIGHTDB_ASSERT(start < m_len && end <= m_len && start < end); + if (m_len == 0) + return not_found; // empty list + + for (size_t i = start; i < end; ++i) { + if (target == Get(i)) + return i; + } + return not_found; +} + +template +size_t BasicArray::find_first(T value, size_t start, size_t end) const +{ + return Find(value, start, end); +} + +template +void BasicArray::find_all(Array& result, T value, size_t add_offset, size_t start, size_t end) +{ + size_t first = start - 1; + for (;;) { + first = Find(value, first + 1, end); + if (first != not_found) + result.add(first + add_offset); + else + break; + } +} + +template +size_t BasicArray::count(T value, size_t start, size_t end) const +{ + size_t count = 0; + size_t lastmatch = start - 1; + for (;;) { + lastmatch = Find(value, lastmatch+1, end); + if (lastmatch != not_found) + ++count; + else + break; + } + return count; +} + +#if 0 +// currently unused +template +double BasicArray::sum(size_t start, size_t end) const +{ + if (end == (size_t)-1) + end = m_len; + if (m_len == 0) + return 0; + TIGHTDB_ASSERT(start < m_len && end <= m_len && start < end); + + R sum = 0; + for (size_t i = start; i < end; ++i) { + sum += Get(i); + } + return sum; +} +#endif + +template template +bool BasicArray::minmax(T& result, size_t start, size_t end) const +{ + if (end == (size_t)-1) + end = m_len; + if (m_len == 0) + return false; + TIGHTDB_ASSERT(start < m_len && end <= m_len && start < end); + + T m = Get(start); + ++start; + for (; start < end; ++start) { + const T val = Get(start); + if (find_max ? val > m : val < m) + m = val; + } + result = m; + return true; +} + +template +bool BasicArray::maximum(T& result, size_t start, size_t end) const +{ + return minmax(result, start, end); +} + +template +bool BasicArray::minimum(T& result, size_t start, size_t end) const +{ + return minmax(result, start, end); +} + + +} // namespace tightdb + +#endif // TIGHTDB_ARRAY_BASIC_TPL_HPP diff --git a/src/tightdb/array_string_long.cpp b/src/tightdb/array_string_long.cpp index 2a473747d43..1ae244ff600 100644 --- a/src/tightdb/array_string_long.cpp +++ b/src/tightdb/array_string_long.cpp @@ -154,7 +154,7 @@ void ArrayStringLong::find_all(Array& result, const char* value, size_t add_offs size_t first = start - 1; for (;;) { first = FindWithLen(value, len, first + 1, end); - if (first != (size_t)-1) + if (first != not_found) result.add(first + add_offset); else break; } @@ -179,7 +179,7 @@ size_t ArrayStringLong::FindWithLen(const char* value, size_t len, size_t start, offset = end; } - return (size_t)-1; // not found + return not_found; } #ifdef TIGHTDB_DEBUG diff --git a/src/tightdb/binary_data.hpp b/src/tightdb/binary_data.hpp index d8d7831608f..6f3d6844562 100644 --- a/src/tightdb/binary_data.hpp +++ b/src/tightdb/binary_data.hpp @@ -36,7 +36,7 @@ class BinaryData { bool compare_payload(const BinaryData &b) const TIGHTDB_NOEXCEPT { - if(b.pointer == pointer && b.len == len) + if (b.pointer == pointer && b.len == len) return true; bool e = std::equal(pointer, pointer + len, b.pointer); return e; diff --git a/src/tightdb/column.cpp b/src/tightdb/column.cpp index 4d7bbb5574e..a8e46a12d95 100644 --- a/src/tightdb/column.cpp +++ b/src/tightdb/column.cpp @@ -8,7 +8,6 @@ #include #include -#include #include using namespace std; @@ -39,7 +38,7 @@ void merge_core_references(Array* vals, Array* idx0, Array* idx1, Array* idxres) void merge_core(const Array& a0, const Array& a1, Array& res); Array* merge(const Array& ArrayList); void merge_references(Array* valuelist, Array* indexlists, Array** indexresult); - + // Input: // vals: An array of values // idx0: Array of indexes pointing into vals, sorted with respect to vals @@ -250,7 +249,8 @@ namespace tightdb { size_t ColumnBase::get_size_from_ref(size_t ref, Allocator& alloc) TIGHTDB_NOEXCEPT { Array a(ref, 0, 0, alloc); - if (!a.IsNode()) return a.Size(); + if (!a.IsNode()) + return a.Size(); Array offsets(a.Get(0), 0, 0, alloc); return offsets.is_empty() ? 0 : size_t(offsets.back()); } @@ -322,14 +322,16 @@ void Column::Destroy() bool Column::is_empty() const TIGHTDB_NOEXCEPT { - if (!IsNode()) return m_array->is_empty(); + if (!IsNode()) + return m_array->is_empty(); const Array offsets = NodeGetOffsets(); return offsets.is_empty(); } size_t Column::Size() const TIGHTDB_NOEXCEPT { - if (!IsNode()) return m_array->Size(); + if (!IsNode()) + return m_array->Size(); const Array offsets = NodeGetOffsets(); return offsets.is_empty() ? 0 : size_t(offsets.back()); } @@ -337,7 +339,8 @@ size_t Column::Size() const TIGHTDB_NOEXCEPT void Column::UpdateParentNdx(int diff) { m_array->UpdateParentNdx(diff); - if (m_index) m_index->UpdateParentNdx(diff); + if (m_index) + m_index->UpdateParentNdx(diff); } // Used by column b-tree code to ensure all leaf having same type @@ -367,7 +370,8 @@ const Column Column::GetSubColumn(size_t ndx) const void Column::Clear() { m_array->Clear(); - if (m_array->IsNode()) m_array->SetType(COLUMN_NORMAL); + if (m_array->IsNode()) + m_array->SetType(COLUMN_NORMAL); } bool Column::Set(size_t ndx, int64_t value) @@ -375,10 +379,12 @@ bool Column::Set(size_t ndx, int64_t value) const int64_t oldVal = m_index ? Get(ndx) : 0; // cache oldval for index const bool res = TreeSet(ndx, value); - if (!res) return false; + if (!res) + return false; // Update index - if (m_index) m_index->Set(ndx, oldVal, value); + if (m_index) + m_index->Set(ndx, oldVal, value); return true; } @@ -393,7 +399,8 @@ bool Column::Insert(size_t ndx, int64_t value) TIGHTDB_ASSERT(ndx <= Size()); const bool res = TreeInsert(ndx, value); - if (!res) return false; + if (!res) + return false; // Update index if (m_index) { @@ -425,110 +432,40 @@ void Column::fill(size_t count) #endif } -template int64_t Column::aggregate(int64_t target, size_t start, size_t end, size_t *matchcount) const -{ -#if 1 - if (end == size_t(-1)) - end = ((Column*)this)->Size(); - Column* m_column = (Column*)this; - - // We must allocate 'node' on stack with malloca() because malloc is slow (makes aggregate on 1000 elements around 10 times - // slower because of initial overhead). - NODE* node = (NODE*)alloca(sizeof(NODE)); - new (node) NODE(target, 0); - -// static NODE node(target, NULL); - - node->QuickInit(m_column, target); - // TODO: Erase matchcount - int64_t r = node->template aggregate(0, start, end, size_t(-1), size_t(-1), matchcount); - node->Destroy(); - return r; -#else - // Experimental - - if (end == size_t(-1)) end = ((Column*)this)->Size(); - Column* m_column = (Column*)this; - // To make column aggregates fast on few number of values we need low initial overhead - // so we allocate Array instance from stack and use fast constructor intended for read-only use - // with GetDirect(): - -// #define ARRAYPTR - -// Array *m_array = (Array*)alloca(sizeof(Array)); // Fast -// new (m_array) Array(false); - - static Array m_array; // Fast but very bad practise - // Array m_array; // Around 10 times slower for 1000 items - // Array *m_array = new Array(false); // Also 10 times slower - - size_t m_leaf_start = 0; - size_t m_leaf_end = 0; - size_t m_local_end = 0; - state_state state; - -#ifdef ARRAYPTR - m_array->state_init(action, &state, NULL); -#else - m_array.state_init(action, &state, NULL); -#endif - - for (size_t s = start; s < end; ) { - // Cache internal leafs - if (s >= m_leaf_end) { -#ifdef ARRAYPTR - m_column->GetBlock(s, *m_array, m_leaf_start); - const size_t leaf_size = m_array->Size(); -#else - m_column->GetBlock(s, m_array, m_leaf_start); - const size_t leaf_size = m_array.Size(); -#endif - m_leaf_end = m_leaf_start + leaf_size; - const size_t e = end - m_leaf_start; - m_local_end = leaf_size < e ? leaf_size : e; - } -#ifdef ARRAYPTR - m_array->find(target, s - m_leaf_start, m_local_end, 0, &state, &tightdb_dummy); -#else - m_array.find(target, s - m_leaf_start, m_local_end, 0, &state, &tightdb_dummy); -#endif - s = m_leaf_end; - } - - return state.state; -#endif -} +// int64_t specific: size_t Column::count(int64_t target) const { - return size_t(aggregate(target, 0, ((Column*)this)->Size())); + return size_t(aggregate(target, 0, Size(), NULL)); } int64_t Column::sum(size_t start, size_t end) const { - return aggregate(0, start, end); + return aggregate(0, start, end, NULL); } double Column::average(size_t start, size_t end) const { if (end == size_t(-1)) - end = ((Column*)this)->Size(); + end = Size(); size_t size = end - start; - size_t sum = aggregate(0, start, end); + int64_t sum = aggregate(0, start, end, NULL); double avg = double( sum ) / double( size == 0 ? 1 : size ); return avg; } int64_t Column::minimum(size_t start, size_t end) const { - return aggregate(0, start, end); + return aggregate(0, start, end, NULL); } int64_t Column::maximum(size_t start, size_t end) const { - return aggregate(0, start, end); + return aggregate(0, start, end, NULL); } + + void Column::sort(size_t start, size_t end) { Array arr; diff --git a/src/tightdb/column.hpp b/src/tightdb/column.hpp index 43678e47afc..a64826e9a62 100644 --- a/src/tightdb/column.hpp +++ b/src/tightdb/column.hpp @@ -41,8 +41,6 @@ class ColumnBase { virtual void SetHasRefs() {}; virtual bool IsIntColumn() const TIGHTDB_NOEXCEPT {return false;} - virtual bool IsStringColumn() const TIGHTDB_NOEXCEPT {return false;} - virtual bool IsBinaryColumn() const TIGHTDB_NOEXCEPT {return false;} virtual size_t Size() const TIGHTDB_NOEXCEPT = 0; @@ -95,7 +93,9 @@ class ColumnBase { }; // Tree functions +public: template T TreeGet(size_t ndx) const; // FIXME: This one should probably be eliminated because it throws due to dynamic memory allocation +protected: template bool TreeSet(size_t ndx, T value); template bool TreeInsert(size_t ndx, T value); template NodeChange DoInsert(size_t ndx, T value); @@ -115,6 +115,11 @@ class ColumnBase { template bool NodeInsertSplit(size_t ndx, size_t newRef); size_t GetRefSize(size_t ref) const; + static std::size_t get_size_from_ref(std::size_t ref, Allocator&) TIGHTDB_NOEXCEPT; + + template + R aggregate(T target, size_t start, size_t end, size_t *matchcount) const; + #ifdef TIGHTDB_DEBUG void ArrayToDot(std::ostream& out, const Array& array) const; virtual void LeafToDot(std::ostream& out, const Array& array) const; @@ -122,11 +127,10 @@ class ColumnBase { // Member variables mutable Array* m_array; // FIXME: This should not be mutable - - static std::size_t get_size_from_ref(std::size_t ref, Allocator&) TIGHTDB_NOEXCEPT; }; -class Column: public ColumnBase { + +class Column : public ColumnBase { public: explicit Column(Allocator&); Column(ColumnDef type, Allocator&); @@ -230,7 +234,7 @@ class Column: public ColumnBase { } void DoSort(size_t lo, size_t hi); - template int64_t aggregate(int64_t target, size_t start, size_t end, size_t *matchcount = 0) const; + // Member variables Index* m_index; @@ -249,7 +253,7 @@ inline int64_t Column::Get(std::size_t ndx) const TIGHTDB_NOEXCEPT inline std::size_t Column::GetAsRef(std::size_t ndx) const TIGHTDB_NOEXCEPT { - return to_ref(m_array->ColumnGet(ndx)); + return to_ref(Get(ndx)); } } // namespace tightdb diff --git a/src/tightdb/column_basic.hpp b/src/tightdb/column_basic.hpp new file mode 100644 index 00000000000..04851ee7d33 --- /dev/null +++ b/src/tightdb/column_basic.hpp @@ -0,0 +1,104 @@ +/************************************************************************* + * + * TIGHTDB CONFIDENTIAL + * __________________ + * + * [2011] - [2012] TightDB Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of TightDB Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to TightDB Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from TightDB Incorporated. + * + **************************************************************************/ +#ifndef TIGHTDB_COLUMN_BASIC_HPP +#define TIGHTDB_COLUMN_BASIC_HPP + +#include +#include + +namespace tightdb { + +template +class BasicColumn : public ColumnBase { +public: + BasicColumn(Allocator& alloc=Allocator::get_default()); + BasicColumn(size_t ref, ArrayParent* parent=NULL, size_t pndx=0, Allocator& alloc=Allocator::get_default()); + ~BasicColumn(); + + void Destroy(); + + virtual size_t Size() const TIGHTDB_NOEXCEPT; + bool is_empty() const TIGHTDB_NOEXCEPT; + + T Get(size_t ndx) const; + virtual bool add() {add(0); return true;} + bool add(T value); + bool Set(size_t ndx, T value); + virtual void insert(size_t ndx) { bool ok = Insert(ndx, 0); TIGHTDB_ASSERT(ok); (void)ok;} + bool Insert(size_t ndx, T value); + void Delete(size_t ndx); + void Clear(); + void Resize(size_t ndx); + void fill(size_t count); + + size_t count(T value) const; + T sum(size_t start = 0, size_t end = -1) const; + double average(size_t start = 0, size_t end = -1) const; + T maximum(size_t start = 0, size_t end = -1) const; + T minimum(size_t start = 0, size_t end = -1) const; + size_t find_first(T value, size_t start=0 , size_t end=-1) const; + void find_all(Array& result, T value, size_t start = 0, size_t end = -1) const; + + // Index + bool HasIndex() const {return false;} + void BuildIndex(Index&) {} + void ClearIndex() {} + size_t FindWithIndex(int64_t) const {return (size_t)-1;} + + size_t GetRef() const {return m_array->GetRef();} + void SetParent(ArrayParent* parent, size_t pndx) {m_array->SetParent(parent, pndx);} + + /// Compare two columns for equality. + bool Compare(const BasicColumn&) const; + +#ifdef TIGHTDB_DEBUG + void Verify() const {}; // Must be upper case to avoid conflict with macro in ObjC +#endif // TIGHTDB_DEBUG + +protected: + friend class ColumnBase; + + void UpdateRef(size_t ref); + + T LeafGet(size_t ndx) const TIGHTDB_NOEXCEPT; + bool LeafSet(size_t ndx, T value); + bool LeafInsert(size_t ndx, T value); + void LeafDelete(size_t ndx); + + template size_t LeafFind(T value, size_t start, size_t end) const; + void LeafFindAll(Array& result, T value, size_t add_offset = 0, size_t start = 0, size_t end = -1) const; + +#ifdef TIGHTDB_DEBUG + virtual void LeafToDot(std::ostream& out, const Array& array) const; +#endif // TIGHTDB_DEBUG + + template + R aggregate(T target, size_t start, size_t end, size_t *matchcount = (size_t*)0) const; +}; + + +} // namespace tightdb + + +// template implementation +#include + + +#endif // TIGHTDB_COLUMN_BASIC_HPP diff --git a/src/tightdb/column_basic_tpl.hpp b/src/tightdb/column_basic_tpl.hpp new file mode 100644 index 00000000000..a7b1f7dc565 --- /dev/null +++ b/src/tightdb/column_basic_tpl.hpp @@ -0,0 +1,468 @@ +/************************************************************************* + * + * TIGHTDB CONFIDENTIAL + * __________________ + * + * [2011] - [2012] TightDB Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of TightDB Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to TightDB Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from TightDB Incorporated. + * + **************************************************************************/ +#ifndef TIGHTDB_COLUMN_BASIC_TPL_HPP +#define TIGHTDB_COLUMN_BASIC_TPL_HPP + +#include + + +namespace { + +using namespace tightdb; + +// TODO: IsNodeFromRef() could be common in column.hpp? +bool IsNodeFromRef(size_t ref, Allocator& alloc) +{ + const uint8_t* const header = (uint8_t*)alloc.Translate(ref); + const bool isNode = (header[0] & 0x80) != 0; + return isNode; +} + +} + + +namespace tightdb { + +// Predeclarations from query_engine.hpp +class ParentNode; +template class BASICNODE; +template class SequentialGetter; + + +template +BasicColumn::BasicColumn(Allocator& alloc) +{ + m_array = new BasicArray(NULL, 0, alloc); +} + +template +BasicColumn::BasicColumn(size_t ref, ArrayParent* parent, size_t pndx, Allocator& alloc) +{ + const bool isNode = IsNodeFromRef(ref, alloc); + if (isNode) + m_array = new Array(ref, parent, pndx, alloc); + else + m_array = new BasicArray(ref, parent, pndx, alloc); +} + +template +BasicColumn::~BasicColumn() +{ + if (IsNode()) + delete m_array; + else + delete (BasicArray*)m_array; +} + +template +void BasicColumn::Destroy() +{ + if (IsNode()) + m_array->Destroy(); + else + ((BasicArray*)m_array)->Destroy(); +} + + +template +void BasicColumn::UpdateRef(size_t ref) +{ + TIGHTDB_ASSERT(IsNodeFromRef(ref, m_array->GetAllocator())); // Can only be called when creating node + + if (IsNode()) + m_array->UpdateRef(ref); + else { + ArrayParent *const parent = m_array->GetParent(); + const size_t pndx = m_array->GetParentNdx(); + + // Replace the generic array with int array for node + Array* array = new Array(ref, parent, pndx, m_array->GetAllocator()); + delete m_array; + m_array = array; + + // Update ref in parent + if (parent) + parent->update_child_ref(pndx, ref); + } +} + +template +bool BasicColumn::is_empty() const TIGHTDB_NOEXCEPT +{ + if (IsNode()) { + const Array offsets = NodeGetOffsets(); + return offsets.is_empty(); + } + else { + return ((BasicArray*)m_array)->is_empty(); + } +} + +template +size_t BasicColumn::Size() const TIGHTDB_NOEXCEPT +{ + if (IsNode()) { + const Array offsets = NodeGetOffsets(); + const size_t size = offsets.is_empty() ? 0 : (size_t)offsets.back(); + return size; + } + else { + return ((BasicArray*)m_array)->Size(); + } +} + +template +void BasicColumn::Clear() +{ + if (m_array->IsNode()) { + ArrayParent *const parent = m_array->GetParent(); + const size_t pndx = m_array->GetParentNdx(); + + // Revert to generic array + BasicArray* array = new BasicArray(parent, pndx, m_array->GetAllocator()); + if (parent) + parent->update_child_ref(pndx, array->GetRef()); + + // Remove original node + m_array->Destroy(); + delete m_array; + + m_array = array; + } + else + ((BasicArray*)m_array)->Clear(); +} + +template +void BasicColumn::Resize(size_t ndx) +{ + TIGHTDB_ASSERT(!IsNode()); // currently only available on leaf level (used by b-tree code) + TIGHTDB_ASSERT(ndx < Size()); + ((BasicArray*)m_array)->Resize(ndx); +} + +template +T BasicColumn::Get(size_t ndx) const +{ + TIGHTDB_ASSERT(ndx < Size()); + return TreeGet >(ndx); // Throws +} + +template +bool BasicColumn::Set(size_t ndx, T value) +{ + TIGHTDB_ASSERT(ndx < Size()); + return TreeSet >(ndx, value); +} + +template +bool BasicColumn::add(T value) +{ + return Insert(Size(), value); +} + +template +bool BasicColumn::Insert(size_t ndx, T value) +{ + TIGHTDB_ASSERT(ndx <= Size()); + return TreeInsert >(ndx, value); +} + +template +void BasicColumn::fill(size_t count) +{ + TIGHTDB_ASSERT(is_empty()); + + // Fill column with default values + // TODO: this is a very naive approach + // we could speedup by creating full nodes directly + for (size_t i = 0; i < count; ++i) { + TreeInsert >(i, 0); + } + +#ifdef TIGHTDB_DEBUG + Verify(); +#endif +} + +template +bool BasicColumn::Compare(const BasicColumn& c) const +{ + const size_t n = Size(); + if (c.Size() != n) + return false; + for (size_t i=0; i +void BasicColumn::Delete(size_t ndx) +{ + TIGHTDB_ASSERT(ndx < Size()); + TreeDelete >(ndx); +} + +template +T BasicColumn::LeafGet(size_t ndx) const TIGHTDB_NOEXCEPT +{ + return ((BasicArray*)m_array)->Get(ndx); +} + +template +bool BasicColumn::LeafSet(size_t ndx, T value) +{ + ((BasicArray*)m_array)->Set(ndx, value); + return true; +} + +template +bool BasicColumn::LeafInsert(size_t ndx, T value) +{ + ((BasicArray*)m_array)->Insert(ndx, value); + return true; +} + +template +void BasicColumn::LeafDelete(size_t ndx) +{ + ((BasicArray*)m_array)->Delete(ndx); +} + + +#ifdef TIGHTDB_DEBUG + +template +void BasicColumn::LeafToDot(std::ostream& out, const Array& array) const +{ + // Rebuild array to get correct type + const size_t ref = array.GetRef(); + const BasicArray newArray(ref, NULL, 0, array.GetAllocator()); + + newArray.ToDot(out); +} + +#endif // TIGHTDB_DEBUG + + +template template +size_t BasicColumn::LeafFind(T value, size_t start, size_t end) const +{ + return ((BasicArray*)m_array)->find_first(value, start, end); +} + +template +void BasicColumn::LeafFindAll(Array &result, T value, size_t add_offset, size_t start, size_t end) const +{ + return ((BasicArray*)m_array)->find_all(result, value, add_offset, start, end); +} + +template +size_t BasicColumn::find_first(T value, size_t start, size_t end) const +{ + TIGHTDB_ASSERT(value); + + return TreeFind, EQUAL>(value, start, end); +} + +template +void BasicColumn::find_all(Array &result, T value, size_t start, size_t end) const +{ + TIGHTDB_ASSERT(value); + + TreeFindAll >(result, value, 0, start, end); +} + + +#if 1 + +template +size_t BasicColumn::count(T target) const +{ + return size_t(ColumnBase::aggregate(target, 0, Size(), NULL)); +} + +template +T BasicColumn::sum(size_t start, size_t end) const +{ + return ColumnBase::aggregate(0, start, end, NULL); +} + +template +double BasicColumn::average(size_t start, size_t end) const +{ + if (end == size_t(-1)) + end = Size(); + size_t size = end - start; + T sum1 = ColumnBase::aggregate(0, start, end, NULL); + double avg = double(sum1) / double( size == 0 ? 1 : size ); + return avg; +} + +template +T BasicColumn::minimum(size_t start, size_t end) const +{ + return ColumnBase::aggregate(0, start, end, NULL); +} + +template +T BasicColumn::maximum(size_t start, size_t end) const +{ + return ColumnBase::aggregate(0, start, end, NULL); +} + +/* +template +void BasicColumn::sort(size_t start, size_t end) +{ + // TODO + assert(0); +} +*/ + +#else + +// Alternative 'naive' reference implementation - useful for reference performance testing. +// TODO: test performance of column aggregates + +template +size_t BasicColumn::count(T target) const +{ + size_t count = 0; + + if (m_array->IsNode()) { + const Array refs = NodeGetRefs(); + const size_t n = refs.Size(); + + for (size_t i = 0; i < n; ++i) { + const size_t ref = refs.GetAsRef(i); + const BasicColumn col(ref, NULL, 0, m_array->GetAllocator()); + + count += col.count(target); + } + } + else { + count += ((BasicArray*)m_array)->count(target); + } + return count; +} + +template +T BasicColumn::sum(size_t start, size_t end) const +{ + if (end == size_t(-1)) + end = Size(); + + double sum = 0; + + if (m_array->IsNode()) { + const Array refs = NodeGetRefs(); + const size_t n = refs.Size(); + + for (size_t i = start; i < n; ++i) { + const size_t ref = refs.GetAsRef(i); + const BasicColumn col(ref, NULL, 0, m_array->GetAllocator()); + + sum += col.sum(start, end); + } + } + else { + sum += ((BasicArray*)m_array)->sum(start, end); + } + return sum; +} + +template +double BasicColumn::average(size_t start, size_t end) const +{ + if (end == size_t(-1)) + end = Size(); + size_t size = end - start; + double sum2 = sum(start, end); + double avg = sum2 / double( size == 0 ? 1 : size ); + return avg; +} + +// #include + +template +T BasicColumn::minimum(size_t start, size_t end) const +{ + if (end == size_t(-1)) + end = Size(); + + T min_val = T(987.0); + if (m_array->IsNode()) { + const Array refs = NodeGetRefs(); + const size_t n = refs.Size(); + + for (size_t i = start; i < n; ++i) { + const size_t ref = refs.GetAsRef(i); + const BasicColumn col(ref, NULL, 0, m_array->GetAllocator()); + T val = col.minimum(start, end); + if (val < min_val || i == start) { + //std::cout << "Min " << i << ": " << min_val << " new val: " << val << "\n"; + val = min_val; + } + } + } + else { + // std::cout << "array-min before: " << min_val; + ((BasicArray*)m_array)->minimum(min_val, start, end); + // std::cout << " after: " << min_val << "\n"; + } + return min_val; +} + +template +T BasicColumn::maximum(size_t start, size_t end) const +{ + if (end == size_t(-1)) + end = Size(); + + T max_val = T(0.0); + if (m_array->IsNode()) { + const Array refs = NodeGetRefs(); + const size_t n = refs.Size(); + + for (size_t i = start; i < n; ++i) { + const size_t ref = refs.GetAsRef(i); + const BasicColumn col(ref, NULL, 0, m_array->GetAllocator()); + T val = col.maximum(start, end); + if (val > max_val || i == start) + val = max_val; + } + } + else { + ((BasicArray*)m_array)->maximum(max_val, start, end); + } + return max_val; +} + +#endif // reference implementation of aggregates + +} // namespace tightdb + +#endif // TIGHTDB_COLUMN_BASIC_TPL_HPP diff --git a/src/tightdb/column_binary.cpp b/src/tightdb/column_binary.cpp index 7c788faa4a2..efe77fdf09c 100644 --- a/src/tightdb/column_binary.cpp +++ b/src/tightdb/column_binary.cpp @@ -57,7 +57,7 @@ void ColumnBinary::UpdateRef(size_t ref) ArrayParent *const parent = m_array->GetParent(); const size_t pndx = m_array->GetParentNdx(); - // Replace the string array with int array for node + // Replace the Binary array with int array for node Array* array = new Array(ref, parent, pndx, m_array->GetAllocator()); delete m_array; m_array = array; @@ -154,7 +154,7 @@ bool ColumnBinary::add(BinaryData bin) void ColumnBinary::Insert(size_t ndx, const char* value, size_t len) { TIGHTDB_ASSERT(ndx <= Size()); - Insert(ndx, BinaryData(value, len)); + Insert(ndx, BinaryData(value, len)); // FIXME:Ignoring return value } bool ColumnBinary::Insert(size_t ndx, BinaryData bin) diff --git a/src/tightdb/column_binary.hpp b/src/tightdb/column_binary.hpp index b58607c645f..6b29f9c345f 100644 --- a/src/tightdb/column_binary.hpp +++ b/src/tightdb/column_binary.hpp @@ -36,8 +36,6 @@ class ColumnBinary : public ColumnBase { void Destroy(); - bool IsBinaryColumn() const TIGHTDB_NOEXCEPT {return true;} - virtual size_t Size() const TIGHTDB_NOEXCEPT; bool is_empty() const TIGHTDB_NOEXCEPT; diff --git a/src/tightdb/column_fwd.hpp b/src/tightdb/column_fwd.hpp index f9c1aa1bd09..d788a106941 100644 --- a/src/tightdb/column_fwd.hpp +++ b/src/tightdb/column_fwd.hpp @@ -25,6 +25,9 @@ namespace tightdb { class ColumnBase; class Column; +template class BasicColumn; +typedef BasicColumn ColumnDouble; +typedef BasicColumn ColumnFloat; class AdaptiveStringColumn; class ColumnStringEnum; class ColumnBinary; diff --git a/src/tightdb/column_mixed.cpp b/src/tightdb/column_mixed.cpp index bc0ca9f4b19..97ed4962746 100644 --- a/src/tightdb/column_mixed.cpp +++ b/src/tightdb/column_mixed.cpp @@ -1,11 +1,7 @@ #include -#include - -using namespace std; namespace tightdb { - ColumnMixed::~ColumnMixed() { delete m_types; @@ -27,11 +23,13 @@ void ColumnMixed::SetParent(ArrayParent *parent, size_t pndx) void ColumnMixed::UpdateFromParent() { - if (!m_array->UpdateFromParent()) return; + if (!m_array->UpdateFromParent()) + return; m_types->UpdateFromParent(); m_refs->UpdateFromParent(); - if (m_data) m_data->UpdateFromParent(); + if (m_data) + m_data->UpdateFromParent(); } @@ -72,7 +70,8 @@ void ColumnMixed::Create(Allocator& alloc, const Table* table, size_t column_ndx void ColumnMixed::InitDataColumn() { - if (m_data) return; + if (m_data) + return; TIGHTDB_ASSERT(m_array->Size() == 2); @@ -84,27 +83,33 @@ void ColumnMixed::InitDataColumn() m_data->SetParent(m_array, 2); } -void ColumnMixed::ClearValue(size_t ndx, ColumnType newtype) +void ColumnMixed::clear_value(size_t ndx, MixedColType newtype) { TIGHTDB_ASSERT(ndx < m_types->Size()); - const ColumnType type = (ColumnType)m_types->Get(ndx); - if (type != COLUMN_TYPE_INT) { + const MixedColType type = (MixedColType)m_types->Get(ndx); + if (type != MIXED_COL_INT) { switch (type) { - case COLUMN_TYPE_BOOL: - case COLUMN_TYPE_DATE: + case MIXED_COL_INT_NEG: + case MIXED_COL_BOOL: + case MIXED_COL_DATE: + case MIXED_COL_FLOAT: + case MIXED_COL_DOUBLE: + case MIXED_COL_DOUBLE_NEG: break; - case COLUMN_TYPE_STRING: - case COLUMN_TYPE_BINARY: + case MIXED_COL_STRING: + case MIXED_COL_BINARY: { // If item is in middle of the column, we just clear // it to avoid having to adjust refs to following items const size_t ref = m_refs->GetAsRef(ndx) >> 1; - if (ref == m_data->Size()-1) m_data->Delete(ref); - else m_data->Set(ref, "", 0); + if (ref == m_data->Size()-1) + m_data->Delete(ref); + else + m_data->Set(ref, "", 0); break; } - case COLUMN_TYPE_TABLE: + case MIXED_COL_TABLE: { // Delete entire table const size_t ref = m_refs->GetAsRef(ndx); @@ -116,64 +121,50 @@ void ColumnMixed::ClearValue(size_t ndx, ColumnType newtype) TIGHTDB_ASSERT(false); } } - - if (type != newtype) m_types->Set(ndx, newtype); -} - -ColumnType ColumnMixed::GetType(size_t ndx) const TIGHTDB_NOEXCEPT -{ - TIGHTDB_ASSERT(ndx < m_types->Size()); - return ColumnType(m_types->Get(ndx)); + if (type != newtype) + m_types->Set(ndx, newtype); } -int64_t ColumnMixed::GetInt(size_t ndx) const -{ - TIGHTDB_ASSERT(ndx < m_types->Size()); - TIGHTDB_ASSERT(m_types->Get(ndx) == COLUMN_TYPE_INT); - - const int64_t value = m_refs->Get(ndx) >> 1; - return value; -} - -bool ColumnMixed::get_bool(size_t ndx) const +void ColumnMixed::Delete(size_t ndx) { TIGHTDB_ASSERT(ndx < m_types->Size()); - TIGHTDB_ASSERT(m_types->Get(ndx) == COLUMN_TYPE_BOOL); - const bool value = (m_refs->Get(ndx) >> 1) == 1; - return value; -} + // Remove refs or binary data + clear_value(ndx, MIXED_COL_INT); -time_t ColumnMixed::get_date(size_t ndx) const -{ - TIGHTDB_ASSERT(ndx < m_types->Size()); - TIGHTDB_ASSERT(m_types->Get(ndx) == COLUMN_TYPE_DATE); + m_types->Delete(ndx); + m_refs->Delete(ndx); - const time_t value = m_refs->Get(ndx) >> 1; - return value; + invalidate_subtables(); } -const char* ColumnMixed::get_string(size_t ndx) const +void ColumnMixed::Clear() { - TIGHTDB_ASSERT(ndx < m_types->Size()); - TIGHTDB_ASSERT(m_types->Get(ndx) == COLUMN_TYPE_STRING); - TIGHTDB_ASSERT(m_data); - - const size_t ref = m_refs->GetAsRef(ndx) >> 1; - const char* value = (const char*)m_data->GetData(ref); - - return value; + m_types->Clear(); + m_refs->Clear(); + if (m_data) + m_data->Clear(); } -BinaryData ColumnMixed::get_binary(size_t ndx) const +ColumnType ColumnMixed::GetType(size_t ndx) const TIGHTDB_NOEXCEPT { TIGHTDB_ASSERT(ndx < m_types->Size()); - TIGHTDB_ASSERT(m_types->Get(ndx) == COLUMN_TYPE_BINARY); - TIGHTDB_ASSERT(m_data); - - const size_t ref = m_refs->GetAsRef(ndx) >> 1; - - return m_data->Get(ref); + MixedColType coltype = static_cast(m_types->Get(ndx)); + switch (coltype) { + case MIXED_COL_INT: return COLUMN_TYPE_INT; + case MIXED_COL_INT_NEG: return COLUMN_TYPE_INT; + case MIXED_COL_BOOL: return COLUMN_TYPE_BOOL; + case MIXED_COL_STRING: return COLUMN_TYPE_STRING; + case MIXED_COL_BINARY: return COLUMN_TYPE_BINARY; + case MIXED_COL_TABLE: return COLUMN_TYPE_TABLE; + case MIXED_COL_DATE: return COLUMN_TYPE_DATE; + case MIXED_COL_FLOAT: return COLUMN_TYPE_FLOAT; + case MIXED_COL_DOUBLE: return COLUMN_TYPE_DOUBLE; + case MIXED_COL_DOUBLE_NEG: return COLUMN_TYPE_DOUBLE; + default: + TIGHTDB_ASSERT(false); + return (COLUMN_TYPE_INT); + } } void ColumnMixed::fill(size_t count) @@ -184,7 +175,7 @@ void ColumnMixed::fill(size_t count) // TODO: this is a very naive approach // we could speedup by creating full nodes directly for (size_t i = 0; i < count; ++i) { - m_types->Insert(i, COLUMN_TYPE_INT); + m_types->Insert(i, MIXED_COL_INT); } for (size_t i = 0; i < count; ++i) { m_refs->Insert(i, 1); // 1 is zero shifted one and low bit set; @@ -195,148 +186,37 @@ void ColumnMixed::fill(size_t count) #endif } -void ColumnMixed::insert_int(size_t ndx, int64_t value) -{ - TIGHTDB_ASSERT(ndx <= m_types->Size()); - - // Shift value one bit and set lowest bit to indicate - // that this is not a ref - const int64_t v = (value << 1) + 1; - - m_types->Insert(ndx, COLUMN_TYPE_INT); - m_refs->Insert(ndx, v); -} - -void ColumnMixed::insert_bool(size_t ndx, bool value) -{ - TIGHTDB_ASSERT(ndx <= m_types->Size()); - - // Shift value one bit and set lowest bit to indicate - // that this is not a ref - const int64_t v = ((value ? 1 : 0) << 1) + 1; - - m_types->Insert(ndx, COLUMN_TYPE_BOOL); - m_refs->Insert(ndx, v); -} - -void ColumnMixed::insert_date(size_t ndx, time_t value) -{ - TIGHTDB_ASSERT(ndx <= m_types->Size()); - - // Shift value one bit and set lowest bit to indicate - // that this is not a ref - const int64_t v = (value << 1) + 1; - - m_types->Insert(ndx, COLUMN_TYPE_DATE); - m_refs->Insert(ndx, v); -} - -void ColumnMixed::insert_string(size_t ndx, const char* value) -{ - TIGHTDB_ASSERT(ndx <= m_types->Size()); - InitDataColumn(); - - const size_t len = strlen(value)+1; - const size_t ref = m_data->Size(); - m_data->add(value, len); - - // Shift value one bit and set lowest bit to indicate - // that this is not a ref - const int64_t v = (ref << 1) + 1; - - m_types->Insert(ndx, COLUMN_TYPE_STRING); - m_refs->Insert(ndx, v); -} - -void ColumnMixed::insert_binary(size_t ndx, const char* value, size_t len) -{ - TIGHTDB_ASSERT(ndx <= m_types->Size()); - InitDataColumn(); - - const size_t ref = m_data->Size(); - m_data->add(value, len); - - // Shift value one bit and set lowest bit to indicate - // that this is not a ref - const int64_t v = (ref << 1) + 1; - - m_types->Insert(ndx, COLUMN_TYPE_BINARY); - m_refs->Insert(ndx, v); -} - -void ColumnMixed::SetInt(size_t ndx, int64_t value) -{ - TIGHTDB_ASSERT(ndx < m_types->Size()); - - // Remove refs or binary data (sets type to int) - ClearValue(ndx, COLUMN_TYPE_INT); - - // Shift value one bit and set lowest bit to indicate - // that this is not a ref - const int64_t v = (value << 1) + 1; - - m_refs->Set(ndx, v); -} - -void ColumnMixed::set_bool(size_t ndx, bool value) -{ - TIGHTDB_ASSERT(ndx < m_types->Size()); - - // Remove refs or binary data (sets type to int) - ClearValue(ndx, COLUMN_TYPE_BOOL); - - // Shift value one bit and set lowest bit to indicate - // that this is not a ref - const int64_t v = ((value ? 1 : 0) << 1) + 1; - - m_refs->Set(ndx, v); -} - -void ColumnMixed::set_date(size_t ndx, time_t value) -{ - TIGHTDB_ASSERT(ndx < m_types->Size()); - - // Remove refs or binary data (sets type to int) - ClearValue(ndx, COLUMN_TYPE_DATE); - - // Shift value one bit and set lowest bit to indicate - // that this is not a ref - const int64_t v = (int64_t(value) << 1) + 1; - - m_refs->Set(ndx, v); -} void ColumnMixed::set_string(size_t ndx, const char* value) { TIGHTDB_ASSERT(ndx < m_types->Size()); InitDataColumn(); - const ColumnType type = (ColumnType)m_types->Get(ndx); + const MixedColType type = (MixedColType)m_types->Get(ndx); const size_t len = strlen(value)+1; // See if we can reuse data position - if (type == COLUMN_TYPE_STRING) { + if (type == MIXED_COL_STRING) { const size_t ref = m_refs->GetAsRef(ndx) >> 1; m_data->Set(ref, value, len); } - else if (type == COLUMN_TYPE_BINARY) { + else if (type == MIXED_COL_BINARY) { const size_t ref = m_refs->GetAsRef(ndx) >> 1; m_data->Set(ref, value, len); - m_types->Set(ndx, COLUMN_TYPE_STRING); + m_types->Set(ndx, MIXED_COL_STRING); } else { // Remove refs or binary data - ClearValue(ndx, COLUMN_TYPE_STRING); + clear_value(ndx, MIXED_COL_STRING); // Add value to data column const size_t ref = m_data->Size(); m_data->add(value, len); - // Shift value one bit and set lowest bit to indicate - // that this is not a ref + // Shift value one bit and set lowest bit to indicate that this is not a ref const int64_t v = (ref << 1) + 1; - m_types->Set(ndx, COLUMN_TYPE_STRING); + m_types->Set(ndx, MIXED_COL_STRING); m_refs->Set(ndx, v); } } @@ -346,89 +226,48 @@ void ColumnMixed::set_binary(size_t ndx, const char* value, size_t len) TIGHTDB_ASSERT(ndx < m_types->Size()); InitDataColumn(); - const ColumnType type = (ColumnType)m_types->Get(ndx); + const MixedColType type = (MixedColType)m_types->Get(ndx); // See if we can reuse data position - if (type == COLUMN_TYPE_STRING) { + if (type == MIXED_COL_STRING) { const size_t ref = m_refs->GetAsRef(ndx) >> 1; m_data->Set(ref, value, len); - m_types->Set(ndx, COLUMN_TYPE_BINARY); + m_types->Set(ndx, MIXED_COL_BINARY); } - else if (type == COLUMN_TYPE_BINARY) { + else if (type == MIXED_COL_BINARY) { const size_t ref = m_refs->GetAsRef(ndx) >> 1; m_data->Set(ref, value, len); } else { // Remove refs or binary data - ClearValue(ndx, COLUMN_TYPE_BINARY); + clear_value(ndx, MIXED_COL_BINARY); // Add value to data column const size_t ref = m_data->Size(); m_data->add(value, len); - // Shift value one bit and set lowest bit to indicate - // that this is not a ref + // Shift value one bit and set lowest bit to indicate that this is not a ref const int64_t v = (ref << 1) + 1; - m_types->Set(ndx, COLUMN_TYPE_BINARY); + m_types->Set(ndx, MIXED_COL_BINARY); m_refs->Set(ndx, v); } } -// FIXME: Check that callers test the return value -bool ColumnMixed::insert_subtable(size_t ndx) -{ - TIGHTDB_ASSERT(ndx <= m_types->Size()); - const size_t ref = Table::create_empty_table(m_array->GetAllocator()); - if (!ref) return false; - // FIXME: These inserts can also fail on allocation - m_types->Insert(ndx, COLUMN_TYPE_TABLE); - m_refs->Insert(ndx, ref); - return true; -} - -// FIXME: Check that callers test the return value -bool ColumnMixed::set_subtable(size_t ndx) -{ - TIGHTDB_ASSERT(ndx < m_types->Size()); - const size_t ref = Table::create_empty_table(m_array->GetAllocator()); - if (!ref) return false; - // FIXME: Could the following operations also fail on allocation? - ClearValue(ndx, COLUMN_TYPE_TABLE); // Remove any previous refs or binary data - m_refs->Set(ndx, ref); - return true; -} - -void ColumnMixed::Delete(size_t ndx) -{ - TIGHTDB_ASSERT(ndx < m_types->Size()); - - // Remove refs or binary data - ClearValue(ndx, COLUMN_TYPE_INT); - - m_types->Delete(ndx); - m_refs->Delete(ndx); - - invalidate_subtables(); -} - -void ColumnMixed::Clear() -{ - m_types->Clear(); - m_refs->Clear(); - if (m_data) m_data->Clear(); -} bool ColumnMixed::Compare(const ColumnMixed& c) const { const size_t n = Size(); - if (c.Size() != n) return false; + if (c.Size() != n) + return false; + for (size_t i=0; iget_table_ref(); ConstTableRef t2 = c.get_subtable_ptr(i)->get_table_ref(); - if (*t1 != *t2) return false; + if (*t1 != *t2) + return false; } break; default: @@ -500,8 +346,8 @@ void ColumnMixed::ToDot(std::ostream& out, const char* title) const // Write sub-tables const size_t count = Size(); for (size_t i = 0; i < count; ++i) { - const ColumnType type = (ColumnType)m_types->Get(i); - if (type != COLUMN_TYPE_TABLE) continue; + const MixedColType type = (MixedColType)m_types->Get(i); + if (type != MIXED_COL_TABLE) continue; ConstTableRef subtable = m_refs->get_subtable(i); subtable->to_dot(out); } diff --git a/src/tightdb/column_mixed.hpp b/src/tightdb/column_mixed.hpp index 590f302d3de..c6f4dfbf423 100644 --- a/src/tightdb/column_mixed.hpp +++ b/src/tightdb/column_mixed.hpp @@ -23,8 +23,12 @@ #include #include #include +#include #include #include +#include +#include + namespace tightdb { @@ -72,9 +76,11 @@ class ColumnMixed : public ColumnBase { virtual size_t Size() const TIGHTDB_NOEXCEPT {return m_types->Size();} bool is_empty() const TIGHTDB_NOEXCEPT {return m_types->is_empty();} - int64_t GetInt(size_t ndx) const; + int64_t get_int(size_t ndx) const; bool get_bool(size_t ndx) const; time_t get_date(size_t ndx) const; + float get_float(size_t ndx) const; + double get_double(size_t ndx) const; const char* get_string(size_t ndx) const; BinaryData get_binary(size_t ndx) const; @@ -87,9 +93,11 @@ class ColumnMixed : public ColumnBase { /// by an instance of BasicTableRef. Table* get_subtable_ptr(std::size_t row_idx) const; - void SetInt(size_t ndx, int64_t value); + void set_int(size_t ndx, int64_t value); void set_bool(size_t ndx, bool value); void set_date(size_t ndx, time_t value); + void set_float(size_t ndx, float value); + void set_double(size_t ndx, double value); void set_string(size_t ndx, const char* value); void set_binary(size_t ndx, const char* value, size_t len); bool set_subtable(size_t ndx); @@ -97,6 +105,8 @@ class ColumnMixed : public ColumnBase { void insert_int(size_t ndx, int64_t value); void insert_bool(size_t ndx, bool value); void insert_date(size_t ndx, time_t value); + void insert_float(size_t ndx, float value); + void insert_double(size_t ndx, double value); void insert_string(size_t ndx, const char* value); void insert_binary(size_t ndx, const char* value, size_t len); bool insert_subtable(size_t ndx); @@ -133,13 +143,46 @@ class ColumnMixed : public ColumnBase { ArrayParent* parent, size_t ndx_in_parent, size_t ref); void InitDataColumn(); - void ClearValue(size_t ndx, ColumnType newtype); + enum MixedColType { + // Column types used in Mixed + MIXED_COL_INT = 0, + MIXED_COL_BOOL = 1, + MIXED_COL_STRING = 2, + MIXED_COL_STRING_ENUM = 3, // double refs + MIXED_COL_BINARY = 4, + MIXED_COL_TABLE = 5, + MIXED_COL_MIXED = 6, + MIXED_COL_DATE = 7, + MIXED_COL_RESERVED1 = 8, // DateTime + MIXED_COL_FLOAT = 9, // Float + MIXED_COL_DOUBLE = 10, // Positive Double + MIXED_COL_DOUBLE_NEG = 11, // Negative Double + MIXED_COL_INT_NEG = 12 // Negative Integers + // Preserve values above for backward compability + }; + + void clear_value(size_t ndx, MixedColType newtype); + + // Get/set/insert 64-bit values in m_refs/m_types + int64_t get_value(size_t ndx) const; + void set_value(size_t ndx, int64_t value, MixedColType coltype); + void insert_int64(size_t ndx, int64_t value, MixedColType pos_type, MixedColType neg_type); + void set_int64(size_t ndx, int64_t value, MixedColType pos_type, MixedColType neg_type); class RefsColumn; - // Member variables + // Member variables: + + // 'm_types' stores the ColumnType of each value at the given index. + // For values that uses all 64 bit's the datatype also stores this bit. + // (By having a type for both positive numbers, and another type for negative numbers) Column* m_types; - RefsColumn* m_refs; + + // Bit 0 is used to indicate if it's a reference. + // If not, the data value is stored (shifted 1 bit left). And the sign bit is stored in m_types. + RefsColumn* m_refs; + + // m_data holds any Binary/String data - if needed. ColumnBinary* m_data; }; @@ -157,57 +200,11 @@ class ColumnMixed::RefsColumn: public ColumnSubtableParent }; -inline ColumnMixed::ColumnMixed(): m_data(0) -{ - Create(Allocator::get_default(), 0, 0); -} - -inline ColumnMixed::ColumnMixed(Allocator& alloc, const Table* table, std::size_t column_ndx): - m_data(0) -{ - Create(alloc, table, column_ndx); -} - -inline ColumnMixed::ColumnMixed(Allocator& alloc, const Table* table, std::size_t column_ndx, - ArrayParent* parent, std::size_t ndx_in_parent, std::size_t ref): - m_data(0) -{ - Create(alloc, table, column_ndx, parent, ndx_in_parent, ref); -} - -inline size_t ColumnMixed::get_subtable_size(size_t row_idx) const TIGHTDB_NOEXCEPT -{ - // FIXME: If the table object is cached, it is possible to get the - // size from it. Maybe it is faster in general to check for the - // the presence of the cached object and use it when available. - TIGHTDB_ASSERT(row_idx < m_types->Size()); - if (m_types->Get(row_idx) != COLUMN_TYPE_TABLE) return 0; - const size_t top_ref = m_refs->GetAsRef(row_idx); - const size_t columns_ref = Array(top_ref, 0, 0, m_refs->GetAllocator()).GetAsRef(1); - const Array columns(columns_ref, 0, 0, m_refs->GetAllocator()); - if (columns.is_empty()) return 0; - const size_t first_col_ref = columns.GetAsRef(0); - return get_size_from_ref(first_col_ref, m_refs->GetAllocator()); -} - -inline Table* ColumnMixed::get_subtable_ptr(size_t row_idx) const -{ - TIGHTDB_ASSERT(row_idx < m_types->Size()); - if (m_types->Get(row_idx) != COLUMN_TYPE_TABLE) return 0; - return m_refs->get_subtable_ptr(row_idx); -} - -inline void ColumnMixed::invalidate_subtables() -{ - m_refs->invalidate_subtables(); -} +} // namespace tightdb -inline void ColumnMixed::invalidate_subtables_virtual() -{ - invalidate_subtables(); -} +// Implementation +#include -} // namespace tightdb #endif // TIGHTDB_COLUMN_MIXED_HPP diff --git a/src/tightdb/column_mixed_tpl.hpp b/src/tightdb/column_mixed_tpl.hpp new file mode 100644 index 00000000000..89f91104051 --- /dev/null +++ b/src/tightdb/column_mixed_tpl.hpp @@ -0,0 +1,353 @@ +/************************************************************************* + * + * TIGHTDB CONFIDENTIAL + * __________________ + * + * [2011] - [2012] TightDB Inc + * All Rights Reserved. + * + * NOTICE: All information contained herein is, and remains + * the property of TightDB Incorporated and its suppliers, + * if any. The intellectual and technical concepts contained + * herein are proprietary to TightDB Incorporated + * and its suppliers and may be covered by U.S. and Foreign Patents, + * patents in process, and are protected by trade secret or copyright law. + * Dissemination of this information or reproduction of this material + * is strictly forbidden unless prior written permission is obtained + * from TightDB Incorporated. + * + **************************************************************************/ + +namespace tightdb { + +inline ColumnMixed::ColumnMixed(): m_data(0) +{ + Create(Allocator::get_default(), 0, 0); +} + +inline ColumnMixed::ColumnMixed(Allocator& alloc, const Table* table, std::size_t column_ndx): + m_data(0) +{ + Create(alloc, table, column_ndx); +} + +inline ColumnMixed::ColumnMixed(Allocator& alloc, const Table* table, std::size_t column_ndx, + ArrayParent* parent, std::size_t ndx_in_parent, std::size_t ref): + m_data(0) +{ + Create(alloc, table, column_ndx, parent, ndx_in_parent, ref); +} + +inline size_t ColumnMixed::get_subtable_size(size_t row_idx) const TIGHTDB_NOEXCEPT +{ + // FIXME: If the table object is cached, it is possible to get the + // size from it. Maybe it is faster in general to check for the + // the presence of the cached object and use it when available. + TIGHTDB_ASSERT(row_idx < m_types->Size()); + if (m_types->Get(row_idx) != COLUMN_TYPE_TABLE) return 0; + const size_t top_ref = m_refs->GetAsRef(row_idx); + const size_t columns_ref = Array(top_ref, 0, 0, m_refs->GetAllocator()).GetAsRef(1); + const Array columns(columns_ref, 0, 0, m_refs->GetAllocator()); + if (columns.is_empty()) return 0; + const size_t first_col_ref = columns.GetAsRef(0); + return get_size_from_ref(first_col_ref, m_refs->GetAllocator()); +} + +inline Table* ColumnMixed::get_subtable_ptr(size_t row_idx) const +{ + TIGHTDB_ASSERT(row_idx < m_types->Size()); + if (m_types->Get(row_idx) != COLUMN_TYPE_TABLE) + return 0; + return m_refs->get_subtable_ptr(row_idx); +} + +inline void ColumnMixed::invalidate_subtables() +{ + m_refs->invalidate_subtables(); +} + +inline void ColumnMixed::invalidate_subtables_virtual() +{ + invalidate_subtables(); +} + + +// +// Getters +// + +#define TIGHTDB_BIT63 0x8000000000000000 + +inline int64_t ColumnMixed::get_value(size_t ndx) const +{ + TIGHTDB_ASSERT(ndx < m_types->Size()); + + // Shift the unsigned value right - ensuring 0 gets in from left. + // Shifting signed integers right doesn't ensure 0's. + const uint64_t value = static_cast(m_refs->Get(ndx)) >> 1; + return static_cast(value); +} + +inline int64_t ColumnMixed::get_int(size_t ndx) const +{ + // Get first 63 bits of the integer value + int64_t value = get_value(ndx); + + // restore 'sign'-bit from the column-type + const MixedColType coltype = static_cast(m_types->Get(ndx)); + if (coltype == MIXED_COL_INT_NEG) + value |= TIGHTDB_BIT63; // set sign bit (63) + else { + TIGHTDB_ASSERT(coltype == MIXED_COL_INT); + } + return value; +} + +inline bool ColumnMixed::get_bool(size_t ndx) const +{ + TIGHTDB_ASSERT(m_types->Get(ndx) == MIXED_COL_BOOL); + + return (get_value(ndx) != 0); +} + +inline time_t ColumnMixed::get_date(size_t ndx) const +{ + TIGHTDB_ASSERT(m_types->Get(ndx) == MIXED_COL_DATE); + + return static_cast(get_value(ndx)); +} + +inline float ColumnMixed::get_float(size_t ndx) const +{ + TIGHTDB_STATIC_ASSERT(std::numeric_limits::is_iec559, "'float' is not IEEE"); + TIGHTDB_STATIC_ASSERT((sizeof(float) * CHAR_BIT == 32), "Assume 32 bit float."); + TIGHTDB_ASSERT(m_types->Get(ndx) == MIXED_COL_FLOAT); + + return TypePunning( get_value(ndx) ); +} + +inline double ColumnMixed::get_double(size_t ndx) const +{ + TIGHTDB_STATIC_ASSERT(std::numeric_limits::is_iec559, "'double' is not IEEE"); + TIGHTDB_STATIC_ASSERT((sizeof(double) * CHAR_BIT == 64), "Assume 64 bit double."); + + int64_t intval = get_value(ndx); + + // restore 'sign'-bit from the column-type + const MixedColType coltype = static_cast(m_types->Get(ndx)); + if (coltype == MIXED_COL_DOUBLE_NEG) + intval |= TIGHTDB_BIT63; // set sign bit (63) + else { + TIGHTDB_ASSERT(coltype == MIXED_COL_DOUBLE); + } + return TypePunning( intval ); +} + +inline const char* ColumnMixed::get_string(size_t ndx) const +{ + TIGHTDB_ASSERT(ndx < m_types->Size()); + TIGHTDB_ASSERT(m_types->Get(ndx) == MIXED_COL_STRING); + TIGHTDB_ASSERT(m_data); + + const size_t ref = m_refs->GetAsRef(ndx) >> 1; + const char* value = static_cast(m_data->GetData(ref)); + return value; +} + +inline BinaryData ColumnMixed::get_binary(size_t ndx) const +{ + TIGHTDB_ASSERT(ndx < m_types->Size()); + TIGHTDB_ASSERT(m_types->Get(ndx) == MIXED_COL_BINARY); + TIGHTDB_ASSERT(m_data); + + const size_t ref = m_refs->GetAsRef(ndx) >> 1; + return m_data->Get(ref); +} + +// +// Setters +// + +// Set a int64 value. +// Store 63 bit of the value in m_refs. Store sign bit in m_types. + +inline void ColumnMixed::set_int64(size_t ndx, int64_t value, MixedColType pos_type, MixedColType neg_type) +{ + TIGHTDB_ASSERT(ndx < m_types->Size()); + + // If sign-bit is set in value, 'store' it in the column-type + const MixedColType coltype = ((value & TIGHTDB_BIT63) == 0) ? pos_type : neg_type; + + // Remove refs or binary data (sets type to double) + clear_value(ndx, coltype); + + // Shift value one bit and set lowest bit to indicate that this is not a ref + value = (value << 1) + 1; + m_refs->Set(ndx, value); +} + +inline void ColumnMixed::set_int(size_t ndx, int64_t value) +{ + set_int64(ndx, value, MIXED_COL_INT, MIXED_COL_INT_NEG); +} + +inline void ColumnMixed::set_double(size_t ndx, double value) +{ + const int64_t val64 = TypePunning( value ); + set_int64(ndx, val64, MIXED_COL_DOUBLE, MIXED_COL_DOUBLE_NEG); +} + +inline void ColumnMixed::set_value(size_t ndx, int64_t value, MixedColType coltype) +{ + TIGHTDB_ASSERT(ndx < m_types->Size()); + + // Remove refs or binary data (sets type to float) + clear_value(ndx, coltype); + + // Shift value one bit and set lowest bit to indicate that this is not a ref + const int64_t v = (value << 1) + 1; + m_refs->Set(ndx, v); +} + +inline void ColumnMixed::set_float(size_t ndx, float value) +{ + const void* vptr = reinterpret_cast(&value); + const int32_t val32 = * reinterpret_cast(vptr); + set_value(ndx, static_cast(val32), MIXED_COL_FLOAT); +} + +inline void ColumnMixed::set_bool(size_t ndx, bool value) +{ + set_value(ndx, (value ? 1 : 0), MIXED_COL_BOOL); +} + +inline void ColumnMixed::set_date(size_t ndx, time_t value) +{ + set_value(ndx, static_cast(value), MIXED_COL_DATE); +} + +// FIXME: Check that callers test the return value +inline bool ColumnMixed::set_subtable(size_t ndx) +{ + TIGHTDB_ASSERT(ndx < m_types->Size()); + const size_t ref = Table::create_empty_table(m_array->GetAllocator()); + if (!ref) + return false; + // FIXME: Could the following operations also fail on allocation? + clear_value(ndx, MIXED_COL_TABLE); // Remove any previous refs or binary data + m_refs->Set(ndx, ref); + return true; +} + +// +// Inserts +// + +// Insert a int64 value. +// Store 63 bit of the value in m_refs. Store sign bit in m_types. + +inline void ColumnMixed::insert_int64(size_t ndx, int64_t value, MixedColType pos_type, MixedColType neg_type) +{ + TIGHTDB_ASSERT(ndx <= m_types->Size()); + + // 'store' the sign-bit in the integer-type + if ((value & TIGHTDB_BIT63) == 0) + m_types->Insert(ndx, pos_type); + else + m_types->Insert(ndx, neg_type); + + // Shift value one bit and set lowest bit to indicate that this is not a ref + value = (value << 1) + 1; + m_refs->Insert(ndx, value); +} + +inline void ColumnMixed::insert_int(size_t ndx, int64_t value) +{ + insert_int64(ndx, value, MIXED_COL_INT, MIXED_COL_INT_NEG); +} + +inline void ColumnMixed::insert_double(size_t ndx, double value) +{ + int64_t val64 = TypePunning( value ); + insert_int64(ndx, val64, MIXED_COL_DOUBLE, MIXED_COL_DOUBLE_NEG); +} + +inline void ColumnMixed::insert_float(size_t ndx, float value) +{ + TIGHTDB_ASSERT(ndx <= m_types->Size()); + + // Convert to int32_t first, to ensure we only access 32 bits from the float. + const int32_t val32 = TypePunning( value ); + + // Shift value one bit and set lowest bit to indicate that this is not a ref + const int64_t val64 = (static_cast(val32) << 1) + 1; + m_refs->Insert(ndx, val64); + m_types->Insert(ndx, MIXED_COL_FLOAT); +} + +inline void ColumnMixed::insert_bool(size_t ndx, bool value) +{ + TIGHTDB_ASSERT(ndx <= m_types->Size()); + + // Shift value one bit and set lowest bit to indicate that this is not a ref + const int64_t v = ((value ? 1 : 0) << 1) + 1; + + m_types->Insert(ndx, MIXED_COL_BOOL); + m_refs->Insert(ndx, v); +} + +inline void ColumnMixed::insert_date(size_t ndx, time_t value) +{ + TIGHTDB_ASSERT(ndx <= m_types->Size()); + + // Shift value one bit and set lowest bit to indicate that this is not a ref + const int64_t v = (value << 1) + 1; + + m_types->Insert(ndx, MIXED_COL_DATE); + m_refs->Insert(ndx, v); +} + +inline void ColumnMixed::insert_string(size_t ndx, const char* value) +{ + TIGHTDB_ASSERT(ndx <= m_types->Size()); + InitDataColumn(); + + const size_t len = strlen(value)+1; + const size_t ref = m_data->Size(); + m_data->add(value, len); + + // Shift value one bit and set lowest bit to indicate that this is not a ref + const int64_t v = (ref << 1) + 1; + + m_types->Insert(ndx, MIXED_COL_STRING); + m_refs->Insert(ndx, v); +} + +inline void ColumnMixed::insert_binary(size_t ndx, const char* value, size_t len) +{ + TIGHTDB_ASSERT(ndx <= m_types->Size()); + InitDataColumn(); + + const size_t ref = m_data->Size(); + m_data->add(value, len); + + // Shift value one bit and set lowest bit to indicate that this is not a ref + const int64_t v = (ref << 1) + 1; + + m_types->Insert(ndx, MIXED_COL_BINARY); + m_refs->Insert(ndx, v); +} + +// FIXME: Check that callers test the return value +inline bool ColumnMixed::insert_subtable(size_t ndx) +{ + TIGHTDB_ASSERT(ndx <= m_types->Size()); + const size_t ref = Table::create_empty_table(m_array->GetAllocator()); + if (!ref) return false; + // FIXME: These inserts can also fail on allocation + m_types->Insert(ndx, MIXED_COL_TABLE); + m_refs->Insert(ndx, ref); + return true; +} + +} // namespace tightdb \ No newline at end of file diff --git a/src/tightdb/column_string.cpp b/src/tightdb/column_string.cpp index 4a3e127975d..0b09976b280 100644 --- a/src/tightdb/column_string.cpp +++ b/src/tightdb/column_string.cpp @@ -387,15 +387,15 @@ bool AdaptiveStringColumn::LeafInsert(size_t ndx, const char* value) return true; } -templatesize_t AdaptiveStringColumn::LeafFind(const char* value, - size_t start, size_t end) const +template +size_t AdaptiveStringColumn::LeafFind(const char* value, size_t start, size_t end) const { - if (IsLongStrings()) { - return ((ArrayStringLong*)m_array)->find_first(value, start, end); - } - else { - return ((ArrayString*)m_array)->find_first(value, start, end); - } + if (IsLongStrings()) { + return ((ArrayStringLong*)m_array)->find_first(value, start, end); + } + else { + return ((ArrayString*)m_array)->find_first(value, start, end); + } } void AdaptiveStringColumn::LeafFindAll(Array &result, const char* value, size_t add_offset, size_t start, size_t end) const diff --git a/src/tightdb/column_string.hpp b/src/tightdb/column_string.hpp index 882c7e89e0f..460adfa225b 100644 --- a/src/tightdb/column_string.hpp +++ b/src/tightdb/column_string.hpp @@ -38,8 +38,6 @@ class AdaptiveStringColumn : public ColumnBase { void Destroy(); - bool IsStringColumn() const TIGHTDB_NOEXCEPT {return true;} - virtual size_t Size() const TIGHTDB_NOEXCEPT; bool is_empty() const TIGHTDB_NOEXCEPT; diff --git a/src/tightdb/column_tpl.hpp b/src/tightdb/column_tpl.hpp index 55f5e47f593..56b9201f308 100644 --- a/src/tightdb/column_tpl.hpp +++ b/src/tightdb/column_tpl.hpp @@ -25,9 +25,57 @@ #include #include #include +#include namespace tightdb { +template class BASICNODE; +template class NODE; +template class SequentialGetter; + +template struct ColumnTypeTraits2; + +template struct ColumnTypeTraits2 { + typedef Column column_type; + typedef NODE node_type; +}; +template struct ColumnTypeTraits2 { + typedef Column column_type; + typedef NODE node_type; +}; +template struct ColumnTypeTraits2 { + typedef ColumnFloat column_type; + typedef BASICNODE node_type; +}; +template struct ColumnTypeTraits2 { + typedef ColumnDouble column_type; + typedef BASICNODE node_type; +}; + + +template +R ColumnBase::aggregate(T target, size_t start, size_t end, size_t *matchcount) const +{ + typedef typename ColumnTypeTraits2::column_type ColType; + typedef typename ColumnTypeTraits2::node_type NodeType; + + if (end == size_t(-1)) + end = Size(); + + NodeType node(target, 0); + + node.QuickInit((ColType*)this, target); + QueryState st; + st.init(action, NULL, size_t(-1)); + + ColType* column = (ColType*)this; + SequentialGetter sg( column ); + node.template aggregate_local(&st, start, end, size_t(-1), &sg, matchcount); + + return st.state; +} + + template T GetColumnFromRef(Array& parent, size_t ndx) // Throws { //TIGHTDB_ASSERT(parent.HasRefs()); @@ -76,9 +124,11 @@ template bool ColumnBase::TreeSet(size_t ndx, T value) // Set item C target = GetColumnFromRef(refs, node_ndx); - if (!target.Set(local_ndx, value)) return false; + if (!target.Set(local_ndx, value)) + return false; } - else if (!static_cast(this)->LeafSet(ndx, value)) return false; + else if (!static_cast(this)->LeafSet(ndx, value)) + return false; // Update index //if (m_index) m_index->Set(ndx, oldVal, value); diff --git a/src/tightdb/column_type.hpp b/src/tightdb/column_type.hpp index 74da8173daa..ace9d409c39 100644 --- a/src/tightdb/column_type.hpp +++ b/src/tightdb/column_type.hpp @@ -22,6 +22,8 @@ namespace tightdb { +// Note: tightdb_objc/Deliv/ColumnType.h must be kept in sync with his file. +// Note: tightdb_java2/src/main/java/ColumnType.java must be kept in sync with his file. // FIXME: The namespace of all-upper-case names must be considered // reserved for macros. Consider renaming 'COLUMN_TYPE_INT' to @@ -46,8 +48,8 @@ enum ColumnType { COLUMN_TYPE_MIXED = 6, COLUMN_TYPE_DATE = 7, COLUMN_TYPE_RESERVED1 = 8, // DateTime - COLUMN_TYPE_RESERVED2 = 9, // Float - COLUMN_TYPE_RESERVED3 = 10, // Double + COLUMN_TYPE_FLOAT = 9, // Float + COLUMN_TYPE_DOUBLE = 10, // Double COLUMN_TYPE_RESERVED4 = 11, // Decimal // Attributes diff --git a/src/tightdb/file.cpp b/src/tightdb/file.cpp index 45d235202e0..97cdef88705 100644 --- a/src/tightdb/file.cpp +++ b/src/tightdb/file.cpp @@ -425,7 +425,7 @@ bool File::lock(bool exclusive, bool non_blocking) #ifdef _WIN32 // Windows version - TIGHTDB_ASSERT(!have_lock); + TIGHTDB_ASSERT(!m_have_lock); // Under Windows a file lock must be explicitely released before // the file is closed. It will eventually be released by the diff --git a/src/tightdb/file.hpp b/src/tightdb/file.hpp index 0d5d071c163..96a2ca091da 100644 --- a/src/tightdb/file.hpp +++ b/src/tightdb/file.hpp @@ -481,7 +481,7 @@ inline void File::open(const std::string& path, Mode m) inline bool File::is_attached() const TIGHTDB_NOEXCEPT { #ifdef _WIN32 - return m_handle; + return (m_handle != NULL); #else return 0 <= m_fd; #endif @@ -585,7 +585,7 @@ template inline void File::Map::sync() template inline bool File::Map::is_attached() const TIGHTDB_NOEXCEPT { - return m_addr; + return (m_addr != NULL); } template inline T* File::Map::get_addr() const TIGHTDB_NOEXCEPT diff --git a/src/tightdb/index_string.cpp b/src/tightdb/index_string.cpp index 1f1429a7f2e..96de8a9225b 100644 --- a/src/tightdb/index_string.cpp +++ b/src/tightdb/index_string.cpp @@ -1,4 +1,5 @@ #include +#include using namespace tightdb; diff --git a/src/tightdb/mixed.hpp b/src/tightdb/mixed.hpp index 2933b8fae21..0edef683f75 100644 --- a/src/tightdb/mixed.hpp +++ b/src/tightdb/mixed.hpp @@ -40,6 +40,8 @@ class Mixed { Mixed(bool) TIGHTDB_NOEXCEPT; Mixed(int64_t) TIGHTDB_NOEXCEPT; + Mixed(float) TIGHTDB_NOEXCEPT; + Mixed(double) TIGHTDB_NOEXCEPT; Mixed(const char*) TIGHTDB_NOEXCEPT; Mixed(BinaryData) TIGHTDB_NOEXCEPT; Mixed(Date) TIGHTDB_NOEXCEPT; @@ -51,12 +53,16 @@ class Mixed { bool get_bool() const TIGHTDB_NOEXCEPT; int64_t get_int() const TIGHTDB_NOEXCEPT; + float get_float() const TIGHTDB_NOEXCEPT; + double get_double() const TIGHTDB_NOEXCEPT; const char* get_string() const TIGHTDB_NOEXCEPT; BinaryData get_binary() const TIGHTDB_NOEXCEPT; std::time_t get_date() const TIGHTDB_NOEXCEPT; void set_bool(bool) TIGHTDB_NOEXCEPT; void set_int(int64_t) TIGHTDB_NOEXCEPT; + void set_float(float) TIGHTDB_NOEXCEPT; + void set_double(double) TIGHTDB_NOEXCEPT; void set_string(const char*) TIGHTDB_NOEXCEPT; void set_binary(BinaryData) TIGHTDB_NOEXCEPT; void set_binary(const char* data, std::size_t size) TIGHTDB_NOEXCEPT; @@ -68,10 +74,12 @@ class Mixed { private: ColumnType m_type; union { - int64_t m_int; - bool m_bool; - const char* m_str; - std::time_t m_date; + int64_t m_int; + bool m_bool; + float m_float; + double m_double; + const char* m_str; + std::time_t m_date; }; std::size_t m_len; }; @@ -97,6 +105,18 @@ template bool operator!=(Wrap, const T&) TIGHTDB_NOEXCEPT; template bool operator==(const T&, Wrap) TIGHTDB_NOEXCEPT; template bool operator!=(const T&, Wrap) TIGHTDB_NOEXCEPT; +// Compare mixed with float +bool operator==(Wrap, float); +bool operator!=(Wrap, float); +bool operator==(float, Wrap); +bool operator!=(float, Wrap); + +// Compare mixed with double +bool operator==(Wrap, double); +bool operator!=(Wrap, double); +bool operator==(double, Wrap); +bool operator!=(double, Wrap); + // Compare mixed with zero-terminated string bool operator==(Wrap, const char*) TIGHTDB_NOEXCEPT; bool operator!=(Wrap, const char*) TIGHTDB_NOEXCEPT; @@ -142,6 +162,18 @@ inline Mixed::Mixed(int64_t v) TIGHTDB_NOEXCEPT m_int = v; } +inline Mixed::Mixed(float v) TIGHTDB_NOEXCEPT +{ + m_type = COLUMN_TYPE_FLOAT; + m_float = v; +} + +inline Mixed::Mixed(double v) TIGHTDB_NOEXCEPT +{ + m_type = COLUMN_TYPE_DOUBLE; + m_double = v; +} + inline Mixed::Mixed(const char* v) TIGHTDB_NOEXCEPT { m_type = COLUMN_TYPE_STRING; @@ -173,6 +205,18 @@ inline int64_t Mixed::get_int() const TIGHTDB_NOEXCEPT return m_int; } +inline float Mixed::get_float() const TIGHTDB_NOEXCEPT +{ + TIGHTDB_ASSERT(m_type == COLUMN_TYPE_FLOAT); + return m_float; +} + +inline double Mixed::get_double() const TIGHTDB_NOEXCEPT +{ + TIGHTDB_ASSERT(m_type == COLUMN_TYPE_DOUBLE); + return m_double; +} + inline const char* Mixed::get_string() const TIGHTDB_NOEXCEPT { TIGHTDB_ASSERT(m_type == COLUMN_TYPE_STRING); @@ -203,6 +247,18 @@ inline void Mixed::set_int(int64_t v) TIGHTDB_NOEXCEPT m_int = v; } +inline void Mixed::set_float(float v) TIGHTDB_NOEXCEPT +{ + m_type = COLUMN_TYPE_FLOAT; + m_float = v; +} + +inline void Mixed::set_double(double v) TIGHTDB_NOEXCEPT +{ + m_type = COLUMN_TYPE_DOUBLE; + m_double = v; +} + inline void Mixed::set_string(const char* v) TIGHTDB_NOEXCEPT { m_type = COLUMN_TYPE_STRING; @@ -235,7 +291,9 @@ inline std::basic_ostream& operator<<(std::basic_ostream& out, c switch (m.m_type) { case COLUMN_TYPE_BOOL: out << m.m_bool; break; case COLUMN_TYPE_INT: out << m.m_int; break; - case COLUMN_TYPE_STRING: out << m.m_str; break; + case COLUMN_TYPE_STRING: out << m.m_str; break; + case COLUMN_TYPE_FLOAT: out << m.m_float; break; + case COLUMN_TYPE_DOUBLE: out << m.m_double; break; case COLUMN_TYPE_BINARY: out << BinaryData(m.m_str, m.m_len); break; case COLUMN_TYPE_DATE: out << Date(m.m_date); break; case COLUMN_TYPE_TABLE: out << "subtable"; break; @@ -243,8 +301,8 @@ inline std::basic_ostream& operator<<(std::basic_ostream& out, c } out << ")"; return out; -} - +} + // Compare mixed with boolean @@ -292,6 +350,52 @@ template inline bool operator!=(const T& a, Wrap b) TIGHTDB_NOEX } +// Compare mixed with float + +inline bool operator==(Wrap a, float b) +{ + return Mixed(a).get_type() == COLUMN_TYPE_FLOAT && Mixed(a).get_float() == b; +} + +inline bool operator!=(Wrap a, float b) +{ + return Mixed(a).get_type() == COLUMN_TYPE_FLOAT && Mixed(a).get_float() != b; +} + +inline bool operator==(float a, Wrap b) +{ + return Mixed(b).get_type() == COLUMN_TYPE_FLOAT && a == Mixed(b).get_float(); +} + +inline bool operator!=(float a, Wrap b) +{ + return Mixed(b).get_type() == COLUMN_TYPE_FLOAT && a != Mixed(b).get_float(); +} + + +// Compare mixed with double + +inline bool operator==(Wrap a, double b) +{ + return Mixed(a).get_type() == COLUMN_TYPE_DOUBLE && Mixed(a).get_double() == b; +} + +inline bool operator!=(Wrap a, double b) +{ + return Mixed(a).get_type() == COLUMN_TYPE_DOUBLE && Mixed(a).get_double() != b; +} + +inline bool operator==(double a, Wrap b) +{ + return Mixed(b).get_type() == COLUMN_TYPE_DOUBLE && a == Mixed(b).get_double(); +} + +inline bool operator!=(double a, Wrap b) +{ + return Mixed(b).get_type() == COLUMN_TYPE_DOUBLE && a != Mixed(b).get_double(); +} + + // Compare mixed with zero-terminated string inline bool operator==(Wrap a, const char* b) TIGHTDB_NOEXCEPT diff --git a/src/tightdb/query.cpp b/src/tightdb/query.cpp index 47d9aaf97b7..c435b045a2d 100644 --- a/src/tightdb/query.cpp +++ b/src/tightdb/query.cpp @@ -1,3 +1,6 @@ +#include +#include +#include #include #include @@ -74,36 +77,47 @@ Query& Query::tableview(const Array &arr) return *this; } -Query& Query::equal(size_t column_ndx, int64_t value) + +// Binary +Query& Query::equal(size_t column_ndx, BinaryData b) { - ParentNode* const p = new NODE(value, column_ndx); + ParentNode* const p = new BINARYNODE(b.pointer, b.len, column_ndx); UpdatePointers(p, &p->m_child); return *this; } -Query& Query::equal(size_t column_ndx, BinaryData b) +// Generic 'simple type' condition +template +Query& Query::add_condition(size_t column_ndx, T value) { - ParentNode* const p = new BINARYNODE(b.pointer, b.len, column_ndx); - UpdatePointers(p, &p->m_child); + ParentNode* const parent = new N(value, column_ndx); + UpdatePointers(parent, &parent->m_child); return *this; } +// int64 +Query& Query::equal(size_t column_ndx, int64_t value) +{ + ParentNode* const p = new NODE(value, column_ndx); + UpdatePointers(p, &p->m_child); + return *this; +} Query& Query::not_equal(size_t column_ndx, int64_t value) { - ParentNode* const p = new NODE(value, column_ndx); + ParentNode* const p = new NODE(value, column_ndx); UpdatePointers(p, &p->m_child); return *this; } Query& Query::greater(size_t column_ndx, int64_t value) { - ParentNode* const p = new NODE(value, column_ndx); + ParentNode* const p = new NODE(value, column_ndx); UpdatePointers(p, &p->m_child); return *this; } Query& Query::greater_equal(size_t column_ndx, int64_t value) { if (value > LLONG_MIN) { - ParentNode* const p = new NODE(value - 1, column_ndx); + ParentNode* const p = new NODE(value - 1, column_ndx); UpdatePointers(p, &p->m_child); } // field >= LLONG_MIN has no effect @@ -112,7 +126,7 @@ Query& Query::greater_equal(size_t column_ndx, int64_t value) Query& Query::less_equal(size_t column_ndx, int64_t value) { if (value < LLONG_MAX) { - ParentNode* const p = new NODE(value + 1, column_ndx); + ParentNode* const p = new NODE(value + 1, column_ndx); UpdatePointers(p, &p->m_child); } // field <= LLONG_MAX has no effect @@ -120,11 +134,10 @@ Query& Query::less_equal(size_t column_ndx, int64_t value) } Query& Query::less(size_t column_ndx, int64_t value) { - ParentNode* const p = new NODE(value, column_ndx); + ParentNode* const p = new NODE(value, column_ndx); UpdatePointers(p, &p->m_child); return *this; } - Query& Query::between(size_t column_ndx, int64_t from, int64_t to) { greater_equal(column_ndx, from); @@ -133,11 +146,74 @@ Query& Query::between(size_t column_ndx, int64_t from, int64_t to) } Query& Query::equal(size_t column_ndx, bool value) { - ParentNode* const p = new NODE(value, column_ndx); + ParentNode* const p = new NODE(value, column_ndx); UpdatePointers(p, &p->m_child); return *this; } +// ------------- float +Query& Query::equal(size_t column_ndx, float value) +{ + return add_condition >(column_ndx, value); +} +Query& Query::not_equal(size_t column_ndx, float value) +{ + return add_condition >(column_ndx, value); +} +Query& Query::greater(size_t column_ndx, float value) +{ + return add_condition >(column_ndx, value); +} +Query& Query::greater_equal(size_t column_ndx, float value) +{ + return add_condition >(column_ndx, value); +} +Query& Query::less_equal(size_t column_ndx, float value) +{ + return add_condition >(column_ndx, value); +} +Query& Query::less(size_t column_ndx, float value) +{ + return add_condition >(column_ndx, value); +} +Query& Query::between(size_t column_ndx, float from, float to) +{ + greater_equal(column_ndx, from); + less_equal(column_ndx, to); + return *this; +} + +// ------------- double +Query& Query::equal(size_t column_ndx, double value) +{ + return add_condition >(column_ndx, value); +} +Query& Query::not_equal(size_t column_ndx, double value) +{ + return add_condition >(column_ndx, value); +} +Query& Query::greater(size_t column_ndx, double value) +{ + return add_condition >(column_ndx, value); +} +Query& Query::greater_equal(size_t column_ndx, double value) +{ + return add_condition >(column_ndx, value); +} +Query& Query::less_equal(size_t column_ndx, double value) +{ + return add_condition >(column_ndx, value); +} +Query& Query::less(size_t column_ndx, double value) +{ + return add_condition >(column_ndx, value); +} +Query& Query::between(size_t column_ndx, double from, double to) +{ + greater_equal(column_ndx, from); + less_equal(column_ndx, to); + return *this; +} // STRINGS Query& Query::equal(size_t column_ndx, const char* value, bool caseSensitive) @@ -191,6 +267,111 @@ Query& Query::not_equal(size_t column_ndx, const char* value, bool caseSensitive return *this; } + +// Aggregates ================================================================================= + + +template +R Query::aggregate(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const +{ + if (end == size_t(-1)) + end = m_table->size(); + + typedef typename ColumnTypeTraits::column_type ColType; + const ColType& c = m_table->GetColumn::id>(column_ndx); + + if (first.size() == 0 || first[0] == 0) { + // User created query with no criteria; aggregate range + if (resultcount) + *resultcount = end-start; + + switch (action) { + case TDB_SUM: return c.sum(start, end); + case TDB_MIN: return c.minimum(start, end); + case TDB_MAX: return c.maximum(start, end); + default: TIGHTDB_ASSERT(false); return 0; + } + } + + Init(*m_table); + size_t matchcount = 0; + QueryState st; + st.init(action, NULL, limit); + R r = first[0]->aggregate(&st, start, end, column_ndx, &matchcount); + if (resultcount) + *resultcount = matchcount; + return r; +} + +int64_t Query::sum(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const +{ + return aggregate(column_ndx, resultcount, start, end, limit); +} +float Query::sum_float(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const +{ + return aggregate(column_ndx, resultcount, start, end, limit); +} +double Query::sum_double(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const +{ + return aggregate(column_ndx, resultcount, start, end, limit); +} + + +template +double Query::average(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const +{ + Init(*m_table); + + size_t resultcount2 = 0; + const R sum1 = aggregate(column_ndx, &resultcount2, start, end, limit); + const double avg1 = (double)sum1 / (double)(resultcount2 > 0 ? resultcount2 : 1); + + if (resultcount) + *resultcount = resultcount2; + return avg1; +} +double Query::average(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const +{ + return average(column_ndx, resultcount, start, end, limit); +} +double Query::average_float(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const +{ + return average(column_ndx, resultcount, start, end, limit); +} +double Query::average_double(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const +{ + return average(column_ndx, resultcount, start, end, limit); +} + +int64_t Query::maximum(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const +{ + return aggregate(column_ndx, resultcount, start, end, limit); +} +float Query::maximum_float(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const +{ + return aggregate(column_ndx, resultcount, start, end, limit); +} +double Query::maximum_double(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const +{ + return aggregate(column_ndx, resultcount, start, end, limit); +} + +int64_t Query::minimum(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const +{ + return aggregate(column_ndx, resultcount, start, end, limit); +} +float Query::minimum_float(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const +{ + return aggregate(column_ndx, resultcount, start, end, limit); +} +double Query::minimum_double(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const +{ + return aggregate(column_ndx, resultcount, start, end, limit); +} + + + +// Grouping Query& Query::group() { update.push_back(0); @@ -198,6 +379,30 @@ Query& Query::group() first.push_back(0); return *this; } +Query& Query::end_group() +{ + if (first.size() < 2) { + error_code = "Unbalanced blockBegin/blockEnd"; + return *this; + } + + if (update[update.size()-2] != 0) + *update[update.size()-2] = first[first.size()-1]; + + if (first[first.size()-2] == 0) + first[first.size()-2] = first[first.size()-1]; + + if (update_override[update_override.size()-1] != 0) + update[update.size() - 2] = update_override[update_override.size()-1]; + else if (update[update.size()-1] != 0) + update[update.size() - 2] = update[update.size()-1]; + + first.pop_back(); + update.pop_back(); + update_override.pop_back(); + return *this; +} + Query& Query::Or() { ParentNode* const o = new OR_NODE(first[first.size()-1]); @@ -228,29 +433,6 @@ void Query::end_subtable() subtables.pop_back(); } -Query& Query::end_group() -{ - if (first.size() < 2) { - error_code = "Unbalanced blockBegin/blockEnd"; - return *this; - } - - if (update[update.size()-2] != 0) - *update[update.size()-2] = first[first.size()-1]; - - if (first[first.size()-2] == 0) - first[first.size()-2] = first[first.size()-1]; - - if (update_override[update_override.size()-1] != 0) - update[update.size() - 2] = update_override[update_override.size()-1]; - else if (update[update.size()-1] != 0) - update[update.size() - 2] = update[update.size()-1]; - - first.pop_back(); - update.pop_back(); - update_override.pop_back(); - return *this; -} size_t Query::find_next(size_t lastmatch) { @@ -284,75 +466,12 @@ TableView Query::find_all(size_t start, size_t end, size_t limit) // Use single threading TableView tv(*m_table); - first[0]->aggregate(&tv.get_ref_column(), start, end, limit); + QueryState st; + st.init(TDB_FINDALL, &tv.get_ref_column(), limit); + first[0]->aggregate(&st, start, end, not_found, NULL); return move(tv); } -int64_t Query::sum(size_t column, size_t* resultcount, size_t start, size_t end, size_t limit) const -{ - if (end == size_t(-1)) - end = m_table->size(); - - if (first.size() == 0 || first[0] == 0) { - // User created query with no criteria; sum() range - if (resultcount) - *resultcount = end-start; - const Column& c = m_table->GetColumn(column); - return c.sum(start, end); - } - - Init(*m_table); - size_t matchcount = 0; - - int64_t r = first[0]->aggregate(NULL, start, end, limit, TDB_SUM, column, &matchcount); - if (resultcount) - *resultcount = matchcount; - return r; -} - -int64_t Query::maximum(size_t column, size_t* resultcount, size_t start, size_t end, size_t limit) const -{ - if (end == size_t(-1)) - end = m_table->size(); - - if (first.size() == 0 || first[0] == 0) { - // User created query with no criteria; max() range - if (resultcount) - *resultcount = end-start; - const Column& c = m_table->GetColumn(column); - return c.maximum(start, end); - } - - Init(*m_table); - size_t matchcount = 0; - - int64_t r = first[0]->aggregate(NULL, start, end, limit, TDB_MAX, column, &matchcount); - if (resultcount) - *resultcount = matchcount; - return r; -} - -int64_t Query::minimum(size_t column, size_t* resultcount, size_t start, size_t end, size_t limit) const -{ - if (end == size_t(-1)) - end = m_table->size(); - - if (first.size() == 0 || first[0] == 0) { - // User created query with no criteria; min() range - if (resultcount) - *resultcount = end-start; - const Column& c = m_table->GetColumn(column); - return c.minimum(start, end); - } - - Init(*m_table); - size_t matchcount = 0; - - int64_t r = first[0]->aggregate(NULL, start, end, limit, TDB_MIN, column, &matchcount); - if (resultcount) - *resultcount = matchcount; - return r; -} size_t Query::count(size_t start, size_t end, size_t limit) const { @@ -365,25 +484,14 @@ size_t Query::count(size_t start, size_t end, size_t limit) const } Init(*m_table); - int64_t r = first[0]->aggregate(NULL, start, end, limit, TDB_COUNT); + QueryState st; + st.init(TDB_COUNT, NULL, limit); + int64_t r = first[0]->aggregate(&st, start, end, not_found, NULL); return size_t(r); } #include -double Query::average(size_t column_ndx, size_t* resultcount, size_t start, size_t end, size_t limit) const -{ - Init(*m_table); - - size_t resultcount2 = 0; - const int64_t sum1 = sum(column_ndx, &resultcount2, start, end, limit); - const double avg1 = (double)sum1 / (double)(resultcount2 > 0 ? resultcount2 : 1); - - if (resultcount != NULL) - *resultcount = resultcount2; - return avg1; -} - // todo, not sure if start, end and limit could be useful for delete. size_t Query::remove(size_t start, size_t end, size_t limit) { @@ -411,6 +519,7 @@ TableView Query::find_all_multi(size_t start, size_t end) { (void)start; (void)end; + #if MULTITHREAD // Initialization Init(*m_table); @@ -501,6 +610,8 @@ void Query::Init(const Table& table) const if (first[0] != NULL) { ParentNode* const top = (ParentNode*)first[0]; top->Init(table); + std::vectorv; + top->gather_children(v); } } diff --git a/src/tightdb/query.hpp b/src/tightdb/query.hpp index deb4e0aca1c..e69b05d2d61 100644 --- a/src/tightdb/query.hpp +++ b/src/tightdb/query.hpp @@ -62,6 +62,24 @@ class Query { Query& less_equal(size_t column_ndx, int64_t value); Query& between(size_t column_ndx, int64_t from, int64_t to); + // Conditions: float + Query& equal(size_t column_ndx, float value); + Query& not_equal(size_t column_ndx, float value); + Query& greater(size_t column_ndx, float value); + Query& greater_equal(size_t column_ndx, float value); + Query& less(size_t column_ndx, float value); + Query& less_equal(size_t column_ndx, float value); + Query& between(size_t column_ndx, float from, float to); + + // Conditions: double + Query& equal(size_t column_ndx, double value); + Query& not_equal(size_t column_ndx, double value); + Query& greater(size_t column_ndx, double value); + Query& greater_equal(size_t column_ndx, double value); + Query& less(size_t column_ndx, double value); + Query& less_equal(size_t column_ndx, double value); + Query& between(size_t column_ndx, double from, double to); + // Conditions: bool Query& equal(size_t column_ndx, bool value); @@ -104,19 +122,31 @@ class Query { Query& Or(); // Searching - size_t find_next(size_t lastmatch=-1); + size_t find_next(size_t lastmatch=size_t(-1)); TableView find_all(size_t start=0, size_t end=size_t(-1), size_t limit=size_t(-1)); ConstTableView find_all(size_t start=0, size_t end=size_t(-1), size_t limit=size_t(-1)) const; // Aggregates - int64_t sum(size_t column, size_t* resultcount=NULL, size_t start=0, size_t end = size_t(-1), size_t limit=size_t(-1)) const; - int64_t maximum(size_t column, size_t* resultcount=NULL, size_t start=0, size_t end = size_t(-1), size_t limit=size_t(-1)) const; - int64_t minimum(size_t column, size_t* resultcount=NULL, size_t start=0, size_t end = size_t(-1), size_t limit=size_t(-1)) const; - double average(size_t column_ndx, size_t* resultcount=NULL, size_t start=0, size_t end=size_t(-1), size_t limit=size_t(-1)) const; - size_t count(size_t start=0, size_t end=size_t(-1), size_t limit=size_t(-1)) const; + size_t count(size_t start=0, size_t end=size_t(-1), size_t limit=size_t(-1)) const; + + int64_t sum( size_t column_ndx, size_t* resultcount=NULL, size_t start=0, size_t end = size_t(-1), size_t limit=size_t(-1)) const; + double average( size_t column_ndx, size_t* resultcount=NULL, size_t start=0, size_t end = size_t(-1), size_t limit=size_t(-1)) const; + int64_t maximum(size_t column_ndx, size_t* resultcount=NULL, size_t start=0, size_t end = size_t(-1), size_t limit=size_t(-1)) const; + int64_t minimum(size_t column_ndx, size_t* resultcount=NULL, size_t start=0, size_t end = size_t(-1), size_t limit=size_t(-1)) const; + + float sum_float( size_t column_ndx, size_t* resultcount=NULL, size_t start=0, size_t end = size_t(-1), size_t limit=size_t(-1)) const; + double average_float( size_t column_ndx, size_t* resultcount=NULL, size_t start=0, size_t end = size_t(-1), size_t limit=size_t(-1)) const; + float maximum_float( size_t column_ndx, size_t* resultcount=NULL, size_t start=0, size_t end = size_t(-1), size_t limit=size_t(-1)) const; + float minimum_float (size_t column_ndx, size_t* resultcount=NULL, size_t start=0, size_t end = size_t(-1), size_t limit=size_t(-1)) const; + + double sum_double( size_t column_ndx, size_t* resultcount=NULL, size_t start=0, size_t end = size_t(-1), size_t limit=size_t(-1)) const; + double average_double(size_t column_ndx, size_t* resultcount=NULL, size_t start=0, size_t end = size_t(-1), size_t limit=size_t(-1)) const; + double maximum_double(size_t column_ndx, size_t* resultcount=NULL, size_t start=0, size_t end = size_t(-1), size_t limit=size_t(-1)) const; + double minimum_double(size_t column_ndx, size_t* resultcount=NULL, size_t start=0, size_t end = size_t(-1), size_t limit=size_t(-1)) const; + /* - time_t maximum_date(const Table& table, size_t column, size_t* resultcount=NULL, size_t start=0, size_t end = size_t(-1), size_t limit=size_t(-1)) const; - time_t minimum_date(const Table& table, size_t column, size_t* resultcount=NULL, size_t start=0, size_t end = size_t(-1), size_t limit=size_t(-1)) const; + TODO: time_t maximum_date(const Table& table, size_t column, size_t* resultcount=NULL, size_t start=0, size_t end = size_t(-1), size_t limit=size_t(-1)) const; + TODO: time_t minimum_date(const Table& table, size_t column, size_t* resultcount=NULL, size_t start=0, size_t end = size_t(-1), size_t limit=size_t(-1)) const; */ // Deletion @@ -177,8 +207,15 @@ class Query { std::vector subtables; std::vector all_nodes; mutable bool do_delete; + private: size_t m_threadcount; + + template Query& add_condition(size_t column_ndx, T value); + template + R aggregate(size_t column, size_t* resultcount=NULL, size_t start=0, size_t end = size_t(-1), size_t limit=size_t(-1)) const; + template + double average(size_t column_ndx, size_t* resultcount=NULL, size_t start=0, size_t end=size_t(-1), size_t limit=size_t(-1)) const; }; diff --git a/src/tightdb/query_conditions.hpp b/src/tightdb/query_conditions.hpp index fcc14fabbdf..8128ff717ba 100644 --- a/src/tightdb/query_conditions.hpp +++ b/src/tightdb/query_conditions.hpp @@ -28,7 +28,7 @@ namespace tightdb { -enum {COND_EQUAL, COND_NOTEQUAL, COND_GREATER, COND_LESS, COND_NONE, COND_COUNT}; +enum {COND_EQUAL, COND_NOTEQUAL, COND_GREATER, COND_GREATER_EQUAL, COND_LESS, COND_LESS_EQUAL, COND_NONE, COND_COUNT}; // FIXME: We cannot use all-uppercase names like 'CONTAINS' for @@ -71,6 +71,11 @@ struct ENDSWITH { }; struct EQUAL { + bool operator()(const bool v1, const bool v2) const + { + return v1 == v2; + } + bool operator()(const char *v1, const char* v1_upper, const char* v1_lower, const char* v2) const { (void)v1_lower; @@ -79,15 +84,16 @@ struct EQUAL { } bool operator()(const char *v1, size_t len1, const char* v2, size_t len2) const { - if(len1 != len2) + if (len1 != len2) return false; - if(len1 == 0) + if (len1 == 0) return true; - if(v1[len1 - 1] != v2[len1 - 1]) + if (v1[len1 - 1] != v2[len1 - 1]) return false; int i = memcmp(v1, v2, len1); return (i == 0); } + template bool operator()(const T& v1, const T& v2) const {return v1 == v2;} int condition(void) {return COND_EQUAL;} bool can_match(int64_t v, int64_t lbound, int64_t ubound) { return (v >= lbound && v <= ubound); } @@ -178,22 +184,25 @@ struct NONE { struct LESS { template bool operator()(const T& v1, const T& v2) const {return v1 < v2;} - int condition(void) {return COND_LESS;} + int condition(void) {return COND_LESS;} bool can_match(int64_t v, int64_t lbound, int64_t ubound) { (void)ubound; return (lbound < v); } bool will_match(int64_t v, int64_t lbound, int64_t ubound) { (void)lbound; return (ubound < v); } }; -struct LESSEQUAL { +struct LESS_EQUAL { template bool operator()(const T& v1, const T& v2) const {return v1 <= v2;} - int condition(void) {return -1;} + int condition(void) {return COND_LESS_EQUAL;} }; -struct GREATEREQUAL { +struct GREATER_EQUAL { template bool operator()(const T& v1, const T& v2) const {return v1 >= v2;} - int condition(void) {return -1;} + int condition(void) {return COND_GREATER_EQUAL;} }; } // namespace tightdb #endif // TIGHTDB_QUERY_CONDITIONS_HPP + + + diff --git a/src/tightdb/query_engine.hpp b/src/tightdb/query_engine.hpp index c1dc5ae177f..e97905b367e 100644 --- a/src/tightdb/query_engine.hpp +++ b/src/tightdb/query_engine.hpp @@ -17,6 +17,27 @@ * from TightDB Incorporated. * **************************************************************************/ + +/* + + + + +TConditionFunction: Each node has a condition from query_conditions.c such as EQUAL, GREATER_EQUAL, etc + +TConditionValue: Type of values in condition column. That is, int64_t, float, int, bool, etc + +TAction: What to do with each search result, from the enums TDB_RETURN_FIRST, TDB_COUNT, TDB_SUM, etc + +TResult: Type of result of actions - float, double, int64_t, etc. Special notes: For TDB_COUNT it's + int64_t, for TDB_FIND_ALL it's int64_t which points at destination array. + +TSourceColumn: Type of source column used in actions, or *ignored* if no source column is used (like for + TDB_COUNT, TDB_RETURN_FIRST) +*/ + + + #ifndef TIGHTDB_QUERY_ENGINE_HPP #define TIGHTDB_QUERY_ENGINE_HPP @@ -26,172 +47,376 @@ #include #include +#include #include -#include #include +#include #include #include +#include + namespace tightdb { +// Number of matches to find in best condition loop before breaking out to probe other conditions. Too low value gives too many +// constant time overheads everywhere in the query engine. Too high value makes it adapt less rapidly to changes in match +// frequencies. +const size_t findlocals = 16; + +// Distance between matches from which performance begins to flatten out because various initial overheads become insignificant +const size_t bestdist = 2; + +// Minimum number of matches required in a certain condition before it can be used to compute statistics. Too high value can spent +// too much time in a bad node (with high match frequency). Too low value gives inaccurate statistics. +const size_t probe_matches = 2; + +// +const size_t bitwidth_time_unit = 8; + + +typedef bool (*CallbackDummy)(int64_t); + +template struct ColumnTypeTraits; + +template<> struct ColumnTypeTraits { + typedef Column column_type; + typedef Array array_type; + static const ColumnType id = COLUMN_TYPE_INT; +}; +template<> struct ColumnTypeTraits { + typedef Column column_type; + typedef Array array_type; + static const ColumnType id = COLUMN_TYPE_BOOL; +}; +template<> struct ColumnTypeTraits { + typedef ColumnFloat column_type; + typedef ArrayFloat array_type; + static const ColumnType id = COLUMN_TYPE_FLOAT; +}; +template<> struct ColumnTypeTraits { + typedef ColumnDouble column_type; + typedef ArrayDouble array_type; + static const ColumnType id = COLUMN_TYPE_DOUBLE; +}; + + + +// Lets you access elements of an integer column in increasing order in a fast way where leafs are cached +class SequentialGetterParent {}; + +templateclass SequentialGetter : public SequentialGetterParent { +public: + typedef typename ColumnTypeTraits::column_type ColType; + typedef typename ColumnTypeTraits::array_type ArrayType; + + // We must destroy m_array immediately after its instantiation to avoid leak of what it preallocates. We cannot + // wait until a SequentialGetter destructor because GetBlock() maps it to data that we don't have ownership of. + SequentialGetter() + { + m_array.Destroy(); + } + + SequentialGetter(const Table& table, size_t column_ndx) + { + m_array.Destroy(); + if (column_ndx != not_found) + m_column = (ColType *)&table.GetColumnBase(column_ndx); + m_leaf_end = 0; + } + + SequentialGetter(ColType* column) + { + m_array.Destroy(); + m_column = column; + m_leaf_end = 0; + } + + inline bool CacheNext(size_t index) + { + // Return wether or not leaf array has changed (could be useful to know for caller) + if (index >= m_leaf_end) { + // GetBlock() does following: If m_column contains only a leaf, then just return pointer to that leaf and + // leave m_array untouched. Else call CreateFromHeader() on m_array (more time consuming) and return pointer to m_array. + m_array_ptr = (ArrayType*) (((Column*)m_column)->GetBlock(index, m_array, m_leaf_start, true)); + const size_t leaf_size = m_array_ptr->Size(); + m_leaf_end = m_leaf_start + leaf_size; + return true; + } + return false; + } + + inline T GetNext(size_t index) + { + CacheNext(index); + T av = m_array_ptr->Get(index - m_leaf_start); + return av; + } + + size_t m_leaf_start; + size_t m_leaf_end; + ColType* m_column; + + // See reason for having both a pointer and instance above + ArrayType* m_array_ptr; +private: + // Never access through m_array because it's uninitialized if column is just a leaf + ArrayType m_array; +}; + class ParentNode { public: - ParentNode() : m_cond(-1), m_table(NULL), m_array(false) {} - virtual ~ParentNode() { + ParentNode() : m_is_integer_node(false), m_table(NULL) {} + + // Note: Changed to avoid a lot of copying of the vector. Lasse, plese review. + void gather_children(std::vector& v) { + m_children.clear(); + ParentNode* p = this; + size_t i = v.size(); + v.push_back(this); + p = p->child_criteria(); + if (p != NULL) + p->gather_children(v); + + m_children = v; + m_children.erase(m_children.begin() + i); + m_children.insert(m_children.begin(), this); + + m_conds = m_children.size(); } - virtual void Init(const Table& table) {m_table = &table; if (m_child) m_child->Init(table);} - virtual size_t find_first(size_t start, size_t end) = 0; - template int64_t agg(Array* res, size_t start, size_t end, size_t limit, size_t agg_col, size_t* matchcount = 0) + struct score_compare { + bool operator ()(ParentNode const* a, ParentNode const* b) const { return (a->cost() < b->cost()); } + }; + + double cost(void) const { - m_array.state_init(action, &m_state, res); - Column *column_agg; // Column on which aggregate function is executed (or not_found actions that don't use row value, such as COUNT and FIND_ALL) - if (agg_col != not_found) - column_agg = (Column*)&m_table->GetColumnBase(agg_col); + return 16.0 / m_dD + m_dT; + } - size_t r = start - 1; - size_t count = 0; - for (;;) { - r = find_first(r + 1, end); - if (r == end || count == limit) - break; - count++; + size_t find_first(size_t start, size_t end) { + + size_t m = 0; + size_t next_cond = 0; + size_t first_cond = 0; + + while (start < end) { - if (agg_col != not_found && m_array.USES_VAL()) - m_array.FIND_ACTION(r, column_agg->Get(r), &m_state, &tightdb_dummy); - else - m_array.FIND_ACTION(r, 0, &m_state, &tightdb_dummy); + m = m_children[next_cond]->find_first_local(start, end); + + next_cond++; + if (next_cond == m_conds) + next_cond = 0; + + if (m == start) { + if (next_cond == first_cond) + return m; + } + else { + first_cond = next_cond; + start = m; + } } - if (matchcount) - *matchcount = int64_t(m_state.match_count); // why this cast? + return end; + } + - return m_state.state; + virtual ~ParentNode() {} + + virtual void Init(const Table& table) { + m_table = &table; + if (m_child) + m_child->Init(table); + } + + virtual size_t find_first_local(size_t start, size_t end) = 0; + + virtual ParentNode* child_criteria(void) { + return m_child; } + // Only purpose is to make all NODE classes have this function (overloaded only in NODE) + virtual size_t aggregate_call_specialized(ACTION /*TAction*/, ColumnType /*TResult*/, + QueryStateParent* /*st*/, + size_t /*start*/, size_t /*end*/, size_t /*local_limit*/, + SequentialGetterParent* /*source_column*/, size_t* /*matchcount*/) + { + TIGHTDB_ASSERT(false); + return 0; + } - virtual int64_t aggregate(Array* res, size_t start, size_t end, size_t limit, ACTION action = TDB_FINDALL, size_t agg_col = not_found, size_t* matchcount = 0) + template + size_t aggregate_local_selector(ParentNode* node, QueryState* st, size_t start, size_t end, size_t local_limit, + SequentialGetter* source_column, size_t* matchcount) { - if (action == TDB_FINDALL) - return agg(res, start, end, limit, agg_col, matchcount); - if (action == TDB_SUM) - return agg(res, start, end, limit, agg_col, matchcount); - if (action == TDB_MAX) - return agg(res, start, end, limit, agg_col, matchcount); - if (action == TDB_MIN) - return agg(res, start, end, limit, agg_col, matchcount); - if (action == TDB_COUNT) - return agg(res, start, end, limit, agg_col, matchcount); + size_t r; + + if (node->m_is_integer_node) + // call method in NODE + r = node->aggregate_call_specialized(TAction, ColumnTypeTraits::id,(QueryStateParent*)st, + start, end, local_limit, source_column, matchcount); + else + // call method in ParentNode + r = aggregate_local(st, start, end, local_limit, source_column, matchcount); + return r; + } + + + template + TResult aggregate(QueryState* st, size_t start, size_t end, size_t agg_col, size_t* matchcount) + { + if (end == size_t(-1)) + end = m_table->size(); + + SequentialGetter* source_column = NULL; + + if (agg_col != not_found) + source_column = new SequentialGetter(*m_table, agg_col); + + size_t td; + + while (start < end) { + size_t best = std::distance(m_children.begin(), std::min_element(m_children.begin(), m_children.end(), score_compare())); + + // Find a large amount of local matches in best condition + td = m_children[best]->m_dT == 0.0 ? end : (start + 1000 > end ? end : start + 1000); + + start = aggregate_local_selector(m_children[best], st, start, td, findlocals, source_column, matchcount); + + // Make remaining conditions compute their m_dD (statistics) + for (size_t c = 0; c < m_children.size() && start < end; c++) { + if (c == best) + continue; + + // Skip test if there is no way its cost can ever be better than best node's + double cost = m_children[c]->cost(); + if (m_children[c]->m_dT < cost) { + + // Limit to bestdist in order not to skip too large parts of index nodes + size_t maxD = m_children[c]->m_dT == 0.0 ? end - start : bestdist; + td = m_children[c]->m_dT == 0.0 ? end : (start + maxD > end ? end : start + maxD); + start = aggregate_local_selector(m_children[best], st, start, td, probe_matches, source_column, matchcount); + } + } + } + + if (matchcount) + *matchcount = st->match_count; + delete source_column; + + return st->state; - TIGHTDB_ASSERT(false); - return uint64_t(-1); // why this cast? } + template + size_t aggregate_local(QueryStateParent* st, size_t start, size_t end, size_t local_limit, + SequentialGetterParent* source_column, size_t* matchcount) + { + // aggregate called on non-integer column type. Speed of this function is not as critical as speed of the + // integer version, because find_first_local() is relatively slower here (because it's non-integers). + // + // Todo: Two speedups are possible. Simple: Initially test if there are no sub criterias and run find_first_local() + // in a tight loop if so (instead of testing if there are sub criterias after each match). Harder: Specialize + // data type array to make array call match() directly on each match, like for integers. + + (void)matchcount; + size_t local_matches = 0; + + size_t r = start - 1; + for (;;) { + if (local_matches == local_limit) { + m_dD = double(r - start) / local_matches; + return r + 1; + } + + // Find first match in this condition node + r = find_first_local(r + 1, end); + if (r == end) { + m_dD = double(r - start) / local_matches; + return end; + } + + local_matches++; + + // Find first match in remaining condition nodes + size_t m = r; + for (size_t c = 1; c < m_conds; c++) { + m = m_children[c]->find_first_local(r, r + 1); + if (m != r) { + break; + } + } + + // If index of first match in this node equals index of first match in all remaining nodes, we have a final match + if (m == r) { + TSourceColumn av = (TSourceColumn)0; + if (source_column != NULL) + av = static_cast*>(source_column)->GetNext(r); // todo, avoid GetNext if value not needed (if !uses_val) + ((QueryState*)st)->template match(r, 0, TResult(av), CallbackDummy()); + } + } + } + + virtual std::string Verify(void) { - if (m_error_code != "") - return m_error_code; + if (error_code != "") + return error_code; if (m_child == 0) return ""; else return m_child->Verify(); } - virtual void Destroy() { - // This destructor must be called if, and only if, caller has created a NODE object with non-allocating version - // of new() - i.e. the fast method to create an instance to perform fast queries with no time overhead like in - // Column::aggregate. Todo: Rewrite into not using std::string. - m_error_code.~basic_string(); - } - -protected: - friend class Query; ParentNode* m_child; + std::vectorm_children; + + size_t m_condition_column_idx; // Column of search criteria + bool m_is_integer_node; // true for NODE, false for any other + + size_t m_conds; + double m_dD; // Average row distance between each local match at current position + double m_dT; // Time overhead of testing index i + 1 if we have just tested index i. > 1 for linear scans, 0 for index/tableview + + size_t m_probes; + size_t m_matches; + protected: - int m_cond; - size_t m_column_id; const Table* m_table; - std::string m_error_code; - Array m_array; - state_state m_state; + std::string error_code; + }; -// FIXME: We cannot use all-uppercase names like 'ARRAYNODE' for -// classes since the risk of colliding with one of the customers macro -// names is too high. In short, the all-uppercase name space is -// reserved for macros. class ARRAYNODE: public ParentNode { public: ARRAYNODE(const Array& arr) : m_arr(arr), m_max(0), m_next(0), m_size(arr.Size()) {m_child = 0;} void Init(const Table& table) { + m_table = &table; + + m_dT = 0.0; + m_dD = m_table->size() / (m_arr.Size() + 1.0); + m_probes = 0; + m_matches = 0; + m_next = 0; if (m_size > 0) m_max = m_arr.Get(m_size - 1); - if (m_child) - m_child->Init(table); + if (m_child) m_child->Init(table); } - size_t find_first(size_t start, size_t end) + size_t find_first_local(size_t start, size_t end) { - for (size_t s = start; s < end; ++s) { - // Test first few values and end - if (m_size == 0) - return end; - - if (m_next < m_size && m_arr.GetAsSizeT(m_next) >= start) goto match; else ++m_next; - if (m_next < m_size && m_arr.GetAsSizeT(m_next) >= start) goto match; else ++m_next; - - if (start > m_max) return end; - - if (m_next < m_size && m_arr.GetAsSizeT(m_next) >= start) goto match; else ++m_next; - if (m_next < m_size && m_arr.GetAsSizeT(m_next) >= start) goto match; else ++m_next; - - // Find bounds - --m_next; - size_t add; - add = 1; - while (m_next + add < m_size && to_size_t(m_arr.Get(m_next + add)) < start) - add *= 2; - - // Do binary search inside bounds - TIGHTDB_ASSERT(m_arr.GetAsSizeT(m_arr.Size() - 1) >= start); - - size_t high; - high = m_next + add < m_size ? m_next + add : m_size; - m_next = m_next + add / 2 - 1; - - while (high - m_next > 1) { - const size_t probe = (m_next + high) / 2; - const size_t v = m_arr.GetAsSizeT(probe); - if (v < start) - m_next = probe; - else - high = probe; - } - if (high == m_next + add) - m_next = end; - else - m_next = high; -match: - // Test remaining query criterias - s = m_arr.Get(m_next); - ++m_next; - if (m_child == 0) - return s; - else { - const size_t a = m_child->find_first(s, end); - if (s == a) - return s; - else - s = a - 1; - } - } - return end; + size_t r = m_arr.FindGTE(start, m_next); + if (r == not_found) + return end; + + m_next = r; + return m_arr.Get(r); } protected: @@ -199,63 +424,33 @@ class ARRAYNODE: public ParentNode { size_t m_max; size_t m_next; size_t m_size; - }; - - -/* -// This is a template NODE, used if you want to special-handle new data types for performance. - -template class NODE: public ParentNode { -public: - NODE(T v, size_t column) : m_value(v), m_column(column) {m_child = 0;} - ~NODE() {delete m_child; } - - size_t find_first(size_t start, size_t end, const Table& table) - { - const C& column = (C&)(table.GetColumnBase(m_column)); - const F function = {}; - for (size_t s = start; s < end; ++s) { - const T t = column.Get(s); - if (function(t, m_value)) { - if (m_child == 0) - return s; - else { - const size_t a = m_child->find_first(s, end, table); - if (s == a) - return s; - else - s = a - 1; - } - } - } - return end; - } - -protected: - T m_value; - size_t m_column; -}; -*/ - -// TODO: Not finished class SUBTABLE: public ParentNode { public: SUBTABLE(size_t column): m_column(column) {m_child = 0; m_child2 = 0;} SUBTABLE() {}; void Init(const Table& table) { - m_table = &table; + m_dT = 100.0; + m_dD = 10.0; + m_probes = 0; + m_matches = 0; - if (m_child) + m_table = &table; + + if (m_child) { m_child->Init(table); + std::vector v; + m_child->gather_children(v); + } + if (m_child2) m_child2->Init(table); } - - size_t find_first(size_t start, size_t end) + + size_t find_first_local(size_t start, size_t end) { TIGHTDB_ASSERT(m_table); TIGHTDB_ASSERT(m_child); @@ -267,306 +462,398 @@ class SUBTABLE: public ParentNode { const size_t subsize = subtable->size(); const size_t sub = m_child->find_first(0, subsize); - if (sub != subsize) { - if (m_child2 == 0) - return s; - else { - const size_t a = m_child2->find_first(s, end); - if (s == a) - return s; - else - s = a - 1; - } - } + if (sub != subsize) + return s; } return end; } -protected: - friend class Query; + ParentNode* child_criteria(void) { + return m_child2; + } + ParentNode* m_child2; size_t m_column; }; - -// FIXME: We cannot use all-uppercase names like 'NODE' for classes -// since the risk of colliding with one of the customers macro names -// is too high. In short, the all-uppercase name space is reserved for -// macros. -template class NODE: public ParentNode { +// NODE is for conditions for types stored as integers in a tightdb::Array (int, date, bool) +template class NODE: public ParentNode { public: - // NOTE: Be careful to call Array(false) constructors on m_array and m_array_agg in the initializer list, otherwise + typedef typename ColumnTypeTraits::column_type ColType; + + // NOTE: Be careful to call Array(no_prealloc_tag) constructors on m_array in the initializer list, otherwise // their default constructors are called which are slow - NODE(T v, size_t column) : m_value(v), m_leaf_start(0), m_leaf_end(0), m_local_end(0), m_array_agg(false), m_leaf_end_agg(0) { - m_column_id = column; + NODE(TConditionValue v, size_t column) : m_value(v), m_array(Array::no_prealloc_tag()) { + m_is_integer_node = true; + m_condition_column_idx = column; m_child = 0; - F f; - m_cond = f.condition(); + m_conds = 0; + m_dT = 1.0; + m_dD = 100.0; + m_probes = 0; + m_matches = 0; } - // Only purpose of this function is to let you quickly create a NODE object and call aggregate() on it to aggregate + // Only purpose of this function is to let you quickly create a NODE object and call aggregate_local() on it to aggregate // on a single stand-alone column, with 1 or 0 search criterias, without involving any tables, etc. Todo, could - // be merged with Init somehowt to simplify + // be merged with Init somehow to simplify void QuickInit(Column *column, int64_t value) { - m_column = column; + m_condition_column = column; m_leaf_end = 0; m_value = value; + m_conds = 0; } - // TODO, Make caller set m_column_id through this function instead of through constructor, to simplify code. - // This also allows to get rid of QuickInit() void Init(const Table& table) { - m_column = (C*)&table.GetColumnBase(m_column_id); + m_condition_column = (ColType*)&table.GetColumnBase(m_condition_column_idx); m_table = &table; m_leaf_end = 0; if (m_child) m_child->Init(table); } - int64_t aggregate(Array* res, size_t start, size_t end, size_t limit, ACTION action = TDB_FINDALL, - size_t agg_col = not_found, size_t* matchcount = 0) - { - if (action == TDB_FINDALL) - return aggregate(res, start, end, limit, agg_col, matchcount); - if (action == TDB_SUM) - return aggregate(res, start, end, limit, agg_col, matchcount); - if (action == TDB_MAX) - return aggregate(res, start, end, limit, agg_col, matchcount); - if (action == TDB_MIN) - return aggregate(res, start, end, limit, agg_col, matchcount); - if (action == TDB_COUNT) - return aggregate(res, start, end, limit, agg_col, matchcount); + // This function is called from Array::find() for each search result if TAction == TDB_CALLBACK_IDX + // in the NODE::aggregate_local() call. Used if aggregate source column is different from search criteria column + template bool match_callback(int64_t v) { + size_t i = to_size_t(v); + m_last_local_match = i; + m_local_matches++; + QueryState* state = static_cast*>(m_state); + SequentialGetter* source_column = static_cast*>(m_source_column); + + // Test remaining sub conditions of this node. m_children[0] is the node that called match_callback(), so skip it + for (size_t c = 1; c < m_conds; c++) { + m_children[c]->m_probes++; + size_t m = m_children[c]->find_first_local(i, i + 1); + if (m != i) + return (m_local_matches != m_local_limit); + } - TIGHTDB_ASSERT(false); - return uint64_t(-1); + bool b; + if (state->template uses_val()) { // Compiler cannot see that Column::Get has no side effect and result is discarded + TResult av = source_column->GetNext(i); + b = state->template match(i, 0, av, CallbackDummy()); + } + else { + b = state->template match(i, 0, TResult(0), CallbackDummy()); + } + + if (m_local_matches == m_local_limit) + return false; + else + return b; } - // This function is called from Array::find() for each search result if action == TDB_CALLBACK_IDX - // in the NODE::aggregate() call. Used if aggregate source column (column on which SUM or MAX or MIN or whatever is being performed) is different from search criteria column - template bool match_callback(int64_t v) { - if (to_size_t(v) >= m_leaf_end_agg) { - m_column_agg->GetBlock(to_size_t(v), m_array_agg, m_leaf_start_agg); - const size_t leaf_size = m_array_agg.Size(); - m_leaf_end_agg = m_leaf_start_agg + leaf_size; - } - int64_t av = 0; - if (m_array.USES_VAL()) // Compiler cannot see that Column::Get has no side effect and result is discarded - av = m_array_agg.Get(to_size_t(v) - m_leaf_start_agg); - bool b = m_array.FIND_ACTION(to_size_t(v), av, &m_state, &tightdb_dummy); + size_t aggregate_call_specialized(ACTION TAction, ColumnType col_id, QueryStateParent* st, + size_t start, size_t end, size_t local_limit, + SequentialGetterParent* source_column, size_t* matchcount) + { + size_t ret; + + if (TAction == TDB_RETURN_FIRST) + ret = aggregate_local(st, start, end, local_limit, source_column, matchcount); + + else if (TAction == TDB_SUM && col_id == COLUMN_TYPE_INT) + ret = aggregate_local(st, start, end, local_limit, source_column, matchcount); + else if (TAction == TDB_SUM && col_id == COLUMN_TYPE_FLOAT) + // todo, fixme, see if we must let sum return a double even when summing a float coltype + ret = aggregate_local(st, start, end, local_limit, source_column, matchcount); + else if (TAction == TDB_SUM && col_id == COLUMN_TYPE_DOUBLE) + ret = aggregate_local(st, start, end, local_limit, source_column, matchcount); + + else if (TAction == TDB_MAX && col_id == COLUMN_TYPE_INT) + ret = aggregate_local(st, start, end, local_limit, source_column, matchcount); + else if (TAction == TDB_MAX && col_id == COLUMN_TYPE_FLOAT) + ret = aggregate_local(st, start, end, local_limit, source_column, matchcount); + else if (TAction == TDB_MAX && col_id == COLUMN_TYPE_DOUBLE) + ret = aggregate_local(st, start, end, local_limit, source_column, matchcount); + + else if (TAction == TDB_MIN && col_id == COLUMN_TYPE_INT) + ret = aggregate_local(st, start, end, local_limit, source_column, matchcount); + else if (TAction == TDB_MIN && col_id == COLUMN_TYPE_FLOAT) + ret = aggregate_local(st, start, end, local_limit, source_column, matchcount); + else if (TAction == TDB_MIN && col_id == COLUMN_TYPE_DOUBLE) + ret = aggregate_local(st, start, end, local_limit, source_column, matchcount); + + else if (TAction == TDB_COUNT) + ret = aggregate_local(st, start, end, local_limit, source_column, matchcount); + + else if (TAction == TDB_FINDALL) + ret = aggregate_local(st, start, end, local_limit, source_column, matchcount); + + else if (TAction == TDB_CALLBACK_IDX) + ret = aggregate_local(st, start, end, local_limit, source_column, matchcount); + + else { + TIGHTDB_ASSERT(false); + return 0; + } - return b; + return ret; } - // res destination array if action == TDB_FINDALL. Ignored if other action - // m_column Set in NODE constructor and is used as source for search criteria for this NODE - // agg_col column number in m_table which must act as source for aggreate action - template int64_t aggregate(Array* res, size_t start, size_t end, size_t limit, size_t agg_col, size_t* matchcount = 0) { - F f; + + // source_column column number in m_table which must act as source for aggreate TAction + template + size_t aggregate_local(QueryStateParent* st, size_t start, size_t end, size_t local_limit, + SequentialGetterParent* source_column, size_t* matchcount) { + + + TConditionFunction f; int c = f.condition(); - if (agg_col != not_found) - m_column_agg = (C*)&m_table->GetColumnBase(agg_col); - - m_array.state_init(action, &m_state, res); - - // If query only has 1 criteria, and arrays have built-in intrinsics for it, then perform it directly on array - if (m_child == 0 && limit > m_column->Size() && (SameType::value || SameType::value || SameType::value || SameType::value || SameType::value)) { - const Array* criteria_arr = NULL; - for (size_t s = start; s < end; ) { - // Cache internal leafs - if (s >= m_leaf_end) { - criteria_arr = m_column->GetBlock(s, m_array, m_leaf_start, true); - const size_t leaf_size = criteria_arr->Size(); - m_leaf_end = m_leaf_start + leaf_size; - const size_t e = end - m_leaf_start; - m_local_end = leaf_size < e ? leaf_size : e; - } + m_local_matches = 0; + m_local_limit = local_limit; + m_last_local_match = start - 1; - if (agg_col == m_column_id || agg_col == size_t(-1)) - criteria_arr->find(c, action, m_value, s - m_leaf_start, m_local_end, m_leaf_start, &m_state); - else - criteria_arr->find< F, TDB_CALLBACK_IDX >(m_value, s - m_leaf_start, m_local_end, m_leaf_start, &m_state, std::bind1st(std::mem_fun(&NODE::template match_callback < action > ), this)); - - s = m_leaf_end; + m_state = st; + for (size_t s = start; s < end; ) { + // Cache internal leafs + if (s >= m_leaf_end) { + m_condition_column->GetBlock(s, m_array, m_leaf_start); + m_leaf_end = m_leaf_start + m_array.Size(); + size_t w = m_array.GetBitWidth(); + m_dT = (w == 0 ? 1.0 / MAX_LIST_SIZE : w / float(bitwidth_time_unit)); } - if (action == TDB_FINDALL && res->Size() > limit) { - res->Resize(limit); // todo, optimize by adding limit argument to find() + size_t end2; + if (end > m_leaf_end) + end2 = m_leaf_end - m_leaf_start; + else + end2 = end - m_leaf_start; + + if (m_conds <= 1 && source_column != NULL && SameType::value && static_cast*>(source_column)->m_column == m_condition_column) { + m_array.find(c, TAction, m_value, s - m_leaf_start, end2, m_leaf_start, (QueryState*)st); + } + else { + QueryState jumpstate; // todo optimize by moving outside for loop + m_source_column = source_column; + m_array.find(m_value, s - m_leaf_start, end2, m_leaf_start, &jumpstate, + std::bind1st(std::mem_fun(&NODE::match_callback), this)); } + + if (m_local_matches == m_local_limit) + break; + + s = end2 + m_leaf_start; + } - if (matchcount) - *matchcount = int64_t(m_state.match_count); - return m_state.state; + if (matchcount) + *matchcount = int64_t(static_cast< QueryState* >(st)->match_count); + if (m_local_matches == m_local_limit) { + m_dD = (m_last_local_match + 1 - start) / (m_local_matches + 1.0); + return m_last_local_match + 1; } else { - size_t r = start - 1; - size_t n = 0; - while (n < limit) { - r = find_first(r + 1, end); - if (r == end) - break; - - if (agg_col == m_column_id || agg_col == size_t(-1)) { - if (m_array.USES_VAL()) // Compiler cannot see that Column::Get has no side effect and result is discarded - m_array.FIND_ACTION(r, m_column->Get(r), &m_state, &tightdb_dummy); - else - m_array.FIND_ACTION(r, 0, &m_state, &tightdb_dummy); - } - else - match_callback(r); - n++; - } + m_dD = (end - start) / (m_local_matches + 1.0); + return end; } - - if (matchcount) - *matchcount = int64_t(m_state.match_count); - return m_state.state; } - size_t find_first(size_t start, size_t end) + size_t find_first_local(size_t start, size_t end) { + TConditionFunction condition; TIGHTDB_ASSERT(m_table); - for (size_t s = start; s < end; ++s) { + while (start < end) { + // Cache internal leafs - if (s >= m_leaf_end) { - m_column->GetBlock(s, m_array, m_leaf_start); - const size_t leaf_size = m_array.Size(); - m_leaf_end = m_leaf_start + leaf_size; - const size_t e = end - m_leaf_start; - m_local_end = leaf_size < e ? leaf_size : e; + if (start >= m_leaf_end) { + m_condition_column->GetBlock(start, m_array, m_leaf_start); + m_leaf_end = m_leaf_start + m_array.Size(); } // Do search directly on cached leaf array - s = m_array.find_first(m_value, s - m_leaf_start, m_local_end); + if (start + 1 == end) { + if (condition(m_array.Get(start - m_leaf_start), m_value)) + return start; + else + return end; + } + + size_t end2; + if (end > m_leaf_end) + end2 = m_leaf_end - m_leaf_start; + else + end2 = end - m_leaf_start; + + size_t s = m_array.find_first(m_value, start - m_leaf_start, end2); if (s == not_found) { - s = m_leaf_end - 1; + start = m_leaf_end; continue; } else - s += m_leaf_start; + return s + m_leaf_start; + } - if (m_child == 0) - return s; - else { - const size_t a = m_child->find_first(s, end); - if (s == a) - return s; - else - s = a - 1; - } - } return end; } -protected: - T m_value; + TConditionValue m_value; protected: - C* m_column; // Column on which search criteria is applied - C *m_column_agg; // Column on which aggregate function is executed (can be same as m_column) + size_t m_last_local_match; + ColType* m_condition_column; // Column on which search criteria is applied + const Array* criteria_arr; + Array m_array; size_t m_leaf_start; size_t m_leaf_end; size_t m_local_end; - Array m_array_agg; - size_t m_leaf_start_agg; - size_t m_leaf_end_agg; - size_t m_local_end_agg; - + size_t m_local_matches; + size_t m_local_limit; + + QueryStateParent* m_state; + SequentialGetterParent* m_source_column; // Column of values used in aggregate (TDB_FINDALL, TDB_RETURN_FIRST, TDB_SUM, etc) }; -template class BINARYNODE: public ParentNode { +template class STRINGNODE: public ParentNode { public: - template int64_t find_all(Array* /*res*/, size_t /*start*/, size_t /*end*/, size_t /*limit*/, size_t /*agg_col*/) {TIGHTDB_ASSERT(false); return 0;} + template + int64_t find_all(Array* res, size_t start, size_t end, size_t limit, size_t source_column) + { + TIGHTDB_ASSERT(false); + return 0; + } - BINARYNODE(const char* v, size_t len, size_t column) + STRINGNODE(const char* v, size_t column) { - m_column_id = column; + m_condition_column_idx = column; m_child = 0; - m_len = len; - m_value = (char *)malloc(len); - memcpy(m_value, v, len); + + m_value = (char *)malloc(strlen(v)*6); + memcpy(m_value, v, strlen(v) + 1); + m_ucase = (char *)malloc(strlen(v)*6); + m_lcase = (char *)malloc(strlen(v)*6); + + const bool b1 = utf8case(v, m_lcase, false); + const bool b2 = utf8case(v, m_ucase, true); + if (!b1 || !b2) + error_code = "Malformed UTF-8: " + std::string(m_value); } - ~BINARYNODE() { - free((void*)m_value); + ~STRINGNODE() { + free((void*)m_value); free((void*)m_ucase); free((void*)m_lcase); } void Init(const Table& table) { + m_dT = 10.0; + m_dD = 10.0; + m_probes = 0; + m_matches = 0; + m_table = &table; - m_column = &table.GetColumnBase(m_column_id); - m_column_type = table.GetRealColumnType(m_column_id); + m_condition_column = &table.GetColumnBase(m_condition_column_idx); + m_column_type = table.GetRealColumnType(m_condition_column_idx); - if (m_child) - m_child->Init(table); + if (m_child) m_child->Init(table); } - size_t find_first(size_t start, size_t end) + size_t find_first_local(size_t start, size_t end) { - F function; + TConditionFunction cond; for (size_t s = start; s < end; ++s) { - const char* t = ((const ColumnBinary*)m_column)->Get(s).pointer; - size_t len2 = ((const ColumnBinary*)m_column)->Get(s).len; + const char* t; - if (function(m_value, m_len, t, len2)) { - if (m_child == 0) - return s; - else { - const size_t a = m_child->find_first(s, end); - if (s == a) - return s; - else - s = a - 1; - } + // todo, can be optimized by placing outside loop + if (m_column_type == COLUMN_TYPE_STRING) + t = ((const AdaptiveStringColumn*)m_condition_column)->Get(s); + else { + //TODO: First check if string is in key list + t = ((const ColumnStringEnum*)m_condition_column)->Get(s); } + + if (cond(m_value, m_ucase, m_lcase, t)) + return s; } return end; } protected: char* m_value; - size_t m_len; - const ColumnBase* m_column; + char* m_lcase; + char* m_ucase; + const ColumnBase* m_condition_column; ColumnType m_column_type; }; -template class STRINGNODE: public ParentNode { +// Can be used for simple types (currently float and double) +template class BASICNODE: public ParentNode { public: - template int64_t find_all(Array* /*res*/, size_t /*start*/, size_t /*end*/, size_t /*limit*/, size_t /*agg_col*/) {TIGHTDB_ASSERT(false); return 0;} - - STRINGNODE(const char* v, size_t column) + typedef typename ColumnTypeTraits::column_type ColType; + + BASICNODE(TConditionValue v, size_t column_ndx) : m_value(v) { - m_column_id = column; + m_condition_column_idx = column_ndx; m_child = 0; + } - m_value = (char *)malloc(strlen(v)*6); - memcpy(m_value, v, strlen(v) + 1); - m_ucase = (char *)malloc(strlen(v)*6); - m_lcase = (char *)malloc(strlen(v)*6); + // Only purpose of this function is to let you quickly create a NODE object and call aggregate_local() on it to aggregate + // on a single stand-alone column, with 1 or 0 search criterias, without involving any tables, etc. Todo, could + // be merged with Init somehow to simplify + void QuickInit(BasicColumn *column, TConditionValue value) { + m_condition_column.m_column = (ColType*)column; + m_condition_column.m_leaf_end = 0; + m_value = value; + m_conds = 0; + } - const bool b1 = utf8case(v, m_lcase, false); - const bool b2 = utf8case(v, m_ucase, true); - if (!b1 || !b2) - m_error_code = "Malformed UTF-8: " + std::string(m_value); + void Init(const Table& table) + { + m_table = &table; + m_condition_column.m_column = (ColType*)(&table.GetColumnBase(m_condition_column_idx)); + m_condition_column.m_leaf_end = 0; + + if (m_child) + m_child->Init(table); } - ~STRINGNODE() { - free((void*)m_value); free((void*)m_ucase); free((void*)m_lcase); + + size_t find_first_local(size_t start, size_t end) { + TConditionFunction cond; + + for (size_t s = start; s < end; ++s) { + TConditionValue v = m_condition_column.GetNext(s); + if (cond(v, m_value)) + return s; + } + return end; + } + +protected: + TConditionValue m_value; + SequentialGetter m_condition_column; +}; + + +template class BINARYNODE: public ParentNode { +public: + template int64_t find_all(Array* /*res*/, size_t /*start*/, size_t /*end*/, size_t /*limit*/, size_t /*source_column*/) {TIGHTDB_ASSERT(false); return 0;} + + BINARYNODE(const char* v, size_t len, size_t column) + { + m_condition_column_idx = column; + m_child = 0; + m_len = len; + m_value = (char *)malloc(len); + memcpy(m_value, v, len); + } + ~BINARYNODE() { + free((void*)m_value); } void Init(const Table& table) { m_table = &table; - m_column = &table.GetColumnBase(m_column_id); - m_column_type = table.GetRealColumnType(m_column_id); + m_condition_column = (const ColumnBinary*)&table.GetColumnBase(m_condition_column_idx); + m_column_type = table.GetRealColumnType(m_condition_column_idx); if (m_child) m_child->Init(table); @@ -574,20 +861,13 @@ template class STRINGNODE: public ParentNode { size_t find_first(size_t start, size_t end) { - F function; + TConditionFunction condition; for (size_t s = start; s < end; ++s) { - const char* t; - - // todo, can be optimized by placing outside loop - if (m_column_type == COLUMN_TYPE_STRING) - t = ((const AdaptiveStringColumn*)m_column)->Get(s); - else { - //TODO: First check if string is in key list - t = ((const ColumnStringEnum*)m_column)->Get(s); - } + const char* t = m_condition_column->Get(s).pointer; + size_t len2 = m_condition_column->Get(s).len; - if (function(m_value, m_ucase, m_lcase, t)) { + if (condition(m_value, m_len, t, len2)) { if (m_child == 0) return s; else { @@ -602,22 +882,38 @@ template class STRINGNODE: public ParentNode { return end; } + size_t find_first_local(size_t start, size_t end) { + TConditionFunction condition; + + for (size_t s = start; s < end; ++s) { + const char* value = m_condition_column->Get(s).pointer; + size_t len = m_condition_column->Get(s).len; + + if (condition(m_value, m_len, value, len)) + return s; + } + return end; + } + protected: char* m_value; - char* m_lcase; - char* m_ucase; - const ColumnBase* m_column; + size_t m_len; + const ColumnBinary* m_condition_column; ColumnType m_column_type; }; - template <> class STRINGNODE: public ParentNode { public: - template int64_t find_all(Array* /*res*/, size_t /*start*/, size_t /**/, size_t /*limit*/, size_t /*agg_col*/) {TIGHTDB_ASSERT(false); return 0;} + template + int64_t find_all(Array* res, size_t start, size_t end, size_t limit, size_t source_column) + { + TIGHTDB_ASSERT(false); + return 0; + } STRINGNODE(const char* v, size_t column): m_key_ndx((size_t)-1) { - m_column_id = column; + m_condition_column_idx = column; m_child = 0; m_value = (char *)malloc(strlen(v)*6); memcpy(m_value, v, strlen(v) + 1); @@ -629,110 +925,100 @@ template <> class STRINGNODE: public ParentNode { void Init(const Table& table) { + m_dD = 10.0; + m_table = &table; - m_column = &table.GetColumnBase(m_column_id); - m_column_type = table.GetRealColumnType(m_column_id); + m_condition_column = &table.GetColumnBase(m_condition_column_idx); + m_column_type = table.GetRealColumnType(m_condition_column_idx); if (m_column_type == COLUMN_TYPE_STRING_ENUM) { - m_key_ndx = ((const ColumnStringEnum*)m_column)->GetKeyNdx(m_value); + m_dT = 1.0; + m_key_ndx = ((const ColumnStringEnum*)m_condition_column)->GetKeyNdx(m_value); + } + else { + m_dT = 10.0; } - if(m_column->HasIndex()) { - if(m_column_type == COLUMN_TYPE_STRING_ENUM) - ((ColumnStringEnum*)m_column)->find_all(m_index, m_value); + if (m_condition_column->HasIndex()) { + if (m_column_type == COLUMN_TYPE_STRING_ENUM) + ((ColumnStringEnum*)m_condition_column)->find_all(m_index, m_value); else { - ((AdaptiveStringColumn*)m_column)->find_all(m_index, m_value); + ((AdaptiveStringColumn*)m_condition_column)->find_all(m_index, m_value); } - has_index = true; last_indexed = 0; } - else - has_index = false; - if (m_child) m_child->Init(table); + if (m_child) + m_child->Init(table); } - size_t find_first(size_t start, size_t end) + size_t find_first_local(size_t start, size_t end) { TIGHTDB_ASSERT(m_table); for (size_t s = start; s < end; ++s) { - - if(has_index) { - for(;;) { - if(last_indexed >= m_index.Size()) { - s = not_found; - break; - } - size_t cand = m_index.GetAsSizeT(last_indexed); - ++last_indexed; - if(cand >= s && cand < end) { - s = cand; - break; - } - else if(cand >= end) { - s = not_found; - break; - } - } + if (m_condition_column->HasIndex()) { + size_t f = m_index.FindGTE(s, last_indexed); + if (f != not_found) + s = m_index.Get(f); + else + s = not_found; + last_indexed = f; } else { - // todo, can be optimized by placing outside loop if (m_column_type == COLUMN_TYPE_STRING) - s = ((const AdaptiveStringColumn*)m_column)->find_first(m_value, s, end); + s = ((const AdaptiveStringColumn*)m_condition_column)->find_first(m_value, s, end); else { - if (m_key_ndx == size_t(-1)) s = end; // not in key set + if (m_key_ndx == size_t(-1)) + s = end; // not in key set else { - const ColumnStringEnum* const cse = (const ColumnStringEnum*)m_column; + const ColumnStringEnum* const cse = (const ColumnStringEnum*)m_condition_column; s = cse->find_first(m_key_ndx, s, end); } } } - if (s == (size_t)-1) s = end; - - if (m_child == 0) - return s; - else { - const size_t a = m_child->find_first(s, end); - if (s == a) - return s; - else - s = a - 1; - } + return s; } return end; } protected: char* m_value; - bool has_index; - size_t last_indexed; private: - const ColumnBase* m_column; + const ColumnBase* m_condition_column; ColumnType m_column_type; size_t m_key_ndx; Array m_index; + size_t last_indexed; }; -// FIXME: We cannot use all-uppercase names like 'OR_NODE' for classes -// since the risk of colliding with one of the customers macro names -// is too high. In short, the all-uppercase name space is reserved for -// macros. class OR_NODE: public ParentNode { public: - template int64_t find_all(Array* /*res*/, size_t /*start*/, size_t /*end*/, size_t /*limit*/, size_t /*agg_col*/) {TIGHTDB_ASSERT(false); return 0;} + template int64_t find_all(Array* res, size_t start, size_t end, size_t limit, size_t source_column) + { + TIGHTDB_ASSERT(false); + return 0; + } + + OR_NODE(ParentNode* p1) : m_table(NULL) {m_child = NULL; m_cond[0] = p1; m_cond[1] = NULL;}; - OR_NODE(ParentNode* p1) {m_child = NULL; m_cond[0] = p1; m_cond[1] = NULL;}; void Init(const Table& table) { - for(size_t c = 0; c < 2; ++c) { + m_dT = 50.0; + m_dD = 10.0; + + std::vector v; + + for (size_t c = 0; c < 2; ++c) { m_cond[c]->Init(table); + v.clear(); + m_cond[c]->gather_children(v); m_last[c] = 0; m_was_match[c] = false; } @@ -743,15 +1029,15 @@ class OR_NODE: public ParentNode { m_table = &table; } - size_t find_first(size_t start, size_t end) + size_t find_first_local(size_t start, size_t end) { for (size_t s = start; s < end; ++s) { size_t f[2]; - for(size_t c = 0; c < 2; ++c) { - if(m_last[c] >= end) + for (size_t c = 0; c < 2; ++c) { + if (m_last[c] >= end) f[c] = end; - else if(m_was_match[c] && m_last[c] >= s) + else if (m_was_match[c] && m_last[c] >= s) f[c] = m_last[c]; else { size_t fmax = m_last[c] > s ? m_last[c] : s; @@ -764,24 +1050,15 @@ class OR_NODE: public ParentNode { s = f[0] < f[1] ? f[0] : f[1]; s = s > end ? end : s; - if (m_child == 0) - return s; - else { - const size_t a = m_child->find_first(s, end); - if (s == a) - return s; - else - s = a - 1; - } - + return s; } return end; } virtual std::string Verify(void) { - if (m_error_code != "") - return m_error_code; + if (error_code != "") + return error_code; if (m_cond[0] == 0) return "Missing left-hand side of OR"; if (m_cond[1] == 0) @@ -804,6 +1081,7 @@ class OR_NODE: public ParentNode { private: size_t m_last[2]; bool m_was_match[2]; + const Table* m_table; }; diff --git a/src/tightdb/replication.hpp b/src/tightdb/replication.hpp index fd128649665..4407d7c2501 100644 --- a/src/tightdb/replication.hpp +++ b/src/tightdb/replication.hpp @@ -491,6 +491,16 @@ inline error_code Replication::mixed_cmd(char cmd, std::size_t column_ndx, buf = encode_int(buf, int(value.get_bool())); transact_log_advance(buf); break; + case COLUMN_TYPE_FLOAT: + TIGHTDB_ASSERT(false); // FIXME: IMPLEMENT + //buf = encode_float(buf, value.get_float())); + transact_log_advance(buf); + break; + case COLUMN_TYPE_DOUBLE: + TIGHTDB_ASSERT(false); // FIXME: IMPLEMENT + //buf = encode_double(buf, value.get_double())); + transact_log_advance(buf); + break; case COLUMN_TYPE_DATE: buf = encode_int(buf, value.get_date()); transact_log_advance(buf); diff --git a/src/tightdb/table.cpp b/src/tightdb/table.cpp index a95cd8b39b2..e376512406e 100644 --- a/src/tightdb/table.cpp +++ b/src/tightdb/table.cpp @@ -27,6 +27,8 @@ #include #include #include +#include + #include #include #include @@ -38,7 +40,6 @@ using namespace std; namespace tightdb { - struct FakeParent: Table::Parent { virtual void update_child_ref(size_t, size_t) {} // Ignore virtual void child_destroyed(size_t) {} // Ignore @@ -107,6 +108,22 @@ void Table::CreateColumns() new_col = c; } break; + case COLUMN_TYPE_FLOAT: + { + ColumnFloat* c = new ColumnFloat(alloc); + m_columns.add(c->GetRef()); + c->SetParent(&m_columns, ref_pos); + new_col = c; + } + break; + case COLUMN_TYPE_DOUBLE: + { + ColumnDouble* c = new ColumnDouble(alloc); + m_columns.add(c->GetRef()); + c->SetParent(&m_columns, ref_pos); + new_col = c; + } + break; case COLUMN_TYPE_STRING: { AdaptiveStringColumn* c = new AdaptiveStringColumn(alloc); @@ -200,7 +217,8 @@ void Table::invalidate() void Table::InstantiateBeforeChange() { // Empty (zero-ref'ed) tables need to be instantiated before first modification - if (!m_columns.IsValid()) CreateColumns(); + if (!m_columns.IsValid()) + CreateColumns(); } void Table::CacheColumns() @@ -231,6 +249,20 @@ void Table::CacheColumns() new_col = c; } break; + case COLUMN_TYPE_FLOAT: + { + ColumnFloat* c = new ColumnFloat(ref, &m_columns, ndx_in_parent, alloc); + colsize = c->Size(); + new_col = c; + } + break; + case COLUMN_TYPE_DOUBLE: + { + ColumnDouble* c = new ColumnDouble(ref, &m_columns, ndx_in_parent, alloc); + colsize = c->Size(); + new_col = c; + } + break; case COLUMN_TYPE_STRING: { AdaptiveStringColumn* c = @@ -522,6 +554,24 @@ size_t Table::do_add_column(ColumnType type) c->fill(count); } break; + case COLUMN_TYPE_FLOAT: + { + ColumnFloat* c = new ColumnFloat(alloc); + m_columns.add(c->GetRef()); + c->SetParent(&m_columns, m_columns.Size()-1); + new_col = c; + c->fill(count); + } + break; + case COLUMN_TYPE_DOUBLE: + { + ColumnDouble* c = new ColumnDouble(alloc); + m_columns.add(c->GetRef()); + c->SetParent(&m_columns, m_columns.Size()-1); + new_col = c; + c->fill(count); + } + break; case COLUMN_TYPE_STRING: { AdaptiveStringColumn* c = new AdaptiveStringColumn(alloc); @@ -652,6 +702,7 @@ void Table::rename_column(const vector& column_path, const char* name) { m_spec_set.rename_column(column_path, name); } + bool Table::has_index(size_t column_ndx) const { TIGHTDB_ASSERT(column_ndx < get_column_count()); @@ -704,6 +755,8 @@ void Table::set_index(size_t column_ndx, bool update_spec) #endif } + + ColumnBase& Table::GetColumnBase(size_t ndx) { TIGHTDB_ASSERT(ndx < get_column_count()); @@ -719,97 +772,46 @@ const ColumnBase& Table::GetColumnBase(size_t ndx) const TIGHTDB_NOEXCEPT return *reinterpret_cast(m_cols.Get(ndx)); } -Column& Table::GetColumn(size_t ndx) -{ - ColumnBase& column = GetColumnBase(ndx); - TIGHTDB_ASSERT(column.IsIntColumn()); - return static_cast(column); -} -const Column& Table::GetColumn(size_t ndx) const TIGHTDB_NOEXCEPT +void Table::validate_column_type(const ColumnBase& column, ColumnType coltype, size_t ndx) const { - const ColumnBase& column = GetColumnBase(ndx); - TIGHTDB_ASSERT(column.IsIntColumn()); - return static_cast(column); + if (coltype == COLUMN_TYPE_INT || coltype == COLUMN_TYPE_DATE || coltype == COLUMN_TYPE_BOOL) { + TIGHTDB_ASSERT(column.IsIntColumn()); + } else { + TIGHTDB_ASSERT(coltype == GetRealColumnType(ndx)); + } + (void)column; + (void)ndx; } -AdaptiveStringColumn& Table::GetColumnString(size_t ndx) -{ - ColumnBase& column = GetColumnBase(ndx); - TIGHTDB_ASSERT(column.IsStringColumn()); - return static_cast(column); -} -const AdaptiveStringColumn& Table::GetColumnString(size_t ndx) const TIGHTDB_NOEXCEPT -{ - const ColumnBase& column = GetColumnBase(ndx); - TIGHTDB_ASSERT(column.IsStringColumn()); - return static_cast(column); -} +// TODO: get rid of the Column* template parameter +Column& Table::GetColumn(size_t ndx) { return GetColumn(ndx); } +const Column& Table::GetColumn(size_t ndx) const TIGHTDB_NOEXCEPT { return GetColumn(ndx); } -ColumnStringEnum& Table::GetColumnStringEnum(size_t ndx) -{ - TIGHTDB_ASSERT(ndx < get_column_count()); - InstantiateBeforeChange(); - TIGHTDB_ASSERT(m_cols.Size() == get_column_count()); - return *reinterpret_cast(m_cols.Get(ndx)); -} +AdaptiveStringColumn& Table::GetColumnString(size_t ndx) { return GetColumn(ndx); } +const AdaptiveStringColumn& Table::GetColumnString(size_t ndx) const TIGHTDB_NOEXCEPT { return GetColumn(ndx); } -const ColumnStringEnum& Table::GetColumnStringEnum(size_t ndx) const TIGHTDB_NOEXCEPT -{ - TIGHTDB_ASSERT(ndx < get_column_count()); - TIGHTDB_ASSERT(m_cols.Size() == get_column_count()); - return *reinterpret_cast(m_cols.Get(ndx)); -} +ColumnStringEnum& Table::GetColumnStringEnum(size_t ndx) { return GetColumn(ndx); } +const ColumnStringEnum& Table::GetColumnStringEnum(size_t ndx) const TIGHTDB_NOEXCEPT { return GetColumn(ndx); } -ColumnBinary& Table::GetColumnBinary(size_t ndx) -{ - ColumnBase& column = GetColumnBase(ndx); - TIGHTDB_ASSERT(column.IsBinaryColumn()); - return static_cast(column); -} +ColumnFloat& Table::GetColumnFloat(size_t ndx) { return GetColumn(ndx); } +const ColumnFloat& Table::GetColumnFloat(size_t ndx) const TIGHTDB_NOEXCEPT { return GetColumn(ndx); } -const ColumnBinary& Table::GetColumnBinary(size_t ndx) const TIGHTDB_NOEXCEPT -{ - const ColumnBase& column = GetColumnBase(ndx); - TIGHTDB_ASSERT(column.IsBinaryColumn()); - return static_cast(column); -} +ColumnDouble& Table::GetColumnDouble(size_t ndx) { return GetColumn(ndx); } +const ColumnDouble& Table::GetColumnDouble(size_t ndx) const TIGHTDB_NOEXCEPT { return GetColumn(ndx); } -ColumnTable &Table::GetColumnTable(size_t ndx) -{ - TIGHTDB_ASSERT(ndx < get_column_count()); - InstantiateBeforeChange(); - TIGHTDB_ASSERT(COLUMN_TYPE_TABLE == get_column_type(ndx)); - TIGHTDB_ASSERT(m_cols.Size() == get_column_count()); - return *static_cast(reinterpret_cast(m_cols.Get(ndx))); -} +ColumnBinary& Table::GetColumnBinary(size_t ndx) { return GetColumn(ndx); } +const ColumnBinary& Table::GetColumnBinary(size_t ndx) const TIGHTDB_NOEXCEPT { return GetColumn(ndx); } -const ColumnTable &Table::GetColumnTable(size_t ndx) const TIGHTDB_NOEXCEPT -{ - TIGHTDB_ASSERT(ndx < get_column_count()); - TIGHTDB_ASSERT(COLUMN_TYPE_TABLE == get_column_type(ndx)); - TIGHTDB_ASSERT(m_cols.Size() == get_column_count()); - return *static_cast(reinterpret_cast(m_cols.Get(ndx))); -} +ColumnTable &Table::GetColumnTable(size_t ndx) { return GetColumn(ndx); } +const ColumnTable &Table::GetColumnTable(size_t ndx) const TIGHTDB_NOEXCEPT { return GetColumn(ndx); } + +ColumnMixed& Table::GetColumnMixed(size_t ndx) { return GetColumn(ndx); } +const ColumnMixed& Table::GetColumnMixed(size_t ndx) const TIGHTDB_NOEXCEPT { return GetColumn(ndx); } -ColumnMixed& Table::GetColumnMixed(size_t ndx) -{ - TIGHTDB_ASSERT(ndx < get_column_count()); - InstantiateBeforeChange(); - TIGHTDB_ASSERT(COLUMN_TYPE_MIXED == get_column_type(ndx)); - TIGHTDB_ASSERT(m_cols.Size() == get_column_count()); - return *static_cast(reinterpret_cast(m_cols.Get(ndx))); -} -const ColumnMixed& Table::GetColumnMixed(size_t ndx) const TIGHTDB_NOEXCEPT -{ - TIGHTDB_ASSERT(m_cols.Size() == get_column_count()); - TIGHTDB_ASSERT(ndx < get_column_count()); - TIGHTDB_ASSERT(COLUMN_TYPE_MIXED == get_column_type(ndx)); - return *static_cast(reinterpret_cast(m_cols.Get(ndx))); -} size_t Table::add_empty_row(size_t num_rows) { @@ -885,6 +887,7 @@ void Table::remove(size_t ndx) #endif } + void Table::insert_subtable(size_t column_ndx, size_t ndx) { TIGHTDB_ASSERT(column_ndx < get_column_count()); @@ -989,6 +992,7 @@ void Table::clear_subtable(size_t col_idx, size_t row_idx) } } + int64_t Table::get_int(size_t column_ndx, size_t ndx) const TIGHTDB_NOEXCEPT { TIGHTDB_ASSERT(column_ndx < get_column_count()); @@ -1024,6 +1028,7 @@ void Table::add_int(size_t column_ndx, int64_t value) #endif } + bool Table::get_bool(size_t column_ndx, size_t ndx) const TIGHTDB_NOEXCEPT { TIGHTDB_ASSERT(column_ndx < get_column_count()); @@ -1056,7 +1061,7 @@ time_t Table::get_date(size_t column_ndx, size_t ndx) const TIGHTDB_NOEXCEPT TIGHTDB_ASSERT(ndx < m_size); const Column& column = GetColumn(column_ndx); - return time_t(column.Get(ndx)); + return (time_t)column.Get(ndx); } void Table::set_date(size_t column_ndx, size_t ndx, time_t value) @@ -1066,7 +1071,7 @@ void Table::set_date(size_t column_ndx, size_t ndx, time_t value) TIGHTDB_ASSERT(ndx < m_size); Column& column = GetColumn(column_ndx); - column.Set(ndx, int64_t(value)); + column.Set(ndx, (int64_t)value); #ifdef TIGHTDB_ENABLE_REPLICATION error_code err = get_local_transact_log().set_value(column_ndx, ndx, value); @@ -1088,6 +1093,83 @@ void Table::insert_int(size_t column_ndx, size_t ndx, int64_t value) #endif } + +float Table::get_float(size_t column_ndx, size_t ndx) const +{ + TIGHTDB_ASSERT(column_ndx < get_column_count()); + TIGHTDB_ASSERT(ndx < m_size); + + const ColumnFloat& column = GetColumnFloat(column_ndx); + return column.Get(ndx); +} + +void Table::set_float(size_t column_ndx, size_t ndx, float value) +{ + TIGHTDB_ASSERT(column_ndx < get_column_count()); + TIGHTDB_ASSERT(ndx < m_size); + + ColumnFloat& column = GetColumnFloat(column_ndx); + column.Set(ndx, value); + +#ifdef TIGHTDB_ENABLE_REPLICATION + error_code err = get_local_transact_log().set_value(column_ndx, ndx, value); + if (err) throw_error(err); +#endif +} + +void Table::insert_float(size_t column_ndx, size_t ndx, float value) +{ + TIGHTDB_ASSERT(column_ndx < get_column_count()); + TIGHTDB_ASSERT(ndx <= m_size); + + ColumnFloat& column = GetColumnFloat(column_ndx); + column.Insert(ndx, value); + +#ifdef TIGHTDB_ENABLE_REPLICATION + error_code err = get_local_transact_log().insert_value(column_ndx, ndx, value); + if (err) throw_error(err); +#endif +} + + +double Table::get_double(size_t column_ndx, size_t ndx) const +{ + TIGHTDB_ASSERT(column_ndx < get_column_count()); + TIGHTDB_ASSERT(ndx < m_size); + + const ColumnDouble& column = GetColumnDouble(column_ndx); + return column.Get(ndx); +} + +void Table::set_double(size_t column_ndx, size_t ndx, double value) +{ + TIGHTDB_ASSERT(column_ndx < get_column_count()); + TIGHTDB_ASSERT(ndx < m_size); + + ColumnDouble& column = GetColumnDouble(column_ndx); + column.Set(ndx, value); + +#ifdef TIGHTDB_ENABLE_REPLICATION + error_code err = get_local_transact_log().set_value(column_ndx, ndx, value); + if (err) throw_error(err); +#endif +} + +void Table::insert_double(size_t column_ndx, size_t ndx, double value) +{ + TIGHTDB_ASSERT(column_ndx < get_column_count()); + TIGHTDB_ASSERT(ndx <= m_size); + + ColumnDouble& column = GetColumnDouble(column_ndx); + column.Insert(ndx, value); + +#ifdef TIGHTDB_ENABLE_REPLICATION + error_code err = get_local_transact_log().insert_value(column_ndx, ndx, value); + if (err) throw_error(err); +#endif +} + + const char* Table::get_string(size_t column_ndx, size_t ndx) const TIGHTDB_NOEXCEPT { TIGHTDB_ASSERT(column_ndx < m_columns.Size()); @@ -1108,7 +1190,7 @@ const char* Table::get_string(size_t column_ndx, size_t ndx) const TIGHTDB_NOEXC size_t Table::get_string_length(size_t column_ndx, size_t ndx) const TIGHTDB_NOEXCEPT { - // FIXME: Implement faster get_string_length() + // TODO: Implement faster get_string_length() return strlen( get_string(column_ndx, ndx) ); } @@ -1160,6 +1242,7 @@ void Table::insert_string(size_t column_ndx, size_t ndx, const char* value) #endif } + BinaryData Table::get_binary(size_t column_ndx, size_t ndx) const { TIGHTDB_ASSERT(column_ndx < m_columns.Size()); @@ -1197,6 +1280,7 @@ void Table::insert_binary(size_t column_ndx, size_t ndx, const char* data, size_ #endif } + Mixed Table::get_mixed(size_t column_ndx, size_t ndx) const { TIGHTDB_ASSERT(column_ndx < m_columns.Size()); @@ -1207,11 +1291,15 @@ Mixed Table::get_mixed(size_t column_ndx, size_t ndx) const switch (type) { case COLUMN_TYPE_INT: - return Mixed(column.GetInt(ndx)); + return Mixed(column.get_int(ndx)); case COLUMN_TYPE_BOOL: return Mixed(column.get_bool(ndx)); case COLUMN_TYPE_DATE: return Mixed(Date(column.get_date(ndx))); + case COLUMN_TYPE_FLOAT: + return Mixed(column.get_float(ndx)); + case COLUMN_TYPE_DOUBLE: + return Mixed(column.get_double(ndx)); case COLUMN_TYPE_STRING: return Mixed(column.get_string(ndx)); // Throws case COLUMN_TYPE_BINARY: @@ -1243,7 +1331,7 @@ void Table::set_mixed(size_t column_ndx, size_t ndx, Mixed value) switch (type) { case COLUMN_TYPE_INT: - column.SetInt(ndx, value.get_int()); + column.set_int(ndx, value.get_int()); break; case COLUMN_TYPE_BOOL: column.set_bool(ndx, value.get_bool()); @@ -1251,6 +1339,12 @@ void Table::set_mixed(size_t column_ndx, size_t ndx, Mixed value) case COLUMN_TYPE_DATE: column.set_date(ndx, value.get_date()); break; + case COLUMN_TYPE_FLOAT: + column.set_float(ndx, value.get_float()); + break; + case COLUMN_TYPE_DOUBLE: + column.set_double(ndx, value.get_double()); + break; case COLUMN_TYPE_STRING: column.set_string(ndx, value.get_string()); break; @@ -1292,6 +1386,12 @@ void Table::insert_mixed(size_t column_ndx, size_t ndx, Mixed value) { case COLUMN_TYPE_DATE: column.insert_date(ndx, value.get_date()); break; + case COLUMN_TYPE_FLOAT: + column.insert_float(ndx, value.get_float()); + break; + case COLUMN_TYPE_DOUBLE: + column.insert_double(ndx, value.get_double()); + break; case COLUMN_TYPE_STRING: column.insert_string(ndx, value.get_string()); break; @@ -1322,7 +1422,7 @@ void Table::insert_done() #ifdef TIGHTDB_DEBUG Verify(); -#endif // TIGHTDB_DEBUG +#endif #ifdef TIGHTDB_ENABLE_REPLICATION error_code err = get_local_transact_log().row_insert_complete(); @@ -1330,15 +1430,24 @@ void Table::insert_done() #endif } + +// count ---------------------------------------------- + size_t Table::count_int(size_t column_ndx, int64_t target) const { - TIGHTDB_ASSERT(column_ndx < get_column_count()); - TIGHTDB_ASSERT(get_column_type(column_ndx) == COLUMN_TYPE_INT); - - const Column& column = GetColumn(column_ndx); + const Column& column = GetColumn(column_ndx); + return column.count(target); +} +size_t Table::count_float(size_t column_ndx, float target) const +{ + const ColumnFloat& column = GetColumn(column_ndx); + return column.count(target); +} +size_t Table::count_double(size_t column_ndx, double target) const +{ + const ColumnDouble& column = GetColumn(column_ndx); return column.count(target); } - size_t Table::count_string(size_t column_ndx, const char* value) const { TIGHTDB_ASSERT(column_ndx < get_column_count()); @@ -1357,51 +1466,110 @@ size_t Table::count_string(size_t column_ndx, const char* value) const } } +// sum ---------------------------------------------- + int64_t Table::sum(size_t column_ndx) const { - TIGHTDB_ASSERT(column_ndx < get_column_count()); - TIGHTDB_ASSERT(get_column_type(column_ndx) == COLUMN_TYPE_INT); - - const Column& column = GetColumn(column_ndx); + const Column& column = GetColumn(column_ndx); + return column.sum(); +} +float Table::sum_float(size_t column_ndx) const +{ + const ColumnFloat& column = GetColumn(column_ndx); + return column.sum(); +} +double Table::sum_double(size_t column_ndx) const +{ + const ColumnDouble& column = GetColumn(column_ndx); return column.sum(); } +// average ---------------------------------------------- + double Table::average(size_t column_ndx) const { - TIGHTDB_ASSERT(column_ndx < get_column_count()); - TIGHTDB_ASSERT(get_column_type(column_ndx) == COLUMN_TYPE_INT); - - const Column& column = GetColumn(column_ndx); + const Column& column = GetColumn(column_ndx); + return column.average(); +} +double Table::average_float(size_t column_ndx) const +{ + const ColumnFloat& column = GetColumn(column_ndx); + return column.average(); +} +double Table::average_double(size_t column_ndx) const +{ + const ColumnDouble& column = GetColumn(column_ndx); return column.average(); } -int64_t Table::maximum(size_t column_ndx) const +// minimum ---------------------------------------------- + +#define USE_COLUMN_AGGREGATE 1 + +int64_t Table::minimum(size_t column_ndx) const { - if (is_empty()) return 0; +#if USE_COLUMN_AGGREGATE + const Column& column = GetColumn(column_ndx); + return column.minimum(); +#else + if (is_empty()) + return 0; int64_t mv = get_int(column_ndx, 0); for (size_t i = 1; i < size(); ++i) { const int64_t v = get_int(column_ndx, i); - if (v > mv) { + if (v < mv) { mv = v; } } return mv; +#endif } -int64_t Table::minimum(size_t column_ndx) const +float Table::minimum_float(size_t column_ndx) const { - if (is_empty()) return 0; + const ColumnFloat& column = GetColumn(column_ndx); + return column.minimum(); +} +double Table::minimum_double(size_t column_ndx) const +{ + const ColumnDouble& column = GetColumn(column_ndx); + return column.minimum(); +} + +// maximum ---------------------------------------------- + +int64_t Table::maximum(size_t column_ndx) const +{ +#if USE_COLUMN_AGGREGATE + const Column& column = GetColumn(column_ndx); + return column.maximum(); +#else + if (is_empty()) + return 0; int64_t mv = get_int(column_ndx, 0); for (size_t i = 1; i < size(); ++i) { const int64_t v = get_int(column_ndx, i); - if (v < mv) { + if (v > mv) { mv = v; } } return mv; +#endif } +float Table::maximum_float(size_t column_ndx) const +{ + const ColumnFloat& column = GetColumn(column_ndx); + return column.maximum(); +} +double Table::maximum_double(size_t column_ndx) const +{ + const ColumnDouble& column = GetColumn(column_ndx); + return column.maximum(); +} + + size_t Table::lookup(const char* value) const { @@ -1462,6 +1630,24 @@ size_t Table::find_first_date(size_t column_ndx, time_t value) const return column.find_first((int64_t)value); } +size_t Table::find_first_float(size_t column_ndx, float value) const +{ + TIGHTDB_ASSERT(column_ndx < m_columns.Size()); + TIGHTDB_ASSERT(GetRealColumnType(column_ndx) == COLUMN_TYPE_FLOAT); + const ColumnFloat& column = GetColumnFloat(column_ndx); + + return column.find_first(value); +} + +size_t Table::find_first_double(size_t column_ndx, double value) const +{ + TIGHTDB_ASSERT(column_ndx < m_columns.Size()); + TIGHTDB_ASSERT(GetRealColumnType(column_ndx) == COLUMN_TYPE_DOUBLE); + const ColumnDouble& column = GetColumnDouble(column_ndx); + + return column.find_first(value); +} + size_t Table::find_first_string(size_t column_ndx, const char* value) const { TIGHTDB_ASSERT(column_ndx < m_columns.Size()); @@ -1538,6 +1724,51 @@ ConstTableView Table::find_all_bool(size_t column_ndx, bool value) const return move(tv); } + +TableView Table::find_all_float(size_t column_ndx, float value) +{ + TIGHTDB_ASSERT(column_ndx < m_columns.Size()); + + const ColumnFloat& column = GetColumnFloat(column_ndx); + + TableView tv(*this); + column.find_all(tv.get_ref_column(), value); + return move(tv); +} + +ConstTableView Table::find_all_float(size_t column_ndx, float value) const +{ + TIGHTDB_ASSERT(column_ndx < m_columns.Size()); + + const ColumnFloat& column = GetColumnFloat(column_ndx); + + ConstTableView tv(*this); + column.find_all(tv.get_ref_column(), value); + return move(tv); +} + +TableView Table::find_all_double(size_t column_ndx, double value) +{ + TIGHTDB_ASSERT(column_ndx < m_columns.Size()); + + const ColumnDouble& column = GetColumnDouble(column_ndx); + + TableView tv(*this); + column.find_all(tv.get_ref_column(), value); + return move(tv); +} + +ConstTableView Table::find_all_double(size_t column_ndx, double value) const +{ + TIGHTDB_ASSERT(column_ndx < m_columns.Size()); + + const ColumnDouble& column = GetColumnDouble(column_ndx); + + ConstTableView tv(*this); + column.find_all(tv.get_ref_column(), value); + return move(tv); +} + TableView Table::find_all_date(size_t column_ndx, time_t value) { TIGHTDB_ASSERT(column_ndx < m_columns.Size()); @@ -1619,8 +1850,6 @@ ConstTableView Table::find_all_binary(size_t column_ndx, const char* value, size } - - TableView Table::find_all_hamming(size_t column_ndx, uint64_t value, size_t max) { TIGHTDB_ASSERT(column_ndx < m_columns.Size()); @@ -1643,6 +1872,7 @@ ConstTableView Table::find_all_hamming(size_t column_ndx, uint64_t value, size_t return move(tv); } + TableView Table::distinct(size_t column_ndx) { TIGHTDB_ASSERT(column_ndx < m_columns.Size()); @@ -1992,6 +2222,12 @@ void Table::to_string_header(std::ostream& out, std::vector& widths) con case COLUMN_TYPE_INT: width = chars_in_int(maximum(col)); break; + case COLUMN_TYPE_FLOAT: + width = 12; // FIXME + break; + case COLUMN_TYPE_DOUBLE: + width = 12; // FIXME + break; case COLUMN_TYPE_TABLE: for (size_t row = 0; row < row_count; ++row) { size_t len = chars_in_int( get_subtable_size(col, row) ); @@ -2036,6 +2272,12 @@ void Table::to_string_header(std::ostream& out, std::vector& widths) con case COLUMN_TYPE_INT: width = max(width, chars_in_int(m.get_int())); break; + case COLUMN_TYPE_FLOAT: + width = max(width, (size_t)12); // FIXME + break; + case COLUMN_TYPE_DOUBLE: + width = max(width, (size_t)12); // FIXME + break; case COLUMN_TYPE_BINARY: width = max(width, chars_in_int(m.get_binary().len) + 6); break; @@ -2110,6 +2352,12 @@ void Table::to_string_row(size_t row_ndx, std::ostream& out, const std::vector void insert_enum(size_t column_ndx, size_t row_ndx, E value); + void insert_float(size_t column_ndx, size_t row_ndx, float value); + void insert_double(size_t column_ndx, size_t row_ndx, double value); void insert_string(size_t column_ndx, size_t row_ndx, const char* value); void insert_binary(size_t column_ndx, size_t row_ndx, const char* data, size_t size); void insert_subtable(size_t column_ndx, size_t row_ndx); // Insert empty table @@ -163,6 +165,8 @@ class Table { int64_t get_int(size_t column_ndx, size_t row_ndx) const TIGHTDB_NOEXCEPT; bool get_bool(size_t column_ndx, size_t row_ndx) const TIGHTDB_NOEXCEPT; time_t get_date(size_t column_ndx, size_t row_ndx) const TIGHTDB_NOEXCEPT; + float get_float(size_t column_ndx, size_t row_ndx) const; // FIXME: Should be modified so it never throws + double get_double(size_t column_ndx, size_t row_ndx) const; // FIXME: Should be modified so it never throws const char* get_string(size_t column_ndx, size_t row_ndx) const TIGHTDB_NOEXCEPT; size_t get_string_length(size_t column_ndx, size_t row_ndx) const TIGHTDB_NOEXCEPT; BinaryData get_binary(size_t column_ndx, size_t row_ndx) const; // FIXME: Should be modified so it never throws @@ -174,6 +178,8 @@ class Table { void set_bool(size_t column_ndx, size_t row_ndx, bool value); void set_date(size_t column_ndx, size_t row_ndx, time_t value); template void set_enum(size_t column_ndx, size_t row_ndx, E value); + void set_float(size_t column_ndx, size_t row_ndx, float value); + void set_double(size_t column_ndx, size_t row_ndx, double value); void set_string(size_t column_ndx, size_t row_ndx, const char* value); void set_binary(size_t column_ndx, size_t row_ndx, const char* value, size_t len); void set_mixed(size_t column_ndx, size_t row_ndx, Mixed value); @@ -195,16 +201,30 @@ class Table { // Aggregate functions size_t count_int(size_t column_ndx, int64_t target) const; size_t count_string(size_t column_ndx, const char* target) const; + size_t count_float(size_t column_ndx, float target) const; + size_t count_double(size_t column_ndx, double target) const; + int64_t sum(size_t column_ndx) const; - int64_t maximum(size_t column_ndx) const; // FIXME: When table is empty? - int64_t minimum(size_t column_ndx) const; // FIXME: When table is empty? - double average(size_t column_ndx) const; // FIXME: When table is empty? + float sum_float(size_t column_ndx) const; + double sum_double(size_t column_ndx) const; + // FIXME: What to return for below when table empty? 0? + int64_t maximum(size_t column_ndx) const; + float maximum_float(size_t column_ndx) const; + double maximum_double(size_t column_ndx) const; + int64_t minimum(size_t column_ndx) const; + float minimum_float(size_t column_ndx) const; + double minimum_double(size_t column_ndx) const; + double average(size_t column_ndx) const; + double average_float(size_t column_ndx) const; + double average_double(size_t column_ndx) const; // Searching size_t lookup(const char* value) const; size_t find_first_int(size_t column_ndx, int64_t value) const; size_t find_first_bool(size_t column_ndx, bool value) const; size_t find_first_date(size_t column_ndx, time_t value) const; + size_t find_first_float(size_t column_ndx, float value) const; + size_t find_first_double(size_t column_ndx, double value) const; size_t find_first_string(size_t column_ndx, const char* value) const; size_t find_first_binary(size_t column_ndx, const char* value, size_t len) const; @@ -214,6 +234,10 @@ class Table { ConstTableView find_all_bool(size_t column_ndx, bool value) const; TableView find_all_date(size_t column_ndx, time_t value); ConstTableView find_all_date(size_t column_ndx, time_t value) const; + TableView find_all_float(size_t column_ndx, float value); + ConstTableView find_all_float(size_t column_ndx, float value) const; + TableView find_all_double(size_t column_ndx, double value); + ConstTableView find_all_double(size_t column_ndx, double value) const; TableView find_all_string(size_t column_ndx, const char* value); ConstTableView find_all_string(size_t column_ndx, const char* value) const; TableView find_all_binary(size_t column_ndx, const char* value, size_t len); @@ -271,8 +295,14 @@ class Table { // FIXME: Most of the things that are protected here, could instead be private // Direct Column access + template T& GetColumn(size_t ndx); + template const T& GetColumn(size_t ndx) const TIGHTDB_NOEXCEPT; Column& GetColumn(size_t column_ndx); const Column& GetColumn(size_t column_ndx) const TIGHTDB_NOEXCEPT; + ColumnFloat& GetColumnFloat(size_t column_ndx); + const ColumnFloat& GetColumnFloat(size_t column_ndx) const TIGHTDB_NOEXCEPT; + ColumnDouble& GetColumnDouble(size_t column_ndx); + const ColumnDouble& GetColumnDouble(size_t column_ndx) const TIGHTDB_NOEXCEPT; AdaptiveStringColumn& GetColumnString(size_t column_ndx); const AdaptiveStringColumn& GetColumnString(size_t column_ndx) const TIGHTDB_NOEXCEPT; ColumnBinary& GetColumnBinary(size_t column_ndx); @@ -284,7 +314,6 @@ class Table { ColumnMixed& GetColumnMixed(size_t column_ndx); const ColumnMixed& GetColumnMixed(size_t column_ndx) const TIGHTDB_NOEXCEPT; - /// Used when the lifetime of a table is managed by reference /// counting. The lifetime of free-standing tables allocated on /// the stack by the application is not managed by reference @@ -390,6 +419,7 @@ class Table { ColumnBase& GetColumnBase(size_t column_ndx); void InstantiateBeforeChange(); + void validate_column_type(const ColumnBase& column, ColumnType expected_type, size_t ndx) const; /// Construct an empty table with independent spec and return just /// the reference to the underlying memory. @@ -443,6 +473,27 @@ class Table::Parent: public ArrayParent { // Implementation: +template +C& Table::GetColumn(size_t ndx) +{ + ColumnBase& column = GetColumnBase(ndx); +#ifdef TIGHTDB_DEBUG + validate_column_type(column, coltype, ndx); +#endif + return static_cast(column); +} + +template +const C& Table::GetColumn(size_t ndx) const TIGHTDB_NOEXCEPT +{ + const ColumnBase& column = GetColumnBase(ndx); +#ifdef TIGHTDB_DEBUG + validate_column_type(column, coltype, ndx); +#endif + return static_cast(column); +} + + inline bool Table::has_shared_spec() const { const Array& top_array = m_top.IsValid() ? m_top : m_columns; diff --git a/src/tightdb/table_accessors.hpp b/src/tightdb/table_accessors.hpp index d8f5c053301..3584024cb2a 100644 --- a/src/tightdb/table_accessors.hpp +++ b/src/tightdb/table_accessors.hpp @@ -41,6 +41,8 @@ struct SpecBase { typedef int64_t Int; typedef bool Bool; typedef tightdb::Date Date; + typedef float Float; + typedef double Double; typedef const char* String; typedef tightdb::BinaryData Binary; typedef tightdb::Mixed Mixed; @@ -250,6 +252,88 @@ class FieldAccessor: public FieldAccessorBa }; +/// Field accessor specialization for floats. +template +class FieldAccessor: public FieldAccessorBase { +private: + typedef FieldAccessorBase Base; + +public: + float get() const + { + return Base::m_table->get_impl()->get_float(col_idx, Base::m_row_idx); + } + + void set(float value) const + { + Base::m_table->get_impl()->set_float(col_idx, Base::m_row_idx, value); + } + + operator float() const { return get(); } + const FieldAccessor& operator=(float value) const { set(value); return *this; } + + const FieldAccessor& operator+=(float value) const + { + // FIXME: Should be optimized (can be both optimized and + // generalized by using a form of expression templates). + set(get() + value); + return *this; + } + + const FieldAccessor& operator-=(float value) const + { + // FIXME: Should be optimized (can be both optimized and + // generalized by using a form of expression templates). + set(get() - value); + return *this; + } + + + explicit FieldAccessor(typename Base::Init i) TIGHTDB_NOEXCEPT: Base(i) {} +}; + + +/// Field accessor specialization for doubles. +template +class FieldAccessor: public FieldAccessorBase { +private: + typedef FieldAccessorBase Base; + +public: + double get() const + { + return Base::m_table->get_impl()->get_double(col_idx, Base::m_row_idx); + } + + void set(double value) const + { + Base::m_table->get_impl()->set_double(col_idx, Base::m_row_idx, value); + } + + operator double() const { return get(); } + const FieldAccessor& operator=(double value) const { set(value); return *this; } + + const FieldAccessor& operator+=(double value) const + { + // FIXME: Should be optimized (can be both optimized and + // generalized by using a form of expression templates). + set(get() + value); + return *this; + } + + const FieldAccessor& operator-=(double value) const + { + // FIXME: Should be optimized (can be both optimized and + // generalized by using a form of expression templates). + set(get() - value); + return *this; + } + + + explicit FieldAccessor(typename Base::Init i) TIGHTDB_NOEXCEPT: Base(i) {} +}; + + /// Field accessor specialization for booleans. template class FieldAccessor: public FieldAccessorBase { @@ -551,6 +635,10 @@ class MixedFieldAccessorBase: public FieldAccessorBase { std::time_t get_date() const { return get().get_date(); } // FIXME: Should be modified so it never throws + float get_float() const { return get().get_float(); } // FIXME: Should be modified so it never throws + + double get_double() const { return get().get_double(); } // FIXME: Should be modified so it never throws + const char* get_string() const { return get().get_string(); } // FIXME: Should be modified so it never throws BinaryData get_binary() const { return get().get_binary(); } // FIXME: Should be modified so it never throws @@ -788,6 +876,11 @@ class ColumnAccessor: return Base::m_table->get_impl()->minimum(col_idx); } + double average() const + { + return Base::m_table->get_impl()->average(col_idx); + } + const ColumnAccessor& operator+=(int64_t value) const { Base::m_table->get_impl()->add_int(col_idx, value); @@ -796,6 +889,112 @@ class ColumnAccessor: }; +/// Column accessor specialization for float +template +class ColumnAccessor: + public ColumnAccessorBase { +private: + typedef ColumnAccessorBase Base; + +public: + explicit ColumnAccessor(Taboid* t) TIGHTDB_NOEXCEPT: Base(t) {} + + std::size_t find_first(float value) const + { + return Base::m_table->get_impl()->find_first_float(col_idx, value); + } + + BasicTableView find_all(float value) const + { + return Base::m_table->get_impl()->find_all_float(col_idx, value); + } + + size_t count(float target) const + { + return Base::m_table->get_impl()->count_float(col_idx, target); + } + + double sum() const + { + return Base::m_table->get_impl()->sum_float(col_idx); + } + + float maximum() const + { + return Base::m_table->get_impl()->maximum_float(col_idx); + } + + float minimum() const + { + return Base::m_table->get_impl()->minimum_float(col_idx); + } + + double average() const + { + return Base::m_table->get_impl()->average_float(col_idx); + } + + const ColumnAccessor& operator+=(float value) const + { + Base::m_table->get_impl()->add_float(col_idx, value); + return *this; + } +}; + + +/// Column accessor specialization for double +template +class ColumnAccessor: + public ColumnAccessorBase { +private: + typedef ColumnAccessorBase Base; + +public: + explicit ColumnAccessor(Taboid* t) TIGHTDB_NOEXCEPT: Base(t) {} + + std::size_t find_first(double value) const + { + return Base::m_table->get_impl()->find_first_double(col_idx, value); + } + + BasicTableView find_all(double value) const + { + return Base::m_table->get_impl()->find_all_double(col_idx, value); + } + + size_t count(double target) const + { + return Base::m_table->get_impl()->count_double(col_idx, target); + } + + double sum() const + { + return Base::m_table->get_impl()->sum_double(col_idx); + } + + double maximum() const + { + return Base::m_table->get_impl()->maximum_double(col_idx); + } + + double minimum() const + { + return Base::m_table->get_impl()->minimum_double(col_idx); + } + + double average() const + { + return Base::m_table->get_impl()->average_double(col_idx); + } + + const ColumnAccessor& operator+=(double value) const + { + Base::m_table->get_impl()->add_double(col_idx, value); + return *this; + } +}; + + /// Column accessor specialization for booleans. template class ColumnAccessor: public ColumnAccessorBase { @@ -938,12 +1137,13 @@ class ColumnAccessor: public ColumnAccessorBase' or /// 'BasicTableView'. Neither may be const-qualified. /// + /// FIXME: These do not belong in this file! template class QueryColumn; @@ -1037,6 +1237,145 @@ class QueryColumn: public QueryColumnBase +class QueryColumn: public QueryColumnBase { +private: + typedef QueryColumnBase Base; + typedef typename Taboid::Query Query; + +public: + explicit QueryColumn(Query* q) TIGHTDB_NOEXCEPT: Base(q) {} + using Base::equal; + using Base::not_equal; + + Query& greater(float value) const + { + Base::m_query->m_impl.greater(col_idx, value); + return *Base::m_query; + } + + Query& greater_equal(float value) const + { + Base::m_query->m_impl.greater_equal(col_idx, value); + return *Base::m_query; + } + + Query& less(float value) const + { + Base::m_query->m_impl.less(col_idx, value); + return *Base::m_query; + } + + Query& less_equal(float value) const + { + Base::m_query->m_impl.less_equal(col_idx, value); + return *Base::m_query; + } + + Query& between(float from, float to) const + { + Base::m_query->m_impl.between(col_idx, from, to); + return *Base::m_query; + }; + + float sum(std::size_t* resultcount=NULL, std::size_t start=0, + std::size_t end = std::size_t(-1), std::size_t limit=std::size_t(-1)) const + { + return Base::m_query->m_impl.sum_float(col_idx, resultcount, start, end, limit); + } + + float maximum(std::size_t* resultcount=NULL, std::size_t start=0, + std::size_t end = std::size_t(-1), std::size_t limit=std::size_t(-1)) const + { + return Base::m_query->m_impl.maximum_float(col_idx, resultcount, start, end, limit); + } + + float minimum(std::size_t* resultcount=NULL, std::size_t start=0, + std::size_t end = std::size_t(-1), std::size_t limit=std::size_t(-1)) const + { + return Base::m_query->m_impl.minimum_float(col_idx, resultcount, start, end, limit); + } + + double average(std::size_t* resultcount=NULL, std::size_t start=0, + std::size_t end=std::size_t(-1), std::size_t limit=std::size_t(-1)) const + { + return Base::m_query->m_impl.average_float(col_idx, resultcount, start, end, limit); + } +}; + + + +/// QueryColumn specialization for doubles. +template +class QueryColumn: public QueryColumnBase { +private: + typedef QueryColumnBase Base; + typedef typename Taboid::Query Query; + +public: + explicit QueryColumn(Query* q) TIGHTDB_NOEXCEPT: Base(q) {} + using Base::equal; + using Base::not_equal; + + Query& greater(double value) const + { + Base::m_query->m_impl.greater(col_idx, value); + return *Base::m_query; + } + + Query& greater_equal(double value) const + { + Base::m_query->m_impl.greater_equal(col_idx, value); + return *Base::m_query; + } + + Query& less(double value) const + { + Base::m_query->m_impl.less(col_idx, value); + return *Base::m_query; + } + + Query& less_equal(double value) const + { + Base::m_query->m_impl.less_equal(col_idx, value); + return *Base::m_query; + } + + Query& between(double from, double to) const + { + Base::m_query->m_impl.between(col_idx, from, to); + return *Base::m_query; + }; + + double sum(std::size_t* resultcount=NULL, std::size_t start=0, + std::size_t end = std::size_t(-1), std::size_t limit=std::size_t(-1)) const + { + return Base::m_query->m_impl.sum_double(col_idx, resultcount, start, end, limit); + } + + double maximum(std::size_t* resultcount=NULL, std::size_t start=0, + std::size_t end = std::size_t(-1), std::size_t limit=std::size_t(-1)) const + { + return Base::m_query->m_impl.maximum_double(col_idx, resultcount, start, end, limit); + } + + double minimum(std::size_t* resultcount=NULL, std::size_t start=0, + std::size_t end = std::size_t(-1), std::size_t limit=std::size_t(-1)) const + { + return Base::m_query->m_impl.minimum_double(col_idx, resultcount, start, end, limit); + } + + double average(std::size_t* resultcount=NULL, std::size_t start=0, + std::size_t end=std::size_t(-1), std::size_t limit=std::size_t(-1)) const + { + return Base::m_query->m_impl.average_double(col_idx, resultcount, start, end, limit); + } +}; + + + /// QueryColumn specialization for booleans. template class QueryColumn: public QueryColumnBase { diff --git a/src/tightdb/table_basic.hpp b/src/tightdb/table_basic.hpp index 4d2e7a46477..c35773c83bb 100644 --- a/src/tightdb/table_basic.hpp +++ b/src/tightdb/table_basic.hpp @@ -318,6 +318,15 @@ template class BasicTable::Query: Query& tableview(const Array& arr) { m_impl.tableview(arr); return *this; } +// Query& Query::tableview(const TableView& tv) +// Query& Query::tableview(const Array &arr) + + Query& tableview(const typename BasicTable::View& v) { + m_impl.tableview(*v.get_impl()); + return *this; + } + + Query& group() { m_impl.group(); return *this; } Query& end_group() { m_impl.end_group(); return *this; } @@ -383,12 +392,19 @@ template class BasicTable::Query: namespace _impl { template struct GetColumnTypeId; + template<> struct GetColumnTypeId { static const ColumnType id = COLUMN_TYPE_INT; }; template<> struct GetColumnTypeId { static const ColumnType id = COLUMN_TYPE_BOOL; }; + template<> struct GetColumnTypeId { + static const ColumnType id = COLUMN_TYPE_FLOAT; + }; + template<> struct GetColumnTypeId { + static const ColumnType id = COLUMN_TYPE_DOUBLE; + }; template<> struct GetColumnTypeId { static const ColumnType id = COLUMN_TYPE_STRING; }; @@ -457,6 +473,22 @@ namespace _impl } }; + // InsertIntoCol specialization for float + template struct InsertIntoCol { + template static void exec(Table* t, std::size_t row_idx, Tuple tuple) + { + t->insert_float(col_idx, row_idx, at(tuple)); + } + }; + + // InsertIntoCol specialization for double + template struct InsertIntoCol { + template static void exec(Table* t, std::size_t row_idx, Tuple tuple) + { + t->insert_double(col_idx, row_idx, at(tuple)); + } + }; + // InsertIntoCol specialization for booleans template struct InsertIntoCol { template static void exec(Table* t, std::size_t row_idx, Tuple tuple) @@ -527,6 +559,22 @@ namespace _impl } }; + // AssignIntoCol specialization for floats + template struct AssignIntoCol { + template static void exec(Table* t, std::size_t row_idx, Tuple tuple) + { + t->set_float(col_idx, row_idx, at(tuple)); + } + }; + + // AssignIntoCol specialization for doubles + template struct AssignIntoCol { + template static void exec(Table* t, std::size_t row_idx, Tuple tuple) + { + t->set_double(col_idx, row_idx, at(tuple)); + } + }; + // AssignIntoCol specialization for booleans template struct AssignIntoCol { template static void exec(Table* t, std::size_t row_idx, Tuple tuple) diff --git a/src/tightdb/table_view.cpp b/src/tightdb/table_view.cpp index 4fcdb3bcbe8..5c6872b3b0b 100644 --- a/src/tightdb/table_view.cpp +++ b/src/tightdb/table_view.cpp @@ -1,5 +1,6 @@ #include #include +#include namespace tightdb { @@ -15,7 +16,6 @@ size_t TableViewBase::find_first_integer(size_t column_ndx, int64_t value) const return size_t(-1); } - size_t TableViewBase::find_first_string(size_t column_ndx, const char* value) const { TIGHTDB_ASSERT_COLUMN_AND_TYPE(column_ndx, COLUMN_TYPE_STRING); @@ -26,48 +26,68 @@ size_t TableViewBase::find_first_string(size_t column_ndx, const char* value) co return size_t(-1); } +size_t TableViewBase::find_first_float(size_t column_ndx, float value) const +{ + for (size_t i = 0; i < m_refs.Size(); i++) + if (get_float(column_ndx, i) == value) + return i; + return size_t(-1); +} + +size_t TableViewBase::find_first_double(size_t column_ndx, double value) const +{ + for (size_t i = 0; i < m_refs.Size(); i++) + if (get_double(column_ndx, i) == value) + return i; + return size_t(-1); +} + + -template int64_t TableViewBase::aggregate(size_t column_ndx) const +template +R TableViewBase::aggregate(size_t column_ndx) const { - TIGHTDB_ASSERT_COLUMN_AND_TYPE(column_ndx, COLUMN_TYPE_INT); + TIGHTDB_ASSERT_COLUMN_AND_TYPE(column_ndx, ColumnTypeTraits::id); TIGHTDB_ASSERT(function == TDB_SUM || function == TDB_MAX || function == TDB_MIN); TIGHTDB_ASSERT(m_table); TIGHTDB_ASSERT(column_ndx < m_table->get_column_count()); if (m_refs.Size() == 0) return 0; - int64_t res = 0; - Column& m_column = m_table->GetColumn(column_ndx); + typedef typename ColumnTypeTraits::column_type ColType; + typedef typename ColumnTypeTraits::array_type ArrType; - if (m_refs.Size() == m_column.Size()) { + const ColType* column = (ColType*)&m_table->GetColumnBase(column_ndx); + + if (m_refs.Size() == column->Size()) { if (function == TDB_MAX) - return m_column.maximum(); + return column->maximum(); if (function == TDB_MIN) - return m_column.minimum(); + return column->minimum(); if (function == TDB_SUM) - return m_column.sum(); + return column->sum(); } - // Array object instantiation must NOT allocate initial memory (capacity) with 'new' because it will lead to mem leak. - // The m_column keeps ownership of the payload in m_array and will free it itself later, so we must not call Destroy() on m_array. - // Todo, create tag constructor for array instead of using 'false'. - Array m_array(false); + // Array object instantiation must NOT allocate initial memory (capacity) + // with 'new' because it will lead to mem leak. The column keeps ownership + // of the payload in array and will free it itself later, so we must not call Destroy() on array. + ArrType arr((Array::no_prealloc_tag())); - size_t m_leaf_start = 0; - size_t m_leaf_end = 0; - size_t s; + size_t leaf_start = 0; + size_t leaf_end = 0; + size_t row_ndx; - res = get_int(column_ndx, 0); + T res = column->template TreeGet(0); for (size_t ss = 1; ss < m_refs.Size(); ++ss) { - s = m_refs.GetAsSizeT(ss); - if (s >= m_leaf_end) { - m_column.GetBlock(s, m_array, m_leaf_start); - const size_t leaf_size = m_array.Size(); - m_leaf_end = m_leaf_start + leaf_size; + row_ndx = m_refs.GetAsSizeT(ss); + if (row_ndx >= leaf_end) { + ((Column*)column)->GetBlock(row_ndx, arr, leaf_start); + const size_t leaf_size = arr.Size(); + leaf_end = leaf_start + leaf_size; } - int64_t v = m_array.Get(s - m_leaf_start); + T v = arr.Get(row_ndx - leaf_start); if (function == TDB_SUM) res += v; @@ -80,18 +100,46 @@ template int64_t TableViewBase::aggregate(size_t column_ndx) const int64_t TableViewBase::sum(size_t column_ndx) const { - return aggregate(column_ndx); + return aggregate(column_ndx); } +double TableViewBase::sum_float(size_t column_ndx) const +{ + return aggregate(column_ndx); +} +double TableViewBase::sum_double(size_t column_ndx) const +{ + return aggregate(column_ndx); +} + int64_t TableViewBase::maximum(size_t column_ndx) const { - return aggregate(column_ndx); + return aggregate(column_ndx); +} +float TableViewBase::maximum_float(size_t column_ndx) const +{ + return aggregate(column_ndx); +} +double TableViewBase::maximum_double(size_t column_ndx) const +{ + return aggregate(column_ndx); } + int64_t TableViewBase::minimum(size_t column_ndx) const { - return aggregate(column_ndx); + return aggregate(column_ndx); +} +float TableViewBase::minimum_float(size_t column_ndx) const +{ + return aggregate(column_ndx); } +double TableViewBase::minimum_double(size_t column_ndx) const +{ + return aggregate(column_ndx); +} + + void TableViewBase::sort(size_t column, bool Ascending) { diff --git a/src/tightdb/table_view.hpp b/src/tightdb/table_view.hpp index 97b9db4e15a..5180658f6c1 100644 --- a/src/tightdb/table_view.hpp +++ b/src/tightdb/table_view.hpp @@ -49,6 +49,8 @@ class TableViewBase { int64_t get_int(size_t column_ndx, size_t row_ndx) const TIGHTDB_NOEXCEPT; bool get_bool(size_t column_ndx, size_t row_ndx) const TIGHTDB_NOEXCEPT; time_t get_date(size_t column_ndx, size_t row_ndx) const TIGHTDB_NOEXCEPT; + float get_float(size_t column_ndx, size_t row_ndx) const; // FIXME: Should be modified so it never throws + double get_double(size_t column_ndx, size_t row_ndx) const; // FIXME: Should be modified so it never throws const char* get_string(size_t column_ndx, size_t row_ndx) const TIGHTDB_NOEXCEPT; BinaryData get_binary(size_t column_ndx, size_t row_ndx) const; // FIXME: Should be modified so it never throws Mixed get_mixed(size_t column_ndx, size_t row_ndx) const; // FIXME: Should be modified so it never throws @@ -61,15 +63,28 @@ class TableViewBase { size_t find_first_int(size_t column_ndx, int64_t value) const; size_t find_first_bool(size_t column_ndx, bool value) const; size_t find_first_date(size_t column_ndx, time_t value) const; + size_t find_first_float(size_t column_ndx, float value) const; + size_t find_first_double(size_t column_ndx, double value) const; size_t find_first_string(size_t column_ndx, const char* value) const; // FIXME: Need: size_t find_first_binary(size_t column_ndx, const char* value, size_t len) const; // Aggregate functions - template int64_t aggregate(size_t column_ndx) const; + template R aggregate(size_t column_ndx) const; + + // TODO, FIXME: rename int versions + // TODO: Add maximum, minimum for date int64_t sum(size_t column_ndx) const; int64_t maximum(size_t column_ndx) const; int64_t minimum(size_t column_ndx) const; + double sum_float(size_t column_ndx) const; + float maximum_float(size_t column_ndx) const; + float minimum_float(size_t column_ndx) const; + + double sum_double(size_t column_ndx) const; + double maximum_double(size_t column_ndx) const; + double minimum_double(size_t column_ndx) const; + // Sort the view according to the specified column and the specified direction. void sort(size_t column, bool ascending = true); @@ -88,6 +103,8 @@ class TableViewBase { friend class Query; template static R find_all_integer(V*, size_t, int64_t); + template static R find_all_float(V*, size_t, float); + template static R find_all_double(V*, size_t, double); template static R find_all_string(V*, size_t, const char*); Table* m_table; @@ -179,6 +196,8 @@ class TableView: public TableViewBase { void set_bool(size_t column_ndx, size_t row_ndx, bool value); void set_date(size_t column_ndx, size_t row_ndx, time_t value); template void set_enum(size_t column_ndx, size_t row_ndx, E value); + void set_float(size_t column_ndx, size_t row_ndx, float value); + void set_double(size_t column_ndx, size_t row_ndx, double value); void set_string(size_t column_ndx, size_t row_ndx, const char* value); void set_binary(size_t column_ndx, size_t row_ndx, const char* value, size_t len); void set_mixed(size_t column_ndx, size_t row_ndx, Mixed value); @@ -196,6 +215,10 @@ class TableView: public TableViewBase { ConstTableView find_all_bool(size_t column_ndx, bool value) const; TableView find_all_date(size_t column_ndx, time_t value); ConstTableView find_all_date(size_t column_ndx, time_t value) const; + TableView find_all_float(size_t column_ndx, float value); + ConstTableView find_all_float(size_t column_ndx, float value) const; + TableView find_all_double(size_t column_ndx, double value); + ConstTableView find_all_double(size_t column_ndx, double value) const; TableView find_all_string(size_t column_ndx, const char *value); ConstTableView find_all_string(size_t column_ndx, const char *value) const; // FIXME: Need: TableView find_all_binary(size_t column_ndx, const char* value, size_t len); @@ -248,6 +271,8 @@ class ConstTableView: public TableViewBase { ConstTableView find_all_int(size_t column_ndx, int64_t value) const; ConstTableView find_all_bool(size_t column_ndx, bool value) const; ConstTableView find_all_date(size_t column_ndx, time_t value) const; + ConstTableView find_all_float(size_t column_ndx, float value) const; + ConstTableView find_all_double(size_t column_ndx, double value) const; ConstTableView find_all_string(size_t column_ndx, const char *value) const; const Table& get_parent() const TIGHTDB_NOEXCEPT { return *m_table; } @@ -355,6 +380,22 @@ inline time_t TableViewBase::get_date(size_t column_ndx, size_t row_ndx) const T return m_table->get_date(column_ndx, real_ndx); } +inline float TableViewBase::get_float(size_t column_ndx, size_t row_ndx) const +{ + TIGHTDB_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, COLUMN_TYPE_FLOAT); + + const size_t real_ndx = size_t(m_refs.Get(row_ndx)); + return m_table->get_float(column_ndx, real_ndx); +} + +inline double TableViewBase::get_double(size_t column_ndx, size_t row_ndx) const +{ + TIGHTDB_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, COLUMN_TYPE_DOUBLE); + + const size_t real_ndx = size_t(m_refs.Get(row_ndx)); + return m_table->get_double(column_ndx, real_ndx); +} + inline const char* TableViewBase::get_string(size_t column_ndx, size_t row_ndx) const TIGHTDB_NOEXCEPT { TIGHTDB_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, COLUMN_TYPE_STRING); @@ -428,6 +469,26 @@ R TableViewBase::find_all_integer(V* view, size_t column_ndx, int64_t value) return move(tv); } +template +R TableViewBase::find_all_float(V* view, size_t column_ndx, float value) +{ + R tv(*view->m_table); + for (size_t i = 0; i < view->m_refs.Size(); i++) + if (view->get_float(column_ndx, i) == value) + tv.get_ref_column().add(i); + return move(tv); +} + +template +R TableViewBase::find_all_double(V* view, size_t column_ndx, double value) +{ + R tv(*view->m_table); + for (size_t i = 0; i < view->m_refs.Size(); i++) + if (view->get_double(column_ndx, i) == value) + tv.get_ref_column().add(i); + return move(tv); +} + template R TableViewBase::find_all_string(V* view, size_t column_ndx, const char* value) { @@ -443,9 +504,9 @@ R TableViewBase::find_all_string(V* view, size_t column_ndx, const char* value) } -// TableView, ConstTableView implementation: - +//-------------------------- TableView, ConstTableView implementation: +// - string inline TableView TableView::find_all_string(size_t column_ndx, const char* value) { return TableViewBase::find_all_string(this, column_ndx, value); @@ -461,6 +522,42 @@ inline ConstTableView ConstTableView::find_all_string(size_t column_ndx, const c return TableViewBase::find_all_string(this, column_ndx, value); } +// - float +inline TableView TableView::find_all_float(size_t column_ndx, float value) +{ + return TableViewBase::find_all_float(this, column_ndx, value); +} + +inline ConstTableView TableView::find_all_float(size_t column_ndx, float value) const +{ + return TableViewBase::find_all_float(this, column_ndx, value); +} + +inline ConstTableView ConstTableView::find_all_float(size_t column_ndx, float value) const +{ + return TableViewBase::find_all_float(this, column_ndx, value); +} + + +// - double +inline TableView TableView::find_all_double(size_t column_ndx, double value) +{ + return TableViewBase::find_all_double(this, column_ndx, value); +} + +inline ConstTableView TableView::find_all_double(size_t column_ndx, double value) const +{ + return TableViewBase::find_all_double(this, column_ndx, value); +} + +inline ConstTableView ConstTableView::find_all_double(size_t column_ndx, double value) const +{ + return TableViewBase::find_all_double(this, column_ndx, value); +} + + + +// -- 3 variants of the 3 find_all_{int, bool, date} all based on integer inline TableView TableView::find_all_integer(size_t column_ndx, int64_t value) { @@ -598,6 +695,22 @@ inline void TableView::set_date(size_t column_ndx, size_t row_ndx, time_t value) m_table->set_date(column_ndx, real_ndx, value); } +inline void TableView::set_float(size_t column_ndx, size_t row_ndx, float value) +{ + TIGHTDB_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, COLUMN_TYPE_FLOAT); + + const size_t real_ndx = size_t(m_refs.Get(row_ndx)); + m_table->set_float(column_ndx, real_ndx, value); +} + +inline void TableView::set_double(size_t column_ndx, size_t row_ndx, double value) +{ + TIGHTDB_ASSERT_INDEX_AND_TYPE(column_ndx, row_ndx, COLUMN_TYPE_DOUBLE); + + const size_t real_ndx = size_t(m_refs.Get(row_ndx)); + m_table->set_double(column_ndx, real_ndx, value); +} + template inline void TableView::set_enum(size_t column_ndx, size_t row_ndx, E value) { const size_t real_ndx = size_t(m_refs.Get(row_ndx)); diff --git a/src/tightdb/table_view_basic.hpp b/src/tightdb/table_view_basic.hpp index de5d0aff01d..4f0b6fd60e3 100644 --- a/src/tightdb/table_view_basic.hpp +++ b/src/tightdb/table_view_basic.hpp @@ -30,6 +30,7 @@ namespace tightdb { /// Common base class for BasicTableView and BasicTableView. /// +/// \tparam Impl Is either TableView or ConstTableView. template class BasicTableViewBase { public: typedef typename Tab::spec_type spec_type; diff --git a/src/tightdb/tightdb_nmmintrin - Kopi.h b/src/tightdb/tightdb_nmmintrin - Kopi.h new file mode 100644 index 00000000000..f409a0c4bd0 --- /dev/null +++ b/src/tightdb/tightdb_nmmintrin - Kopi.h @@ -0,0 +1,78 @@ +#ifndef TIGHTDB_NMMINTRIN_H +#define TIGHTDB_NMMINTRIN_H + +/* + We must support runtime detection of CPU support of SSE when distributing TightDB as a closed source library. + + This is a problem on gcc and llvm: To use SSE intrinsics we need to pass -msse on the command line (to get offered + __builtin_ accessors used by intrinsics functions). However, the -msse flag allows gcc to emit SSE instructions + in its code generation/optimization. This is unwanted because the binary would crash on non-SSE CPUs. + + Since there exists no flag in gcc that enables intrinsics but probits SSE in code generation, we define our + own intrinsics to be assembled by the back end assembler and omit passing -msse to gcc. +*/ + +#ifndef _MSC_VER + +#ifdef TIGHTDB_COMPILER_SSE + #include // SSE2 (using __m128i) +#endif + +// Instructions introduced by SSE 3 and 4.2 +static inline __m128i _mm_cmpgt_epi64(__m128i xmm1, __m128i xmm2) +{ + __asm__("pcmpgtq %1, %0" : "+x" (xmm1) : "xm" (xmm2)); + return xmm1; +} + +static inline __m128i _mm_cmpeq_epi64(__m128i xmm1, __m128i xmm2) +{ + __asm__("pcmpeqq %1, %0" : "+x" (xmm1) : "xm" (xmm2)); + return xmm1; +} + +static inline __m128i __attribute__((always_inline)) _mm_min_epi8(__m128i xmm1, __m128i xmm2) +{ + __asm__("pminsb %1, %0" : "+x" (xmm1) : "xm" (xmm2)); + return xmm1; +} + +static inline __m128i __attribute__((always_inline)) _mm_max_epi8(__m128i xmm1, __m128i xmm2) +{ + __asm__("pmaxsb %1, %0" : "+x" (xmm1) : "xm" (xmm2)); + return xmm1; +} + +static inline __m128i __attribute__((always_inline)) _mm_max_epi32(__m128i xmm1, __m128i xmm2) +{ + __asm__("pmaxsd %1, %0" : "+x" (xmm1) : "xm" (xmm2)); + return xmm1; +} + +static inline __m128i __attribute__((always_inline)) _mm_min_epi32(__m128i xmm1, __m128i xmm2) +{ + __asm__("pminsd %1, %0" : "+x" (xmm1) : "xm" (xmm2)); + return xmm1; +} + +static inline __m128i __attribute__((always_inline)) _mm_cvtepi8_epi16(__m128i xmm2) +{ + __m128i xmm1; + __asm__("pmovsxbw %1, %0" : "=x" (xmm1) : "xm" (xmm2) : "xmm1"); + return xmm1; +} +static inline __m128i __attribute__((always_inline)) _mm_cvtepi16_epi32(__m128i xmm2) +{ + __m128i xmm1; + asm("pmovsxwd %1, %0" : "=x" (xmm1) : "xm" (xmm2)); + return xmm1; +} + +static inline __m128i __attribute__((always_inline)) _mm_cvtepi32_epi64(__m128i xmm2) +{ + __m128i xmm1; + __asm__("pmovsxdq %1, %0" : "=x" (xmm1) : "xm" (xmm2)); + return xmm1; +} +#endif +#endif diff --git a/src/tightdb/utilities.cpp b/src/tightdb/utilities.cpp index e00e3e0bee3..695b6d0e44d 100644 --- a/src/tightdb/utilities.cpp +++ b/src/tightdb/utilities.cpp @@ -34,9 +34,9 @@ void cpuid_init() #endif // Byte is atomic. Race can/will occur but that's fine - if(cret & 0x100000) // test for 4.2 + if (cret & 0x100000) // test for 4.2 sse_support = 1; - else if(cret & 0x1) // Test for 3 + else if (cret & 0x1) // Test for 3 sse_support = 0; else sse_support = -2; @@ -139,4 +139,51 @@ void checksum_rolling(unsigned char* data, size_t len, checksum_t* t) return; } +// popcount +#if defined(_MSC_VER) && _MSC_VER >= 1500 + #include + int fast_popcount32(int32_t x) + { + return __popcnt(x); + } + #if defined(_M_X64) + int fast_popcount64(int64_t x) + { + return (int)__popcnt64(x); + } + #else + int fast_popcount64(int64_t x) + { + return __popcnt((unsigned)(x)) + __popcnt((unsigned)(x >> 32)); + } + #endif +#elif defined(__GNUC__) && __GNUC__ >= 4 || defined(__INTEL_COMPILER) && __INTEL_COMPILER >= 900 + #define fast_popcount32 __builtin_popcount + #if ULONG_MAX == 0xffffffff + int fast_popcount64(int64_t x) + { + return __builtin_popcount((unsigned)(x)) + __builtin_popcount((unsigned)(x >> 32)); + } + #else + int fast_popcount64(int64_t x) + { + return __builtin_popcountll(x); + } + #endif +#else + static const char a_popcount_bits[256] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, + }; + + // Masking away bits might be faster than bit shifting (which can be slow). Note that the compiler may optimize this automatically. Todo, investigate. + inline int fast_popcount32(uint32_t x) + { + return a_popcount_bits[255 & x] + a_popcount_bits[255 & x>> 8] + a_popcount_bits[255 & x>>16] + a_popcount_bits[255 & x>>24]; + } + inline int fast_popcount64(uint64_t x) + { + return fast_popcount32(x) + fast_popcount32(x >> 32); + } + +#endif // select best popcount implementations } diff --git a/src/tightdb/utilities.hpp b/src/tightdb/utilities.hpp index d70502fd8c1..834abbfad28 100644 --- a/src/tightdb/utilities.hpp +++ b/src/tightdb/utilities.hpp @@ -22,6 +22,7 @@ #include #include +#include // size_t #ifdef _MSC_VER # include @@ -93,9 +94,9 @@ template TIGHTDB_FORCEINLINE bool cpuid_sse() */ TIGHTDB_STATIC_ASSERT(version == 30 || version == 42, "Only SSE 3 and 42 supported for detection"); #ifdef TIGHTDB_COMPILER_SSE - if(version == 30) + if (version == 30) return (sse_support >= 0); - else if(version == 42) + else if (version == 42) return (sse_support > 0); // faster than == 1 (0 requres no immediate operand) #else return false; @@ -121,6 +122,9 @@ size_t round_up(size_t p, size_t align); size_t round_down(size_t p, size_t align); void checksum_init(checksum_t* t); +// popcount +int fast_popcount32(int32_t x); +int fast_popcount64(int64_t x); @@ -152,6 +156,19 @@ inline std::size_t to_size_t(int64_t v) TIGHTDB_NOEXCEPT return std::size_t(v); } + +template +ReturnType TypePunning( OriginalType variable ) +{ + union + { + OriginalType in; + ReturnType out; + }; + in = variable; + return out; +} + } // namespace tightdb #endif // TIGHTDB_UTILITIES_HPP diff --git a/test/TestQuery.cpp b/test/TestQuery.cpp index 87cb13135e8..5d59e6b5e6a 100644 --- a/test/TestQuery.cpp +++ b/test/TestQuery.cpp @@ -28,8 +28,164 @@ TIGHTDB_TABLE_5(PeopleTable, hired, Date, photo, Binary) +TIGHTDB_TABLE_2(FloatTable, + col_float, Float, + col_double, Double) + +TIGHTDB_TABLE_3(FloatTable3, + col_float, Float, + col_double, Double, + col_int, Int) } // anonymous namespace + +TEST(TestQueryFloat3) +{ + FloatTable3 t; + + t.add(float(1.1), double(2.1), 1); + t.add(float(1.2), double(2.2), 2); + t.add(float(1.3), double(2.3), 3); + t.add(float(1.4), double(2.4), 4); // match + t.add(float(1.5), double(2.5), 5); // match + t.add(float(1.6), double(2.6), 6); // match + t.add(float(1.7), double(2.7), 7); + t.add(float(1.8), double(2.8), 8); + t.add(float(1.9), double(2.9), 9); + + FloatTable3::Query q1 = t.where().col_float.greater(1.35f).col_double.less(2.65); + int64_t a1 = q1.col_int.sum(); + CHECK_EQUAL(15, a1); + + FloatTable3::Query q2 = t.where().col_double.less(2.65).col_float.greater(1.35f); + int64_t a2 = q2.col_int.sum(); + CHECK_EQUAL(15, a2); + + FloatTable3::Query q3 = t.where().col_double.less(2.65).col_float.greater(1.35f); + double a3 = q3.col_float.sum(); + CHECK_EQUAL(1.4f + 1.5f + 1.6f, a3); + + FloatTable3::Query q4 = t.where().col_float.greater(1.35f).col_double.less(2.65); + double a4 = q4.col_float.sum(); + CHECK_EQUAL(1.4f + 1.5f + 1.6f, a4); + + FloatTable3::Query q5 = t.where().col_int.greater_equal(4).col_double.less(2.65); + double a5 = q5.col_float.sum(); + CHECK_EQUAL(1.4f + 1.5f + 1.6f, a5); + + FloatTable3::Query q6 = t.where().col_double.less(2.65).col_int.greater_equal(4); + double a6 = q6.col_float.sum(); + CHECK_EQUAL(1.4f + 1.5f + 1.6f, a6); + + FloatTable3::Query q7 = t.where().col_int.greater(3).col_int.less(7); + int64_t a7 = q7.col_int.sum(); + CHECK_EQUAL(15, a7); + FloatTable3::Query q8 = t.where().col_int.greater(3).col_int.less(7); + int64_t a8 = q8.col_int.sum(); + CHECK_EQUAL(15, a8); +} + + +TEST(TestQueryFloat4) +{ + FloatTable3 t; + + t.add(std::numeric_limits::max(), std::numeric_limits::max(), 11111); + t.add(std::numeric_limits::infinity(), std::numeric_limits::infinity(), 11111); + t.add(12345.0, 12345.0, 11111); + + FloatTable3::Query q1 = t.where(); + float a1 = q1.col_float.maximum(); + double a2 = q1.col_double.maximum(); + CHECK_EQUAL(std::numeric_limits::infinity(), a1); + CHECK_EQUAL(std::numeric_limits::infinity(), a2); + + + FloatTable3::Query q2 = t.where(); + float a3 = q1.col_float.minimum(); + double a4 = q1.col_double.minimum(); + CHECK_EQUAL(12345.0, a3); + CHECK_EQUAL(12345.0, a4); +} + + +TEST(TestQueryFloat) +{ + FloatTable t; + + t.add(1.10f, 2.20); + t.add(1.13f, 2.21); + t.add(1.13f, 2.22); + t.add(1.10f, 2.20); + t.add(1.20f, 3.20); + + // Test find_all() + FloatTable::View v = t.where().col_float.equal(1.13f).find_all(); + CHECK_EQUAL(2, v.size()); + CHECK_EQUAL(1.13f, v[0].col_float.get()); + CHECK_EQUAL(1.13f, v[1].col_float.get()); + + FloatTable::View v2 = t.where().col_double.equal(3.2).find_all(); + CHECK_EQUAL(1, v2.size()); + CHECK_EQUAL(3.2, v2[0].col_double.get()); + + // Test operators (and count) + CHECK_EQUAL(2, t.where().col_float.equal(1.13f).count()); + CHECK_EQUAL(3, t.where().col_float.not_equal(1.13f).count()); + CHECK_EQUAL(3, t.where().col_float.greater(1.1f).count()); + CHECK_EQUAL(3, t.where().col_float.greater_equal(1.13f).count()); + CHECK_EQUAL(4, t.where().col_float.less_equal(1.13f).count()); + CHECK_EQUAL(2, t.where().col_float.less(1.13f).count()); + CHECK_EQUAL(3, t.where().col_float.between(1.13f, 1.2f).count()); + + CHECK_EQUAL(2, t.where().col_double.equal(2.20).count()); + CHECK_EQUAL(3, t.where().col_double.not_equal(2.20).count()); + CHECK_EQUAL(2, t.where().col_double.greater(2.21).count()); + CHECK_EQUAL(3, t.where().col_double.greater_equal(2.21).count()); + CHECK_EQUAL(4, t.where().col_double.less_equal(2.22).count()); + CHECK_EQUAL(3, t.where().col_double.less(2.22).count()); + CHECK_EQUAL(4, t.where().col_double.between(2.20, 2.22).count()); + + // ------ Test sum() + + // ... NO conditions + float sum1_f = 1.10f + 1.13f + 1.13f + 1.10f + 1.20f; + double sum1_d = 2.20 + 2.21 + 2.22 + 2.20 + 3.20; + CHECK_EQUAL(sum1_d, t.where().col_double.sum()); + CHECK_EQUAL(sum1_f, t.where().col_float.sum()); + + // ... with conditions + float sum2_f = 1.13f + 1.20f; + double sum2_d = 2.21 + 3.20; + FloatTable::Query q2 = t.where().col_float.between(1.13f, 1.20f).col_double.not_equal(2.22); + CHECK_EQUAL(sum2_d, q2.col_double.sum()); + CHECK_EQUAL(sum2_f, q2.col_float.sum()); + + // ------ Test average() + + // ... NO conditions + CHECK_EQUAL(sum1_f/5.0f, t.where().col_float.average()); + CHECK_EQUAL(sum1_d/5.0, t.where().col_double.average()); + // ... with conditions + CHECK_EQUAL(sum2_f/2.0f, q2.col_float.average()); + CHECK_EQUAL(sum2_d/2.0, q2.col_double.average()); + + // -------- Test minimum(), maximum() + + // ... NO conditions + CHECK_EQUAL(1.20f, t.where().col_float.maximum()); + CHECK_EQUAL(1.10f, t.where().col_float.minimum()); + CHECK_EQUAL(3.20, t.where().col_double.maximum()); + CHECK_EQUAL(2.20, t.where().col_double.minimum()); + + // ... with conditions + CHECK_EQUAL(1.20f, q2.col_float.maximum()); + CHECK_EQUAL(1.13f, q2.col_float.minimum()); + CHECK_EQUAL(3.20, q2.col_double.maximum()); + CHECK_EQUAL(2.21, q2.col_double.minimum()); +} + + TEST(TestDateQuery) { PeopleTable table; @@ -41,9 +197,8 @@ TEST(TestDateQuery) // Find people where hired year == 2012 (hour:minute:second is default initialized to 00:00:00) PeopleTable::View view5 = table.where().hired.greater_equal(tightdb::Date(2012, 1, 1).get_date()) .hired.less( tightdb::Date(2013, 1, 1).get_date()).find_all(); - - CHECK(view5.size() == 1 && view5[0].name == "Mary"); - + CHECK_EQUAL(1, view5.size()); + CHECK_EQUAL("Mary", view5[0].name); } @@ -140,7 +295,7 @@ TEST(TestQueryFindAll_Contains2_2) CHECK_EQUAL(5, tv2.get_source_ndx(2)); #endif } - +/* TEST(TestQuery_sum_new_aggregates) { // test the new ACTION_FIND_PATTERN() method in array @@ -159,6 +314,7 @@ TEST(TestQuery_sum_new_aggregates) CHECK_EQUAL(2000, c); } +*/ TEST(TestQuery_sum_min_max_avg_foreign_col) { @@ -465,10 +621,15 @@ TEST(TestQuerySubtable) subtable->insert_done(); + int64_t val50 = 50; + int64_t val200 = 200; + int64_t val20 = 20; + int64_t val300 = 300; + Query q1 = table->where(); - q1.greater(0, 200); + q1.greater(0, val200); q1.subtable(2); - q1.less(0, 50); + q1.less(0, val50); q1.end_subtable(); TableView t1 = q1.find_all(0, (size_t)-1); CHECK_EQUAL(2, t1.size()); @@ -478,9 +639,9 @@ TEST(TestQuerySubtable) Query q2 = table->where(); q2.subtable(2); - q2.greater(0, 50); + q2.greater(0, val50); q2.Or(); - q2.less(0, 20); + q2.less(0, val20); q2.end_subtable(); TableView t2 = q2.find_all(0, (size_t)-1); CHECK_EQUAL(2, t2.size()); @@ -490,11 +651,11 @@ TEST(TestQuerySubtable) Query q3 = table->where(); q3.subtable(2); - q3.greater(0, 50); + q3.greater(0, val50); q3.Or(); - q3.less(0, 20); + q3.less(0, val20); q3.end_subtable(); - q3.less(0, 300); + q3.less(0, val300); TableView t3 = q3.find_all(0, (size_t)-1); CHECK_EQUAL(1, t3.size()); CHECK_EQUAL(0, t3.get_source_ndx(0)); @@ -504,13 +665,14 @@ TEST(TestQuerySubtable) q4.equal(0, (int64_t)333); q4.Or(); q4.subtable(2); - q4.greater(0, 50); + q4.greater(0, val50); q4.Or(); - q4.less(0, 20); + q4.less(0, val20); q4.end_subtable(); TableView t4 = q4.find_all(0, (size_t)-1); + CHECK_EQUAL(3, t4.size()); CHECK_EQUAL(0, t4.get_source_ndx(0)); CHECK_EQUAL(2, t4.get_source_ndx(1)); @@ -714,7 +876,7 @@ TEST(TestQuerySimple2) CHECK_EQUAL(7, tv1.get_source_ndx(2)); } - +/* TEST(TestQueryLimit) { TupleTableType ttt; @@ -760,6 +922,7 @@ TEST(TestQueryLimit) TupleTableType::View tv5 = q3.find_all(0, 3, 5); CHECK_EQUAL(3, tv5.size()); } +*/ TEST(TestQueryFindNext) { @@ -1347,25 +1510,19 @@ TEST(TestTV) t.add(1, "a"); t.add(2, "a"); t.add(3, "c"); + + TupleTableType::View v = t.where().first.greater(1).find_all(); - Array arr; - arr.add(0); - arr.add(2); - TupleTableType::Query q1 = t.where().tableview(arr); + TupleTableType::Query q1 = t.where().tableview(v); CHECK_EQUAL(2, q1.count()); - TupleTableType::Query q2 = t.where().second.equal("a").tableview(arr); - CHECK_EQUAL(1, q2.count()); - - TupleTableType::Query q3 = t.where().tableview(arr).second.equal("a"); + TupleTableType::Query q3 = t.where().tableview(v).second.equal("a"); CHECK_EQUAL(1, q3.count()); - - TupleTableType::Query q4 = t.where().tableview(arr).second.equal("a"); - TupleTableType::View v4 = q4.find_all(); - CHECK_EQUAL(1, v4.size()); - arr.Destroy(); } + #if 0 + !!! + TEST(TestQuery_sum_min_max_avg) { TupleTableType t; @@ -1403,6 +1560,7 @@ TEST(TestQuery_sum_min_max_avg) CHECK_EQUAL(6, t.where().first.sum(&cnt, 0, size_t(-1))); CHECK_EQUAL(3, cnt); } +#endif TEST(TestQuery_avg) { diff --git a/test/UnitTest++/UnitTest++.vsnet2005.vcxproj b/test/UnitTest++/UnitTest++.vsnet2005.vcxproj index 227ac10866d..babcecdef53 100644 --- a/test/UnitTest++/UnitTest++.vsnet2005.vcxproj +++ b/test/UnitTest++/UnitTest++.vsnet2005.vcxproj @@ -186,7 +186,11 @@ Level4 ProgramDatabase + true + + true + diff --git a/test/UnitTest++/src/ExecuteTest.h b/test/UnitTest++/src/ExecuteTest.h index a00d4c5757e..86f7ebc32e9 100644 --- a/test/UnitTest++/src/ExecuteTest.h +++ b/test/UnitTest++/src/ExecuteTest.h @@ -35,7 +35,7 @@ void ExecuteTest(T& testObject, TestDetails const& details) stream << "Unhandled exception: " << e.what(); CurrentTest::Results()->OnTestFailure(details, stream.GetText()); } - catch (...) + catch (...) { CurrentTest::Results()->OnTestFailure(details, "Unhandled exception: Crash!"); } diff --git a/test/benchmark-sqlite/benchmark-sqlite.vcxproj b/test/benchmark-sqlite/benchmark-sqlite.vcxproj index 87ae02b501e..293d92a3d33 100644 --- a/test/benchmark-sqlite/benchmark-sqlite.vcxproj +++ b/test/benchmark-sqlite/benchmark-sqlite.vcxproj @@ -1,4 +1,4 @@ - + @@ -207,6 +207,7 @@ true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true Console @@ -215,6 +216,9 @@ true psapi.lib %(AdditionalOptions) + + true + diff --git a/test/benchmark-stl/benchmark-stl.vcxproj b/test/benchmark-stl/benchmark-stl.vcxproj index 884408dfaa0..b3271e7ce94 100644 --- a/test/benchmark-stl/benchmark-stl.vcxproj +++ b/test/benchmark-stl/benchmark-stl.vcxproj @@ -213,6 +213,7 @@ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true Console @@ -221,6 +222,9 @@ true Psapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + true + diff --git a/test/benchmark-tightdb/benchmark-tightdb.vcxproj b/test/benchmark-tightdb/benchmark-tightdb.vcxproj index 3a25b64aba5..27bebbfbc44 100644 --- a/test/benchmark-tightdb/benchmark-tightdb.vcxproj +++ b/test/benchmark-tightdb/benchmark-tightdb.vcxproj @@ -210,6 +210,7 @@ true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) ../../src + true Console @@ -220,6 +221,9 @@ tightdb32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) ..\..\lib\ + + true + diff --git a/test/experiments/float.cpp b/test/experiments/float.cpp new file mode 100644 index 00000000000..2ff30620904 --- /dev/null +++ b/test/experiments/float.cpp @@ -0,0 +1,36 @@ +#include +#include + +union Float_t +{ + Float_t(float num = 0.0f) : f(num) {} + // Portable extraction of components. + bool Negative() const { return (i >> 31) != 0; } + int32_t RawMantissa() const { return i & ((1 << 23) - 1); } + int32_t RawExponent() const { return (i >> 23) & 0xFF; } + + int32_t i; + float f; +#ifdef _DEBUG + struct + { // Bitfields for exploration. Do not use in production code. + uint32_t mantissa : 23; + uint32_t exponent : 8; + uint32_t sign : 1; + } parts; +#endif +}; + +void exploreFloat() +{ + Float_t num(1.0f); + num.i -= 1; + printf("Float value, representation, sign, exponent, mantissa\n"); + for (int i=0; i<100;i++) + { + // Breakpoint here. + printf("%1.8e, 0x%08X, %d, %d, 0x%06X\n", + num.f, num.i, + num.parts.sign, num.parts.exponent, num.parts.mantissa); + } +} \ No newline at end of file diff --git a/test/experiments/fp_print.c b/test/experiments/fp_print.c new file mode 100644 index 00000000000..a74dd3c2b90 --- /dev/null +++ b/test/experiments/fp_print.c @@ -0,0 +1,592 @@ +/* Free-format floating point printer */ + +#include + +#if 1 //defined(__alpha) || defined(__i386) || defined(_M_IX86) || defined(__x86_64) +#define LITTLE_ENDIAN_IEEE_DOUBLE +#elif !(defined(__ppc__) || defined(sparc) || defined(__sgi) || defined(_IBMR2) || defined(hpux)) +#error unknown machine type +#endif + +#if defined(_M_IX86) +#define UNSIGNED64 unsigned __int64 +#elif defined(__alpha) || defined(__x86_64) +#define UNSIGNED64 unsigned long +#else +#define UNSIGNED64 unsigned long long +#endif + +#if defined(sparc) || defined(hpux) +#define PROTO(x) () +#else +#define PROTO(x) x +#endif + +#ifndef U32 +#define U32 unsigned int +#endif + +/* exponent stored + 1024, hidden bit to left of decimal point */ +#define bias 1023 +#define bitstoright 52 +#define m1mask 0xf +#define hidden_bit 0x100000 +#ifdef LITTLE_ENDIAN_IEEE_DOUBLE +struct dblflt { + unsigned int m4: 16; + unsigned int m3: 16; + unsigned int m2: 16; + unsigned int m1: 4; + unsigned int e: 11; + unsigned int s: 1; +}; +#else +/* Big Endian IEEE Double Floats */ +struct dblflt { + unsigned int s: 1; + unsigned int e: 11; + unsigned int m1: 4; + unsigned int m2: 16; + unsigned int m3: 16; + unsigned int m4: 16; +}; +#endif +#define float_radix 2.147483648e9 + + + + +typedef UNSIGNED64 Bigit; +#define BIGSIZE 24 +#define MIN_E -1074 +#define MAX_FIVE 325 +#define B_P1 ((Bigit)1 << 52) + +typedef struct { + int l; + Bigit d[BIGSIZE]; +} Bignum; + +Bignum R, S, MP, MM, five[MAX_FIVE]; +Bignum S2, S3, S4, S5, S6, S7, S8, S9; +int ruf, k, s_n, use_mp, qr_shift, sl, slr; + +void mul10 PROTO((Bignum *x)); +void big_short_mul PROTO((Bignum *x, Bigit y, Bignum *z)); +void print_big PROTO((Bignum *x)); +int estimate PROTO((int n)); +void one_shift_left PROTO((int y, Bignum *z)); +void short_shift_left PROTO((Bigit x, int y, Bignum *z)); +void big_shift_left PROTO((Bignum *x, int y, Bignum *z)); +int big_comp PROTO((Bignum *x, Bignum *y)); +int sub_big PROTO((Bignum *x, Bignum *y, Bignum *z)); +void add_big PROTO((Bignum *x, Bignum *y, Bignum *z)); +int add_cmp PROTO((void)); +int qr PROTO((void)); + +int dragon PROTO((char *buf, double v)); +void free_init PROTO((void)); + +#define ADD(x, y, z, k) {\ + Bigit x_add, z_add;\ + x_add = (x);\ + if ((k))\ + z_add = x_add + (y) + 1, (k) = (z_add <= x_add);\ + else\ + z_add = x_add + (y), (k) = (z_add < x_add);\ + (z) = z_add;\ +} + +#define SUB(x, y, z, b) {\ + Bigit x_sub, y_sub;\ + x_sub = (x); y_sub = (y);\ + if ((b))\ + (z) = x_sub - y_sub - 1, b = (y_sub >= x_sub);\ + else\ + (z) = x_sub - y_sub, b = (y_sub > x_sub);\ +} + +#define MUL(x, y, z, k) {\ + Bigit x_mul, low, high;\ + x_mul = (x);\ + low = (x_mul & 0xffffffff) * (y) + (k);\ + high = (x_mul >> 32) * (y) + (low >> 32);\ + (k) = high >> 32;\ + (z) = (low & 0xffffffff) | (high << 32);\ +} + +#define SLL(x, y, z, k) {\ + Bigit x_sll = (x);\ + (z) = (x_sll << (y)) | (k);\ + (k) = x_sll >> (64 - (y));\ +} + +void mul10(Bignum *x) { + int i, l; + Bigit *p, k; + + l = x->l; + for (i = l, p = &x->d[0], k = 0; i >= 0; i--) + MUL(*p, 10, *p++, k); + if (k != 0) + *p = k, x->l = l+1; +} + +void big_short_mul(Bignum *x, Bigit y, Bignum*z) { + int i, xl, zl; + Bigit *xp, *zp, k; + U32 high, low; + + xl = x->l; + xp = &x->d[0]; + zl = xl; + zp = &z->d[0]; + high = y >> 32; + low = y & 0xffffffff; + for (i = xl, k = 0; i >= 0; i--, xp++, zp++) { + Bigit xlow, xhigh, z0, t, c, z1; + xlow = *xp & 0xffffffff; + xhigh = *xp >> 32; + z0 = (xlow * low) + k; /* Cout is (z0 < k) */ + t = xhigh * low; + z1 = (xlow * high) + t; + c = (z1 < t); + t = z0 >> 32; + z1 += t; + c += (z1 < t); + *zp = (z1 << 32) | (z0 & 0xffffffff); + k = (xhigh * high) + (c << 32) + (z1 >> 32) + (z0 < k); + } + if (k != 0) + *zp = k, zl++; + z->l = zl; +} + +void print_big(Bignum *x) { + int i; + Bigit *p; + + printf("#x"); + i = x->l; + p = &x->d[i]; + for (p = &x->d[i]; i >= 0; i--) { + Bigit b = *p--; + printf("%08x%08x", (int)(b >> 32), (int)(b & 0xffffffff)); + } +} + +int estimate(int n) { + if (n < 0) + return (int)(n*0.3010299956639812); + else + return 1+(int)(n*0.3010299956639811); +} + +void one_shift_left(int y, Bignum *z) { + int n, m, i; + Bigit *zp; + + n = y / 64; + m = y % 64; + zp = &z->d[0]; + for (i = n; i > 0; i--) *zp++ = 0; + *zp = (Bigit)1 << m; + z->l = n; +} + +void short_shift_left(Bigit x, int y, Bignum *z) { + int n, m, i, zl; + Bigit *zp; + + n = y / 64; + m = y % 64; + zl = n; + zp = &(z->d[0]); + for (i = n; i > 0; i--) *zp++ = 0; + if (m == 0) + *zp = x; + else { + Bigit high = x >> (64 - m); + *zp = x << m; + if (high != 0) + *++zp = high, zl++; + } + z->l = zl; +} + +void big_shift_left(Bignum *x, int y, Bignum *z) { + int n, m, i, xl, zl; + Bigit *xp, *zp, k; + + n = y / 64; + m = y % 64; + xl = x->l; + xp = &(x->d[0]); + zl = xl + n; + zp = &(z->d[0]); + for (i = n; i > 0; i--) *zp++ = 0; + if (m == 0) + for (i = xl; i >= 0; i--) *zp++ = *xp++; + else { + for (i = xl, k = 0; i >= 0; i--) + SLL(*xp++, m, *zp++, k); + if (k != 0) + *zp = k, zl++; + } + z->l = zl; +} + + +int big_comp(Bignum *x, Bignum *y) { + int i, xl, yl; + Bigit *xp, *yp; + + xl = x->l; + yl = y->l; + if (xl > yl) return 1; + if (xl < yl) return -1; + xp = &x->d[xl]; + yp = &y->d[xl]; + for (i = xl; i >= 0; i--, xp--, yp--) { + Bigit a = *xp; + Bigit b = *yp; + + if (a > b) return 1; + else if (a < b) return -1; + } + return 0; +} + +int sub_big(Bignum *x, Bignum *y, Bignum *z) { + int xl, yl, zl, b, i; + Bigit *xp, *yp, *zp; + + xl = x->l; + yl = y->l; + if (yl > xl) return 1; + xp = &x->d[0]; + yp = &y->d[0]; + zp = &z->d[0]; + + for (i = yl, b = 0; i >= 0; i--) + SUB(*xp++, *yp++, *zp++, b); + for (i = xl-yl; b && i > 0; i--) { + Bigit x_sub; + x_sub = *xp++; + *zp++ = x_sub - 1; + b = (x_sub == 0); + } + for (; i > 0; i--) *zp++ = *xp++; + if (b) return 1; + zl = xl; + while (zl > 0 && *--zp == 0) zl--; + z->l = zl; + return 0; +} + +void add_big(Bignum *x, Bignum *y, Bignum *z) { + int xl, yl, k, i; + Bigit *xp, *yp, *zp; + + xl = x->l; + yl = y->l; + if (yl > xl) { + int tl; + Bignum *tn; + tl = xl; xl = yl; yl = tl; + tn = x; x = y; y = tn; + } + + xp = &x->d[0]; + yp = &y->d[0]; + zp = &z->d[0]; + + for (i = yl, k = 0; i >= 0; i--) + ADD(*xp++, *yp++, *zp++, k); + for (i = xl-yl; k && i > 0; i--) { + Bigit z_add; + z_add = *xp++ + 1; + k = (z_add == 0); + *zp++ = z_add; + } + for (; i > 0; i--) *zp++ = *xp++; + if (k) + *zp = 1, z->l = xl+1; + else + z->l = xl; +} + +int add_cmp() { + int rl, ml, sl, suml; + static Bignum sum; + + rl = R.l; + ml = (use_mp ? MP.l : MM.l); + sl = S.l; + + suml = rl >= ml ? rl : ml; + if ((sl > suml+1) || ((sl == suml+1) && (S.d[sl] > 1))) return -1; + if (sl < suml) return 1; + + add_big(&R, (use_mp ? &MP : &MM), &sum); + return big_comp(&sum, &S); +} + +int qr() { + if (big_comp(&R, &S5) < 0) + if (big_comp(&R, &S2) < 0) + if (big_comp(&R, &S) < 0) + return 0; + else { + sub_big(&R, &S, &R); + return 1; + } + else if (big_comp(&R, &S3) < 0) { + sub_big(&R, &S2, &R); + return 2; + } + else if (big_comp(&R, &S4) < 0) { + sub_big(&R, &S3, &R); + return 3; + } + else { + sub_big(&R, &S4, &R); + return 4; + } + else if (big_comp(&R, &S7) < 0) + if (big_comp(&R, &S6) < 0) { + sub_big(&R, &S5, &R); + return 5; + } + else { + sub_big(&R, &S6, &R); + return 6; + } + else if (big_comp(&R, &S9) < 0) + if (big_comp(&R, &S8) < 0) { + sub_big(&R, &S7, &R); + return 7; + } + else { + sub_big(&R, &S8, &R); + return 8; + } + else { + sub_big(&R, &S9, &R); + return 9; + } +} + +#define OUTDIG(d) { *buf++ = (d) + '0'; *buf = 0; return k; } + +int dragon(char *buf, double v) { + struct dblflt *x; + int sign, e, f_n, m_n, i, d, tc1, tc2; + Bigit f; + + /* decompose float into sign, mantissa & exponent */ + x = (struct dblflt *)&v; + sign = x->s; + e = x->e; + f = (Bigit)(x->m1 << 16 | x->m2) << 32 | (U32)(x->m3 << 16 | x->m4); + if (e != 0) { + e = e - bias - bitstoright; + f |= (Bigit)hidden_bit << 32; + } + else if (f != 0) + /* denormalized */ + e = 1 - bias - bitstoright; + + if (sign) *buf++ = '-'; + if (f == 0) { + *buf++ = '0'; + *buf = 0; + return 0; + } + + ruf = !(f & 1); /* ruf = (even? f) */ + + /* Compute the scaling factor estimate, k */ + if (e > MIN_E) + k = estimate(e+52); + else { + int n; + Bigit y; + + for (n = e+52, y = (Bigit)1 << 52; f < y; n--) y >>= 1; + k = estimate(n); + } + + if (e >= 0) + if (f != B_P1) + use_mp = 0, f_n = e+1, s_n = 1, m_n = e; + else + use_mp = 1, f_n = e+2, s_n = 2, m_n = e; + else + if ((e == MIN_E) || (f != B_P1)) + use_mp = 0, f_n = 1, s_n = 1-e, m_n = 0; + else + use_mp = 1, f_n = 2, s_n = 2-e, m_n = 0; + + /* Scale it! */ + if (k == 0) { + short_shift_left(f, f_n, &R); + one_shift_left(s_n, &S); + one_shift_left(m_n, &MM); + if (use_mp) one_shift_left(m_n+1, &MP); + qr_shift = 1; + } + else if (k > 0) { + s_n += k; + if (m_n >= s_n) + f_n -= s_n, m_n -= s_n, s_n = 0; + else + f_n -= m_n, s_n -= m_n, m_n = 0; + short_shift_left(f, f_n, &R); + big_shift_left(&five[k-1], s_n, &S); + one_shift_left(m_n, &MM); + if (use_mp) one_shift_left(m_n+1, &MP); + qr_shift = 0; + } + else { + Bignum *power = &five[-k-1]; + + s_n += k; + big_short_mul(power, f, &S); + big_shift_left(&S, f_n, &R); + one_shift_left(s_n, &S); + big_shift_left(power, m_n, &MM); + if (use_mp) big_shift_left(power, m_n+1, &MP); + qr_shift = 1; + } + + /* fixup */ + if (add_cmp() <= -ruf) { + k--; + mul10(&R); + mul10(&MM); + if (use_mp) mul10(&MP); + } + + /* + printf("\nk = %d\n", k); + printf("R = "); print_big(&R); + printf("\nS = "); print_big(&S); + printf("\nM- = "); print_big(&MM); + if (use_mp) printf("\nM+ = "), print_big(&MP); + putchar('\n'); + fflush(0); + */ + + if (qr_shift) { + sl = s_n / 64; + slr = s_n % 64; + } + else { + big_shift_left(&S, 1, &S2); + add_big(&S2, &S, &S3); + big_shift_left(&S2, 1, &S4); + add_big(&S4, &S, &S5); + add_big(&S4, &S2, &S6); + add_big(&S4, &S3, &S7); + big_shift_left(&S4, 1, &S8); + add_big(&S8, &S, &S9); + } + +again: + if (qr_shift) { /* Take advantage of the fact that S = (ash 1 s_n) */ + if (R.l < sl) + d = 0; + else if (R.l == sl) { + Bigit *p; + + p = &R.d[sl]; + d = *p >> slr; + *p &= ((Bigit)1 << slr) - 1; + for (i = sl; (i > 0) && (*p == 0); i--) p--; + R.l = i; + } + else { + Bigit *p; + + p = &R.d[sl+1]; + d = *p << (64 - slr) | *(p-1) >> slr; + p--; + *p &= ((Bigit)1 << slr) - 1; + for (i = sl; (i > 0) && (*p == 0); i--) p--; + R.l = i; + } + } + else /* We need to do quotient-remainder */ + d = qr(); + + tc1 = big_comp(&R, &MM) < ruf; + tc2 = add_cmp() > -ruf; + if (!tc1) + if (!tc2) { + mul10(&R); + mul10(&MM); + if (use_mp) mul10(&MP); + *buf++ = d + '0'; + goto again; + } + else + OUTDIG(d+1) + else + if (!tc2) + OUTDIG(d) + else { + big_shift_left(&R, 1, &MM); + if (big_comp(&MM, &S) == -1) + OUTDIG(d) + else + OUTDIG(d+1) + } +} + +void free_init() { + int n, i, l; + Bignum *b; + Bigit *xp, *zp, k; + + five[0].l = l = 0; + five[0].d[0] = 5; + for (n = MAX_FIVE-1, b = &five[0]; n > 0; n--) { + xp = &b->d[0]; + b++; + zp = &b->d[0]; + for (i = l, k = 0; i >= 0; i--) + MUL(*xp++, 5, *zp++, k); + if (k != 0) + *zp = k, l++; + b->l = l; + } + + /* + for (n = 1, b = &five[0]; n <= MAX_FIVE; n++) { + big_shift_left(b++, n, &R); + print_big(&R); + putchar('\n'); + } + fflush(0); + */ +} + +void test_fp(double f) +{ + char buf[30]; + printf("Float printf: %f.\n", f); + dragon(buf, f); + printf("Dragon: %s\n", buf); +} + +void test_fp_print() { + free_init(); + + test_fp(1.3); + test_fp(1.23456); + test_fp(0.13); + test_fp(1.0/3.0); + + +} \ No newline at end of file diff --git a/test/large_tests/test_column.cpp b/test/large_tests/test_column.cpp index f62f8e94ce5..5aa4de9e451 100644 --- a/test/large_tests/test_column.cpp +++ b/test/large_tests/test_column.cpp @@ -59,7 +59,7 @@ TEST(LESS) size_t LEN2 = 64 * 8 / (a.GetBitWidth() == 0 ? 1 : a.GetBitWidth()); Array akku; - state_state state; + QueryState state; state.state = int64_t(&akku); for (size_t from = 0; from < LEN2; from++) { diff --git a/test/main.cpp b/test/main.cpp index e0d5309a3dc..ccb33ccb86a 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -2,16 +2,9 @@ #include #include #include - -// FIXME: I suggest we enable this only if -// TIGHTDB_VISUAL_LEAK_DETECTOR is defined. It is problematic that we -// cannot run the unit tests witout installing this (and indeed -// installing it in that particular location) -/* -#if defined(_MSC_VER) && defined(_DEBUG) -# include "C:\\Program Files (x86)\\Visual Leak Detector\\include\\vld.h" +#if defined(_MSC_VER) && defined(_DEBUG) && defined(USE_VLD) + #include "C:\\Program Files (x86)\\Visual Leak Detector\\include\\vld.h" #endif -*/ using namespace std; diff --git a/test/test-sqlite3/test-sqlite3.vcxproj b/test/test-sqlite3/test-sqlite3.vcxproj index 8248227872a..1e648177c7d 100644 --- a/test/test-sqlite3/test-sqlite3.vcxproj +++ b/test/test-sqlite3/test-sqlite3.vcxproj @@ -207,6 +207,7 @@ Level3 ProgramDatabase + true Psapi.lib;%(AdditionalDependencies) @@ -216,6 +217,9 @@ true MachineX86 + + true + diff --git a/test/test-stl/test-stl.vcxproj b/test/test-stl/test-stl.vcxproj index 303808f7284..d28c192086c 100644 --- a/test/test-stl/test-stl.vcxproj +++ b/test/test-stl/test-stl.vcxproj @@ -207,6 +207,7 @@ Level3 ProgramDatabase + true Psapi.lib;%(AdditionalDependencies) @@ -216,6 +217,9 @@ true MachineX86 + + true + diff --git a/test/test-tightdb/test-tightdb.vcxproj b/test/test-tightdb/test-tightdb.vcxproj index 7a0be0d51d0..0ed165e8028 100644 --- a/test/test-tightdb/test-tightdb.vcxproj +++ b/test/test-tightdb/test-tightdb.vcxproj @@ -1,4 +1,4 @@ - + @@ -271,6 +271,7 @@ Level3 ProgramDatabase + true tightdb32.lib;Psapi.lib;%(AdditionalDependencies) @@ -282,6 +283,9 @@ ..\..\lib libcpmt.lib;LIBCMT.lib + + true + diff --git a/test/testTableView.cpp b/test/testTableView.cpp index fcb26d42060..378ef4f7e68 100644 --- a/test/testTableView.cpp +++ b/test/testTableView.cpp @@ -3,9 +3,10 @@ using namespace tightdb; +namespace { TIGHTDB_TABLE_1(TestTableInt, first, Int) - +} TEST(GetSetInteger) { @@ -32,6 +33,104 @@ TEST(GetSetInteger) } + +namespace { +TIGHTDB_TABLE_3(TableFloats, + col_float, Float, + col_double, Double, + col_int, Int) +} + +TEST(TableView_Floats_GetSet) +{ + TableFloats table; + + float f_val[] = { 1.1f, 2.1f, 3.1f, -1.1f, 2.1f, 0.0f }; + double d_val[] = { 1.2 , 2.2 , 3.2 , -1.2 , 2.3, 0.0 }; + + CHECK_EQUAL(1, table.is_empty()); + + // Test add(?,?) with parameters + for (size_t i=0; i<5; ++i) + table.add(f_val[i], d_val[i], i); + table.add(); + CHECK_EQUAL(6, table.size()); + for (size_t i=0; i<6; ++i) { + CHECK_EQUAL(f_val[i], table.column().col_float[i]); + CHECK_EQUAL(d_val[i], table.column().col_double[i]); + } + + TableFloats::View v; // Test empty construction + v = table.column().col_float.find_all(2.1f); // Test assignment + CHECK_EQUAL(2, v.size()); + + // Test of Get + CHECK_EQUAL(2.1f, v[0].col_float); + CHECK_EQUAL(2.1f, v[1].col_float); + CHECK_EQUAL(2.2, v[0].col_double); + CHECK_EQUAL(2.3, v[1].col_double); + + // Test of Set + v[0].col_float = 123.321f; + CHECK_EQUAL(123.321f, v[0].col_float); + v[0].col_double = 123.3219; + CHECK_EQUAL(123.3219, v[0].col_double); +} + +TEST(TableView_Floats_Find_and_Aggregations) +{ + TableFloats table; + float f_val[] = { 1.2f, 2.1f, 3.1f, -1.1f, 2.1f, 0.0f }; + double d_val[] = { -1.2, 2.2 , 3.2 ,-1.2 , 2.3 , 0.0 }; + float sum_f = 0.0f; + double sum_d = 0.0; + for (size_t i=0; i<6; ++i) { + table.add(f_val[i], d_val[i], 1); + sum_d += d_val[i]; + sum_f += f_val[i]; + } + + // Test find_all() + TableFloats::View v_all = table.column().col_int.find_all(1); + CHECK_EQUAL(6, v_all.size()); + + TableFloats::View v_some = table.column().col_double.find_all(-1.2); + CHECK_EQUAL(2, v_some.size()); + CHECK_EQUAL(0, v_some.get_source_ndx(0)); + CHECK_EQUAL(3, v_some.get_source_ndx(1)); + + // Test find_first + CHECK_EQUAL(0, v_all.column().col_double.find_first(-1.2) ); + CHECK_EQUAL(5, v_all.column().col_double.find_first(0.0) ); + CHECK_EQUAL(2, v_all.column().col_double.find_first(3.2) ); + + CHECK_EQUAL(1, v_all.column().col_float.find_first(2.1f) ); + CHECK_EQUAL(5, v_all.column().col_float.find_first(0.0f) ); + CHECK_EQUAL(2, v_all.column().col_float.find_first(3.1f) ); + + // TODO: add for float as well + + // Test sum + CHECK_EQUAL(sum_d, v_all.column().col_double.sum()); + CHECK_EQUAL(sum_f, v_all.column().col_float.sum()); + CHECK_EQUAL(-1.2 -1.2, v_some.column().col_double.sum()); + CHECK_EQUAL(1.2f -1.1f, v_some.column().col_float.sum()); + + // Test max + CHECK_EQUAL(3.2, v_all.column().col_double.maximum()); + CHECK_EQUAL(-1.2, v_some.column().col_double.maximum()); + CHECK_EQUAL(3.1f, v_all.column().col_float.maximum()); + CHECK_EQUAL(1.2f, v_some.column().col_float.maximum()); + + // Test min + CHECK_EQUAL(-1.2, v_all.column().col_double.minimum()); + CHECK_EQUAL(-1.2, v_some.column().col_double.minimum()); + CHECK_EQUAL(-1.1f, v_all.column().col_float.minimum()); + CHECK_EQUAL(-1.1f, v_some.column().col_float.minimum()); + + // TODO: Test +=, average, count +} + TEST(TableViewSum) { TestTableInt table; @@ -82,8 +181,6 @@ TEST(TableViewMax) CHECK_EQUAL(2, max); } - - TEST(TableViewMax2) { TestTableInt table; @@ -119,7 +216,6 @@ TEST(TableViewMin) CHECK_EQUAL(-1, min); } - TEST(TableViewMin2) { TestTableInt table; @@ -138,7 +234,6 @@ TEST(TableViewMin2) } - TEST(TableViewFind) { TestTableInt table; @@ -176,8 +271,10 @@ TEST(TableViewFindAll) CHECK_EQUAL(2, v2.get_source_ndx(1)); } +namespace { TIGHTDB_TABLE_1(TestTableString, first, String) +} TEST(TableViewFindAllString) { diff --git a/test/testarrayfloat.cpp b/test/testarrayfloat.cpp new file mode 100644 index 00000000000..6d3b1b05faa --- /dev/null +++ b/test/testarrayfloat.cpp @@ -0,0 +1,384 @@ +#include +#include +#include + +template inline +size_t SizeOfArray( const T(&)[ N ] ) +{ + return N; +} + +using namespace tightdb; + +// Article about comparing floats: +// http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ + +namespace { +float floatVal[] = {0.0f, + 1.0f, + 2.12345f, + 12345.12f, + -12345.12f + }; +const size_t floatValLen = SizeOfArray(floatVal); + +double doubleVal[] = {0.0, + 1.0, + 2.12345, + 12345.12, + -12345.12 + }; +const size_t doubleValLen = SizeOfArray(doubleVal); +} + +// TODO: Add test of full range of floats. + +template +void BasicArray_AddGet(T val[], size_t valLen) +{ + C f; + for (size_t i=0; i(floatVal, floatValLen); } +TEST(ArrayDouble_AddGet){ BasicArray_AddGet(doubleVal, doubleValLen); } + + +template +void BasicArray_AddManyValues() +{ + C f; + const size_t repeats = 1100; + for (size_t i=0; i(); } +TEST(ArrayDouble_AddManyValues){ BasicArray_AddManyValues(); } + +template +void BasicArray_Delete() +{ + C f; + for (size_t i=0; i<5; ++i) + f.add( T(i) ); + + // Delete first + f.Delete(0); + CHECK_EQUAL(4, f.Size()); + CHECK_EQUAL(1, f.Get(0)); + CHECK_EQUAL(2, f.Get(1)); + CHECK_EQUAL(3, f.Get(2)); + CHECK_EQUAL(4, f.Get(3)); + + // Delete last + f.Delete(3); + CHECK_EQUAL(3, f.Size()); + CHECK_EQUAL(1, f.Get(0)); + CHECK_EQUAL(2, f.Get(1)); + CHECK_EQUAL(3, f.Get(2)); + + // Delete middle + f.Delete(1); + CHECK_EQUAL(2, f.Size()); + CHECK_EQUAL(1, f.Get(0)); + CHECK_EQUAL(3, f.Get(1)); + + // Delete all + f.Delete(0); + CHECK_EQUAL(1, f.Size()); + CHECK_EQUAL(3, f.Get(0)); + f.Delete(0); + CHECK_EQUAL(0, f.Size()); + CHECK(f.is_empty()); +} +TEST(ArrayFloat_Delete) { BasicArray_Delete(); } +TEST(ArrayDouble_Delete){ BasicArray_Delete(); } + + +template +void BasicArray_Set(T val[], size_t valLen) +{ + C f; + CHECK_EQUAL(0, f.Size()); + for (size_t i=0; i(floatVal, floatValLen); } +TEST(ArrayDouble_Set){ BasicArray_Set(doubleVal, doubleValLen); } + + +template +void BasicArray_Insert() +{ + C f; + const T v0 = T(123.970); + const T v1 = T(-321.971); + T v2 = T(555.972); + T v3 = T(-999.973); + + // Insert in empty array + f.Insert(0, v0); + CHECK_EQUAL(v0, f.Get(0)); + CHECK_EQUAL(1, f.Size()); + + // Insert in top + f.Insert(0, v1); + CHECK_EQUAL(v1, f.Get(0)); + CHECK_EQUAL(v0, f.Get(1)); + CHECK_EQUAL(2, f.Size()); + + // Insert in middle + f.Insert(1, v2); + CHECK_EQUAL(v1, f.Get(0)); + CHECK_EQUAL(v2, f.Get(1)); + CHECK_EQUAL(v0, f.Get(2)); + CHECK_EQUAL(3, f.Size()); + + // Insert at buttom + f.Insert(3, v3); + CHECK_EQUAL(v1, f.Get(0)); + CHECK_EQUAL(v2, f.Get(1)); + CHECK_EQUAL(v0, f.Get(2)); + CHECK_EQUAL(v3, f.Get(3)); + CHECK_EQUAL(4, f.Size()); +} +TEST(ArrayFloat_Insert) { BasicArray_Insert(); } +TEST(ArrayDouble_Insert){ BasicArray_Insert(); } + +#if 0 +// sum() is unused by other classes +template +void BasicArray_Sum() +{ + C f; + + T values[] = { T(1.1), T(2.2), T(3.3), T(4.4), T(5.5)}; + double sum = 0.0; + for (size_t i=0; i<5; ++i) { + f.add(values[i]); + sum += values[i]; + } + CHECK_EQUAL(5, f.Size()); + + // all + CHECK_EQUAL(sum, f.sum()); + // first + CHECK_EQUAL(double(values[0]), f.sum(0, 1)); + // last + CHECK_EQUAL(double(values[4]), f.sum(4, 5)); + // middle range + CHECK_EQUAL(double(values[2]) + double(values[3]) + double(values[4]), f.sum(2)); + // single middle + CHECK_EQUAL(double(values[2]), f.sum(2, 3)); +} +TEST(ArrayFloat_Sum) { BasicArray_Sum(); } +TEST(ArrayDouble_Sum){ BasicArray_Sum(); } +#endif + +template +void BasicArray_Minimum() +{ + C f; + T res; + + CHECK_EQUAL(false, f.minimum(res)); + + T values[] = { T(1.1), T(2.2), T(-1.0), T(5.5), T(4.4)}; + for (size_t i=0; i<5; ++i) { + f.add(values[i]); + } + CHECK_EQUAL(5, f.Size()); + + // middle match in all + CHECK_EQUAL(true, f.minimum(res)); + CHECK_EQUAL(values[2], res); + // first match + CHECK_EQUAL(true, f.minimum(res, 0, 2)); + CHECK_EQUAL(values[0], res); + // middle range, last match + CHECK_EQUAL(true, f.minimum(res, 1, 3)); + CHECK_EQUAL(values[2], res); + // single middle + CHECK_EQUAL(true, f.minimum(res, 3, 4)); + CHECK_EQUAL(values[3], res); + // first match in range + CHECK_EQUAL(true, f.minimum(res, 3, size_t(-1))); + CHECK_EQUAL(values[4], res); +} +TEST(ArrayFloat_Minimum) { BasicArray_Minimum(); } +TEST(ArrayDouble_Minimum){ BasicArray_Minimum(); } + + +template +void BasicArray_Maximum() +{ + C f; + T res; + + CHECK_EQUAL(false, f.maximum(res)); + + T values[] = { T(1.1), T(2.2), T(-1.0), T(5.5), T(4.4)}; + for (size_t i=0; i<5; ++i) { + f.add(values[i]); + } + CHECK_EQUAL(5, f.Size()); + + // middle match in all + CHECK_EQUAL(true, f.maximum(res)); + CHECK_EQUAL(values[3], res); + // last match + CHECK_EQUAL(true, f.maximum(res, 0, 2)); + CHECK_EQUAL(values[1], res); + // middle range, last match + CHECK_EQUAL(true, f.maximum(res, 1, 4)); + CHECK_EQUAL(values[3], res); + // single middle + CHECK_EQUAL(true, f.maximum(res, 3, 4)); + CHECK_EQUAL(values[3], res); + // first match in range + CHECK_EQUAL(true, f.maximum(res, 3, size_t(-1))); + CHECK_EQUAL(values[3], res); +} +TEST(ArrayFloat_Maximum) { BasicArray_Maximum(); } +TEST(ArrayDouble_Maximum){ BasicArray_Maximum(); } + + +template +void BasicArray_Find() +{ + C f; + + // Empty list + CHECK_EQUAL(-1, f.find_first(0)); + + // Add some values + T values[] = { T(1.1), T(2.2), T(-1.0), T(5.5), T(1.1), T(4.4)}; + for (size_t i=0; i<6; ++i) { + f.add(values[i]); + } + + // Find (full range: start=0, end=-1) + CHECK_EQUAL(0, f.find_first(T(1.1))); + CHECK_EQUAL(5, f.find_first(T(4.4))); + CHECK_EQUAL(2, f.find_first(T(-1.0))); + + // non-existing + CHECK_EQUAL(-1, f.find_first(T(0))); + + // various range limitations + CHECK_EQUAL( 1, f.find_first(T(2.2), 1, 2)); // ok + CHECK_EQUAL( 1, f.find_first(T(2.2), 1, 3)); + CHECK_EQUAL( 5, f.find_first(T(4.4), 1)); // defaul end=all + CHECK_EQUAL(-1, f.find_first(T(2.2), 1, 1)); // start=end + CHECK_EQUAL(-1, f.find_first(T(1.1), 1, 4)); // no match .end 1 too little + CHECK_EQUAL( 4, f.find_first(T(1.1), 1, 5)); // skip first match, end at last match + + // Find all + Array resArr; + f.find_all(resArr, T(1.1), 0); + CHECK_EQUAL(2, resArr.Size()); + CHECK_EQUAL(0, resArr.Get(0)); + CHECK_EQUAL(4, resArr.Get(1)); + // Find all, range limited -> no match + resArr.Clear(); + f.find_all(resArr, T(1.1), 0, 1, 4); + CHECK_EQUAL(0, resArr.Size()); + +} +TEST(ArrayFloat_Find) { BasicArray_Find(); } +TEST(ArrayDouble_Find){ BasicArray_Find(); } + + +template +void BasicArray_Count() +{ + C f; + + // Empty list + CHECK_EQUAL(0, f.count(0)); + + // Add some values + // 0 1 2 3 4 5 + T values[] = { T(1.1), T(2.2), T(-1.0), T(5.5), T(1.1), T(4.4)}; + for (size_t i=0; i<6; ++i) { + f.add(values[i]); + } + // count full range + CHECK_EQUAL(0, f.count(T(0.0))); + CHECK_EQUAL(1, f.count(T(4.4))); + CHECK_EQUAL(1, f.count(T(-1.0))); + CHECK_EQUAL(2, f.count(T(1.1))); + + // limited range + CHECK_EQUAL(0, f.count(T(4.4), 0, 5)); + CHECK_EQUAL(1, f.count(T(4.4), 0, 6)); + CHECK_EQUAL(1, f.count(T(4.4), 5, 6)); + + CHECK_EQUAL(0, f.count(T(-1.0), 1, 2)); + CHECK_EQUAL(0, f.count(T(-1.0), 3, 4)); + CHECK_EQUAL(1, f.count(T(-1.0), 2, 4)); + CHECK_EQUAL(1, f.count(T(-1.0), 1)); + +} +TEST(ArrayFloat_Count) { BasicArray_Count(); } +TEST(ArrayDouble_Count){ BasicArray_Count(); } + + +template +void BasicArray_Compare() +{ + C f1, f2; + + // Empty list + CHECK_EQUAL(true, f1.Compare(f2)); + CHECK_EQUAL(true, f2.Compare(f1)); + + // Add some values + T values[] = { T(1.1), T(2.2), T(-1.0), T(5.5), T(1.1), T(4.4)}; + for (size_t i=0; i<6; ++i) { + f1.add(values[i]); + f2.add(values[i]); + CHECK_EQUAL(true, f1.Compare(f2)); + } + + f1.Delete(0); + CHECK_EQUAL(false, f1.Compare(f2)); + + f2.Delete(0); + CHECK_EQUAL(true, f1.Compare(f2)); +} +TEST(ArrayFloat_Compare) { BasicArray_Compare(); } +TEST(ArrayDouble_Compare){ BasicArray_Compare(); } + diff --git a/test/testcolumnfloat.cpp b/test/testcolumnfloat.cpp new file mode 100644 index 00000000000..e24bf8f8755 --- /dev/null +++ b/test/testcolumnfloat.cpp @@ -0,0 +1,221 @@ +#include +#include +#include + +using namespace tightdb; + +template inline +size_t SizeOfArray( const T(&)[ N ] ) +{ + return N; +} + +namespace { +float floatVal[] = {0.0f, + 1.0f, + 2.12345f, + 12345.12f, + -12345.12f + }; +const size_t floatValLen = SizeOfArray(floatVal); + +double doubleVal[] = {0.0, + 1.0, + 2.12345, + 12345.12, + -12345.12 + }; +const size_t doubleValLen = SizeOfArray(doubleVal); + +} + +void printCol(ColumnFloat& c) +{ + for (size_t i=0; i < c.Size(); ++i) { + std::cerr << " Col[" << i << "] = " << c.Get(i) << " \n"; + } +} + + +template +void BasicColumn_IsEmpty() +{ + C c; + CHECK(c.is_empty()); + CHECK_EQUAL(c.Size(), (size_t)0); +} +TEST(ColumnFloat_IsEmpty) { BasicColumn_IsEmpty(); } +TEST(ColumnDouble_IsEmpty){ BasicColumn_IsEmpty(); } + + +template +void BasicColumn_AddGet(T val[], size_t valLen) +{ + C c; + for (size_t i=0; i(floatVal, floatValLen); } +TEST(ColumnDouble_AddGet){ BasicColumn_AddGet(doubleVal, doubleValLen); } + + +template +void BasicColumn_Clear() +{ + C c; + CHECK(c.is_empty()); + + for (size_t i=0; i<100; ++i) + c.add(); + CHECK(!c.is_empty()); + + c.Clear(); + CHECK(c.is_empty()); +} +TEST(ColumnFloat_Clear) { BasicColumn_Clear(); } +TEST(ColumnDouble_Clear){ BasicColumn_Clear(); } + + +template +void BasicColumn_Set(T val[], size_t valLen) +{ + C c; + for (size_t i=0; i(floatVal, floatValLen); } +TEST(ColumnDouble_Set){ BasicColumn_Set(doubleVal, doubleValLen); } + + +template +void BasicColumn_Insert(T val[], size_t valLen) +{ + (void)valLen; + + C c; + + // Insert in empty column + c.Insert(0, val[0]); + CHECK_EQUAL(val[0], c.Get(0)); + CHECK_EQUAL(1, c.Size()); + + // Insert in top + c.Insert(0, val[1]); + CHECK_EQUAL(val[1], c.Get(0)); + CHECK_EQUAL(val[0], c.Get(1)); + CHECK_EQUAL(2, c.Size()); + + // Insert in middle + c.Insert(1, val[2]); + CHECK_EQUAL(val[1], c.Get(0)); + CHECK_EQUAL(val[2], c.Get(1)); + CHECK_EQUAL(val[0], c.Get(2)); + CHECK_EQUAL(3, c.Size()); + + // Insert at buttom + c.Insert(3, val[3]); + CHECK_EQUAL(val[1], c.Get(0)); + CHECK_EQUAL(val[2], c.Get(1)); + CHECK_EQUAL(val[0], c.Get(2)); + CHECK_EQUAL(val[3], c.Get(3)); + CHECK_EQUAL(4, c.Size()); + + // Insert at top + c.Insert(0, val[4]); + CHECK_EQUAL(val[4], c.Get(0)); + CHECK_EQUAL(val[1], c.Get(1)); + CHECK_EQUAL(val[2], c.Get(2)); + CHECK_EQUAL(val[0], c.Get(3)); + CHECK_EQUAL(val[3], c.Get(4)); + CHECK_EQUAL(5, c.Size()); +} +TEST(ColumnFloat_Insert) { BasicColumn_Insert(floatVal, floatValLen); } +TEST(ColumnDouble_Insert){ BasicColumn_Insert(doubleVal, doubleValLen); } + + +template +void BasicColumn_Aggregates(T val[], size_t valLen) +{ + (void)valLen; + (void)val; + + C c; + +// double sum = c.sum(); +// CHECK_EQUAL(0, sum); + + // todo: add tests for minimum, maximum, + // todo !!! + + + +} +TEST(ColumnFloat_Aggregates) { BasicColumn_Aggregates(floatVal, floatValLen); } +TEST(ColumnDouble_Aggregates){ BasicColumn_Aggregates(doubleVal, doubleValLen); } + + +template +void BasicColumn_Delete(T val[], size_t valLen) +{ + C c; + for (size_t i=0; i(floatVal, floatValLen); } +TEST(ColumnDouble_Delete){ BasicColumn_Delete(doubleVal, doubleValLen); } + diff --git a/test/testcolumnmixed.cpp b/test/testcolumnmixed.cpp index f9df6bde7f4..877eb762149 100644 --- a/test/testcolumnmixed.cpp +++ b/test/testcolumnmixed.cpp @@ -1,37 +1,114 @@ #include #include +#include + using namespace tightdb; TEST(ColumnMixed_Int) { ColumnMixed c; + const int64_t maxval = std::numeric_limits::max(); + const int64_t minval = std::numeric_limits::min(); + const int64_t allbit = 0xFFFFFFFFFFFFFFFF; c.insert_int(0, 2); - c.insert_int(1, 100); - c.insert_int(2, 20000); - CHECK_EQUAL(3, c.Size()); + c.insert_int(1, minval); + c.insert_int(2, maxval); + c.insert_int(3, allbit); + CHECK_EQUAL(4, c.Size()); for (size_t i = 0; i < c.Size(); ++i) { CHECK_EQUAL(COLUMN_TYPE_INT, c.GetType(i)); } - CHECK_EQUAL( 2, c.GetInt(0)); - CHECK_EQUAL( 100, c.GetInt(1)); - CHECK_EQUAL(20000, c.GetInt(2)); + CHECK_EQUAL( 2, c.get_int(0)); + CHECK_EQUAL(minval, c.get_int(1)); + CHECK_EQUAL(maxval, c.get_int(2)); + CHECK_EQUAL(allbit, c.get_int(3)); - c.SetInt(0, 400); - c.SetInt(1, 0); - c.SetInt(2, 99999); + c.set_int(0, 400); + c.set_int(1, 0); + c.set_int(2, -99999); + c.set_int(3, 1); for (size_t i = 0; i < c.Size(); ++i) { CHECK_EQUAL(COLUMN_TYPE_INT, c.GetType(i)); } - CHECK_EQUAL( 400, c.GetInt(0)); - CHECK_EQUAL( 0, c.GetInt(1)); - CHECK_EQUAL(99999, c.GetInt(2)); - CHECK_EQUAL(3, c.Size()); + CHECK_EQUAL( 400, c.get_int(0)); + CHECK_EQUAL( 0, c.get_int(1)); + CHECK_EQUAL(-99999, c.get_int(2)); + CHECK_EQUAL( 1, c.get_int(3)); + CHECK_EQUAL(4, c.Size()); + + c.Destroy(); +} + + +TEST(ColumnMixed_Float) +{ + ColumnMixed c; + + const uint32_t v = 0xFFFFFFFF; + const float f = static_cast(v); + float fval1[] = {0.0f, 100.123f, -111.222f, f}; + float fval2[] = {-0.0f, -100.123f, std::numeric_limits::max(), std::numeric_limits::min()}; + + // Test insert + for (size_t i=0; i<4; ++i) + c.insert_float(i, fval1[i]); + CHECK_EQUAL(4, c.Size()); + + for (size_t i = 0; i < c.Size(); ++i) { + CHECK_EQUAL(COLUMN_TYPE_FLOAT, c.GetType(i)); + CHECK_EQUAL( fval1[i], c.get_float(i)); + } + + // Set to new values - ensure sign is changed + for (size_t i=0; i<4; ++i) + c.set_float(i, fval2[i]); + + for (size_t i = 0; i < c.Size(); ++i) { + CHECK_EQUAL(COLUMN_TYPE_FLOAT, c.GetType(i)); + CHECK_EQUAL( fval2[i], c.get_float(i)); + } + CHECK_EQUAL(4, c.Size()); + + c.Destroy(); +} + + +TEST(ColumnMixed_Double) +{ + ColumnMixed c; + + const uint64_t v = 0xFFFFFFFFFFFFFFFF; + const double d = static_cast(v); + double fval1[] = {1.0, 200.123, -111.222, d}; + double fval2[] = {-1.0, -100.123, std::numeric_limits::max(), std::numeric_limits::min()}; + + // Test insert + for (size_t i=0; i<4; ++i) { + c.insert_double(i, fval1[i]); + } + CHECK_EQUAL(4, c.Size()); + + for (size_t i = 0; i < c.Size(); ++i) { + CHECK_EQUAL(COLUMN_TYPE_DOUBLE, c.GetType(i)); + double v = c.get_double(i); + CHECK_EQUAL( fval1[i], v); + } + + // Set to new values - ensure sign is changed + for (size_t i=0; i<4; ++i) + c.set_double(i, fval2[i]); + + CHECK_EQUAL(4, c.Size()); + for (size_t i = 0; i < c.Size(); ++i) { + CHECK_EQUAL(COLUMN_TYPE_DOUBLE, c.GetType(i)); + CHECK_EQUAL( fval2[i], c.get_double(i)); + } c.Destroy(); } @@ -201,24 +278,32 @@ TEST(ColumnMixed_Mixed) c.insert_string(0, "Hello"); c.insert_binary(0, "binary", 7); c.insert_subtable(0); - CHECK_EQUAL(6, c.Size()); - - CHECK_EQUAL(COLUMN_TYPE_TABLE, c.GetType(0)); - CHECK_EQUAL(COLUMN_TYPE_BINARY, c.GetType(1)); - CHECK_EQUAL(COLUMN_TYPE_STRING, c.GetType(2)); - CHECK_EQUAL(COLUMN_TYPE_DATE, c.GetType(3)); - CHECK_EQUAL(COLUMN_TYPE_BOOL, c.GetType(4)); - CHECK_EQUAL(COLUMN_TYPE_INT, c.GetType(5)); + c.insert_float(0, 1.124f); + c.insert_double(0, 1234.124); + CHECK_EQUAL(8, c.Size()); + + CHECK_EQUAL(COLUMN_TYPE_DOUBLE, c.GetType(0)); + CHECK_EQUAL(COLUMN_TYPE_FLOAT, c.GetType(1)); + CHECK_EQUAL(COLUMN_TYPE_TABLE, c.GetType(2)); + CHECK_EQUAL(COLUMN_TYPE_BINARY, c.GetType(3)); + CHECK_EQUAL(COLUMN_TYPE_STRING, c.GetType(4)); + CHECK_EQUAL(COLUMN_TYPE_DATE, c.GetType(5)); + CHECK_EQUAL(COLUMN_TYPE_BOOL, c.GetType(6)); + CHECK_EQUAL(COLUMN_TYPE_INT, c.GetType(7)); // Change all entries to new types - c.SetInt(0, 23); + c.set_int(0, 23); c.set_bool(1, false); c.set_date(2, 23423); c.set_string(3, "Hello"); c.set_binary(4, "binary", 7); c.set_subtable(5); - CHECK_EQUAL(6, c.Size()); + c.set_float(6, 1.124f); + c.set_double(7, 1234.124); + CHECK_EQUAL(8, c.Size()); + CHECK_EQUAL(COLUMN_TYPE_DOUBLE, c.GetType(7)); + CHECK_EQUAL(COLUMN_TYPE_FLOAT, c.GetType(6)); CHECK_EQUAL(COLUMN_TYPE_TABLE, c.GetType(5)); CHECK_EQUAL(COLUMN_TYPE_BINARY, c.GetType(4)); CHECK_EQUAL(COLUMN_TYPE_STRING, c.GetType(3)); diff --git a/test/testtable.cpp b/test/testtable.cpp index 45d2c283803..8c7a7f35764 100644 --- a/test/testtable.cpp +++ b/test/testtable.cpp @@ -47,6 +47,46 @@ TEST(Table1) #endif // TIGHTDB_DEBUG } +TEST(Table_floats) +{ + Table table; + table.add_column(COLUMN_TYPE_FLOAT, "first"); + table.add_column(COLUMN_TYPE_DOUBLE, "second"); + + CHECK_EQUAL(COLUMN_TYPE_FLOAT, table.get_column_type(0)); + CHECK_EQUAL(COLUMN_TYPE_DOUBLE, table.get_column_type(1)); + CHECK_EQUAL("first", table.get_column_name(0)); + CHECK_EQUAL("second", table.get_column_name(1)); + + // Test adding a single empty row + // and filling it with values + size_t ndx = table.add_empty_row(); + table.set_float(0, ndx, float(1.12)); + table.set_double(1, ndx, double(102.13)); + + CHECK_EQUAL(float(1.12), table.get_float(0, ndx)); + CHECK_EQUAL(double(102.13), table.get_double(1, ndx)); + + // Test adding multiple rows + ndx = table.add_empty_row(7); + for (size_t i = ndx; i < 7; ++i) { + table.set_float(0, i, float(1.12) + 100*i); + table.set_double(1, i, double(102.13)*200*i); + } + + for (size_t i = ndx; i < 7; ++i) { + const float v1 = float(1.12) + 100*i; + const double v2 = double(102.13)*200*i; + CHECK_EQUAL(v1, table.get_float(0, i)); + CHECK_EQUAL(v2, table.get_double(1, i)); + } + +#ifdef TIGHTDB_DEBUG + table.Verify(); +#endif // TIGHTDB_DEBUG +} + +namespace { enum Days { Mon, Tue, @@ -62,6 +102,7 @@ TIGHTDB_TABLE_4(TestTable, second, Int, third, Bool, fourth, Enum) +} TEST(Table2) { @@ -108,10 +149,11 @@ TEST(Table3) #endif // TIGHTDB_DEBUG } +namespace { TIGHTDB_TABLE_2(TestTableEnum, first, Enum, second, String) - +} TEST(Table4) { TestTableEnum table; @@ -132,6 +174,29 @@ TEST(Table4) #endif // TIGHTDB_DEBUG } +namespace { +TIGHTDB_TABLE_2(TestTableFloats, + first, Float, + second, Double) +} + +TEST(Table_float2) +{ + TestTableFloats table; + + table.add(1.1f, 2.2); + table.add(1.1f, 2.2); + const TestTableFloats::Cursor r = table.back(); // last item + + CHECK_EQUAL(1.1f, r.first); + CHECK_EQUAL(2.2, r.second); + +#ifdef TIGHTDB_DEBUG + table.Verify(); +#endif // TIGHTDB_DEBUG +} + + TEST(Table_Delete) { TestTable table; @@ -351,6 +416,7 @@ TEST(Table_test_json_simple) s.add_column(COLUMN_TYPE_INT, "int"); s.add_column(COLUMN_TYPE_BOOL, "bool"); s.add_column(COLUMN_TYPE_DATE, "date"); + // FIXME: Add float, double s.add_column(COLUMN_TYPE_STRING, "string"); s.add_column(COLUMN_TYPE_BINARY, "binary"); table.update_from_spec(); @@ -521,10 +587,11 @@ TEST(Table_Index_String) CHECK_EQUAL(2, c1); } +namespace { TIGHTDB_TABLE_2(LookupTable, first, String, second, Int) - +} TEST(Table_Lookup) { LookupTable table; @@ -689,12 +756,13 @@ TEST(Table_Index_Int) } */ +namespace { TIGHTDB_TABLE_4(TestTableAE, first, Int, second, String, third, Bool, fourth, Enum) - +} TEST(TableAutoEnumeration) { TestTableAE table; @@ -1327,14 +1395,48 @@ TEST(Table_Mixed) CHECK_EQUAL("John", subtable2->get_string(0, 0)); CHECK_EQUAL(40, subtable2->get_int(1, 0)); + // Insert float, double + table.insert_int(0, 6, 31); + table.insert_mixed(1, 6, float(1.123)); + table.insert_done(); + table.insert_int(0, 7, 0); + table.insert_mixed(1, 7, double(2.234)); + table.insert_done(); + + CHECK_EQUAL(0, table.get_int(0, 0)); + CHECK_EQUAL(43, table.get_int(0, 1)); + CHECK_EQUAL(0, table.get_int(0, 3)); + CHECK_EQUAL(43, table.get_int(0, 4)); + CHECK_EQUAL(0, table.get_int(0, 5)); + CHECK_EQUAL(31, table.get_int(0, 6)); + CHECK_EQUAL(0, table.get_int(0, 7)); + CHECK_EQUAL(COLUMN_TYPE_BOOL, table.get_mixed(1, 0).get_type()); + CHECK_EQUAL(COLUMN_TYPE_INT, table.get_mixed(1, 1).get_type()); + CHECK_EQUAL(COLUMN_TYPE_STRING, table.get_mixed(1, 2).get_type()); + CHECK_EQUAL(COLUMN_TYPE_DATE, table.get_mixed(1, 3).get_type()); + CHECK_EQUAL(COLUMN_TYPE_BINARY, table.get_mixed(1, 4).get_type()); + CHECK_EQUAL(COLUMN_TYPE_TABLE, table.get_mixed(1, 5).get_type()); + CHECK_EQUAL(COLUMN_TYPE_FLOAT, table.get_mixed(1, 6).get_type()); + CHECK_EQUAL(COLUMN_TYPE_DOUBLE, table.get_mixed(1, 7).get_type()); + CHECK_EQUAL(true, table.get_mixed(1, 0).get_bool()); + CHECK_EQUAL(12, table.get_mixed(1, 1).get_int()); + CHECK_EQUAL("test", table.get_mixed(1, 2).get_string()); + CHECK_EQUAL(324234, table.get_mixed(1, 3).get_date()); + CHECK_EQUAL("binary", (const char*)table.get_mixed(1, 4).get_binary().pointer); + CHECK_EQUAL(7, table.get_mixed(1, 4).get_binary().len); + CHECK_EQUAL(float(1.123), table.get_mixed(1, 6).get_float()); + CHECK_EQUAL(double(2.234), table.get_mixed(1, 7).get_double()); + #ifdef TIGHTDB_DEBUG table.Verify(); #endif // TIGHTDB_DEBUG } + +namespace { TIGHTDB_TABLE_1(TestTableMX, first, Mixed) - +} TEST(Table_Mixed2) { @@ -1692,6 +1794,63 @@ TEST(Table_HasSharedSpec) CHECK(table4[0].subtab->has_shared_spec()); } + +namespace +{ + TIGHTDB_TABLE_3(TableAgg, + c_int, Int, + c_float, Float, + c_double, Double) + + // TODO: Bool? Date +} + +#if TEST_DURATION > 0 +#define TBL_SIZE MAX_LIST_SIZE*10 +#else +#define TBL_SIZE 10 +#endif + +TEST(Table_Aggregates) +{ + TableAgg table; + int64_t i_sum = 0; + float f_sum = 0; + double d_sum = 0; + + for (int i = 0; i < TBL_SIZE; i++) { + table.add(5987654, 4.0f, 3.0); + i_sum += 5987654; + f_sum += 4.0f; + d_sum += 3.0; + } + table.add(1, 1.1f, 1.2); + table.add(987654321, 11.0f, 12.0); + table.add(5, 4.0f, 3.0); + i_sum += 1 + 987654321 + 5; + f_sum += 1.1f + 11.0f + 4.0f; + d_sum += 1.2 + 12.0 + 3.0; + double size = TBL_SIZE + 3; + + // minimum + CHECK_EQUAL(1, table.column().c_int.minimum()); + CHECK_EQUAL(1.1f, table.column().c_float.minimum()); + CHECK_EQUAL(1.2, table.column().c_double.minimum()); + // maximum + CHECK_EQUAL(987654321, table.column().c_int.maximum()); + CHECK_EQUAL(11.0f, table.column().c_float.maximum()); + CHECK_EQUAL(12.0, table.column().c_double.maximum()); + // sum + CHECK_EQUAL(i_sum, table.column().c_int.sum()); + CHECK_EQUAL(f_sum, table.column().c_float.sum()); + CHECK_EQUAL(d_sum, table.column().c_double.sum()); + // average + CHECK_EQUAL(double(i_sum)/size, table.column().c_int.average()); + CHECK_EQUAL(double(f_sum)/size, table.column().c_float.average()); + CHECK_EQUAL(double(d_sum)/size, table.column().c_double.average()); +} + + #include TEST(Table_LanguageBindings) diff --git a/test/testtransactions_lasse.cpp b/test/testtransactions_lasse.cpp index 78a4213e5d1..fd890e816cc 100644 --- a/test/testtransactions_lasse.cpp +++ b/test/testtransactions_lasse.cpp @@ -5,12 +5,6 @@ #include #include -#ifdef _WIN32 -#include -#else -#include -#endif - #include #include @@ -20,6 +14,13 @@ #include "testsettings.hpp" +#ifdef _WIN32 +#define NOMINMAX +#include +#else +#include +#endif + using namespace std; using namespace tightdb; @@ -337,7 +338,7 @@ TEST(Transactions_Stress3) for(size_t t = 0; t < READERS3; t++) pthread_join(read_threads3[t], NULL); } - +#endif #ifdef STRESSTEST4 @@ -438,4 +439,4 @@ TEST(Transactions_Stress4) #endif -#endif + diff --git a/tightdb.xcodeproj/project.pbxproj b/tightdb.xcodeproj/project.pbxproj index 406792ca05d..e8c1c19261b 100644 --- a/tightdb.xcodeproj/project.pbxproj +++ b/tightdb.xcodeproj/project.pbxproj @@ -491,6 +491,16 @@ 36D6FC3915E3813700B02534 /* terminate.cpp */, 36D6FC3A15E3813700B02534 /* terminate.hpp */, 365CCDF1157CC37D00172BF8 /* alloc_slab.cpp */, + 36D6FC3115E3813700B02534 /* assert.hpp */, + 36D6FC3215E3813700B02534 /* pthread_helpers.hpp */, + 36D6FC3315E3813700B02534 /* replication.cpp */, + 36D6FC3415E3813700B02534 /* replication.hpp */, + 36D6FC3515E3813700B02534 /* safe_int_ops.hpp */, + 36D6FC3615E3813700B02534 /* string_buffer.cpp */, + 36D6FC3715E3813700B02534 /* string_buffer.hpp */, + 36D6FC3815E3813700B02534 /* table_basic_fwd.hpp */, + 36D6FC3915E3813700B02534 /* terminate.cpp */, + 36D6FC3A15E3813700B02534 /* terminate.hpp */, 365CCDF2157CC37D00172BF8 /* alloc_slab.hpp */, 365CCDF3157CC37D00172BF8 /* alloc.hpp */, 365CCDF4157CC37D00172BF8 /* array_binary.cpp */,