#include #include #include #include #include #include #include #include namespace { #ifndef UINT64_C #define UINT64_C(x) ((uint64_t)x) #endif using namespace jellyfish; // Tuple is {key_len, val_len, reprobe_len}. Actual reprobe value is computed from the reprobe_len. class ComputeOffsetsTest : public ::testing::TestWithParam< ::std::tr1::tuple > { public: Offsets offsets; ComputeOffsetsTest() : offsets(::std::tr1::get<0>(GetParam()), ::std::tr1::get<1>(GetParam()), (1 << ::std::tr1::get<2>(GetParam())) - 2) { } ~ComputeOffsetsTest() { } }; void test_key_offsets(const Offsets::offset_t* it, unsigned int k_len, const char* message) { SCOPED_TRACE(message); unsigned int val_boff = (it->key.boff + k_len + 1 + (k_len - (bsizeof(uint64_t) - it->key.boff - 1)) / (bsizeof(uint64_t) - 1)) % bsizeof(uint64_t); if(it->key.sb_mask1) { // key between two words EXPECT_LT(sizeof(uint64_t), it->key.boff + k_len) << ": Key not between words, should not have mask2"; EXPECT_EQ(val_boff + (val_boff > 0), it->val.boff) << ": Invalid value offset"; if(it->val.boff == 0) EXPECT_EQ((uint64_t)0, it->key.sb_mask2); } else { if(bsizeof(uint64_t) >= it->key.boff + k_len) { EXPECT_EQ((it->key.boff + k_len) % bsizeof(uint64_t), it->val.boff) << ": Invalid value offset"; } else { EXPECT_TRUE(val_boff % bsizeof(uint64_t) == 0) << ": Key between words, should have mask2"; } } EXPECT_EQ((uint64_t)1 << (it->key.boff - 1), it->key.lb_mask) << ": Invalid lb_mask"; if(it->key.sb_mask1) { EXPECT_EQ(bsizeof(uint64_t) - it->key.boff - 1, it->key.shift) << ": invalid key shift"; EXPECT_EQ(it->key.boff == 1 ? 0 : (uint64_t)1 << (bsizeof(uint64_t) - it->key.boff + 1), 1 + (it->key.mask1 >> (it->key.boff - 1))) << ": invalid key mask"; EXPECT_EQ((uint64_t)1 << it->val.boff, 1 + it->key.mask2) << ": invalid key mask2"; EXPECT_EQ(bsizeof(uint64_t) - it->key.boff - 1, it->key.shift) << ": invalid key shift"; EXPECT_EQ((unsigned int)0, (k_len - (it->key.shift + it->key.cshift)) % (bsizeof(uint64_t) - 1)) << ": invalid sum of shift and cshift"; if(it->key.sb_mask2) EXPECT_EQ(1 + it->key.mask2, (uint64_t)1 << (it->key.cshift + 1)) << ": invalid key cshift"; else EXPECT_EQ((unsigned int)0, it->key.cshift); EXPECT_EQ((uint64_t)1 << 63, it->key.sb_mask1) << ": invalid key key sb_mask1"; EXPECT_EQ(k_len - (bsizeof(uint64_t) - 1 - it->key.boff) >= bsizeof(uint64_t) - 1, it->key.full_words); } else { EXPECT_EQ(0u, it->key.shift) << ": key shift should be zero, no sb_mask"; EXPECT_EQ((uint64_t)1 << (k_len + 1), 1 + (it->key.mask1 >> (it->key.boff - 1))) << ": invalid key mask, no sb_mask"; EXPECT_EQ(0u, it->key.cshift) << ": key cshift should be zero, no sb_mask"; EXPECT_EQ(0u, it->key.sb_mask1 | it->key.sb_mask2) << ": set bit masks should be 0"; } } void test_val_offsets(const Offsets::offset_t* it, unsigned int v_len, const char* message) { SCOPED_TRACE(message); if(it->val.mask2) { EXPECT_TRUE(bsizeof(uint64_t) <= it->val.boff + v_len && (it->val.boff + v_len) % bsizeof(uint64_t) != 0) << ": val not between words, should not have mask2"; EXPECT_EQ(bsizeof(uint64_t) - it->val.boff, it->val.shift) << ": invalid val shift"; EXPECT_EQ((uint64_t)1 << (bsizeof(uint64_t) - it->val.boff), 1 + (it->val.mask1 >> it->val.boff)) << ": invalid val mask1"; EXPECT_EQ((uint64_t)1 << (it->val.boff + v_len - bsizeof(uint64_t)), 1 + it->val.mask2) << ": invalid val mask2"; } else { EXPECT_TRUE(bsizeof(uint64_t) > it->val.boff + v_len || (bsizeof(uint64_t) <= it->val.boff + v_len && (it->val.boff + v_len) % bsizeof(uint64_t) == 0)) << ": val between words, should have mask2"; EXPECT_EQ(v_len, it->val.shift) << ": invalid val shift"; EXPECT_EQ(v_len == bsizeof(uint64_t) ? (uint64_t)0 : (uint64_t)1 << v_len, 1 + (it->val.mask1 >> it->val.boff)) << ": invalid val mask1"; } EXPECT_EQ(v_len, it->val.shift + it->val.cshift); } TEST_P(ComputeOffsetsTest, CheckCoherency) { const Offsets::offset_t *it = NULL, *pit = NULL; const Offsets::offset_t *lit = NULL, *lpit = NULL; unsigned int k_len = ::std::tr1::get<0>(GetParam()); unsigned int v_len = ::std::tr1::get<1>(GetParam()); unsigned int kv_len = k_len + v_len; unsigned int lk_len = ::std::tr1::get<2>(GetParam()); unsigned int lv_len = std::min(kv_len - lk_len, (unsigned int)bsizeof(uint64_t)); unsigned int i = 0; EXPECT_EQ(lk_len, this->offsets.reprobe_len()); EXPECT_EQ((uint64_t)1 << lk_len, this->offsets.reprobe_mask() + 1); EXPECT_EQ(lv_len, this->offsets.lval_len()); for(i = 0; i < bsizeof(uint64_t); i++) { SCOPED_TRACE(::testing::Message() << "key:" << k_len << " val:" << v_len << " i:" << i << " lkey:" << lk_len << " lval:" << lv_len); this->offsets.word_offset(i, &it, &lit, NULL); if(i > 0) { this->offsets.word_offset(i-1, &pit, &lpit, NULL); EXPECT_GE(it->key.woff, pit->key.woff) << "Current key offset larger than previous"; EXPECT_GE(it->key.woff, pit->val.woff) << "Current key offset larger than previous val offset"; EXPECT_TRUE(it->key.woff != pit->key.woff || it->key.boff > pit->key.boff) << "Key bit offset did not increase within a word"; EXPECT_EQ((pit->val.boff + v_len) % 64 + 1, it->key.boff) << ": Invalid jump of bit offset"; } // if(i > 0) EXPECT_GE(it->val.woff, it->key.woff) << "Val offset larger than key offset"; EXPECT_TRUE(it->key.woff != it->val.woff || it->val.boff > it->key.boff) << "Val bit offset did not increase within a word"; EXPECT_EQ(it->key.woff, lit->key.woff); EXPECT_EQ(it->key.boff, lit->key.boff); EXPECT_EQ(it->key.lb_mask, lit->key.lb_mask); test_key_offsets(it, k_len, "Key offsets"); test_key_offsets(lit, lk_len, "Large key offsets"); test_val_offsets(it, v_len, "Val offsets"); test_val_offsets(lit, lv_len, "Large val offsets"); size_t next_bit = (it->val.boff + v_len) % bsizeof(uint64_t); if(next_bit >= 62 || next_bit == 0) break; } EXPECT_EQ(i + 1, this->offsets.block_len()); } INSTANTIATE_TEST_CASE_P(OffsetsTest, ComputeOffsetsTest, ::testing::Combine(::testing::Range(8, 192, 2), // Key lengths ::testing::Range(0, 64), // Val lengths ::testing::Range(4, 9) // Reprobe lengths )); } // namespace {