#include #include #include #include #include using namespace NFlatHash; namespace { constexpr size_t INIT_SIZE = 128; struct TDummy { static size_t Count; TDummy() { ++Count; } TDummy(const TDummy&) { ++Count; } ~TDummy() { --Count; } }; size_t TDummy::Count = 0; struct TAlmostDummy { static size_t Count; TAlmostDummy(int j = 0) : Junk(j) { ++Count; } TAlmostDummy(const TAlmostDummy& d) : Junk(d.Junk) { ++Count; } ~TAlmostDummy() { --Count; } bool operator==(const TAlmostDummy& r) const { return Junk == r.Junk; }; bool operator!=(const TAlmostDummy& r) const { return !operator==(r); }; int Junk; }; size_t TAlmostDummy::Count = 0; struct TNotSimple { enum class EType { Value, Empty, Deleted } Type_ = EType::Value; TString Junk = "something"; // to prevent triviality propagation int Value = 0; static int CtorCalls; static int DtorCalls; static int CopyCtorCalls; static int MoveCtorCalls; TNotSimple() { ++CtorCalls; } explicit TNotSimple(int value) : Value(value) { ++CtorCalls; } TNotSimple(const TNotSimple& rhs) { ++CopyCtorCalls; Value = rhs.Value; Type_ = rhs.Type_; } TNotSimple(TNotSimple&& rhs) { ++MoveCtorCalls; Value = rhs.Value; Type_ = rhs.Type_; } ~TNotSimple() { ++DtorCalls; } TNotSimple& operator=(const TNotSimple& rhs) { ++CopyCtorCalls; Value = rhs.Value; Type_ = rhs.Type_; return *this; } TNotSimple& operator=(TNotSimple&& rhs) { ++MoveCtorCalls; Value = rhs.Value; Type_ = rhs.Type_; return *this; } static TNotSimple Empty() { TNotSimple ret; ret.Type_ = EType::Empty; return ret; } static TNotSimple Deleted() { TNotSimple ret; ret.Type_ = EType::Deleted; return ret; } bool operator==(const TNotSimple& rhs) const noexcept { return Value == rhs.Value; } static void ResetStats() { CtorCalls = 0; DtorCalls = 0; CopyCtorCalls = 0; MoveCtorCalls = 0; } }; int TNotSimple::CtorCalls = 0; int TNotSimple::DtorCalls = 0; int TNotSimple::CopyCtorCalls = 0; int TNotSimple::MoveCtorCalls = 0; struct TNotSimpleEmptyMarker { using value_type = TNotSimple; value_type Create() const { return TNotSimple::Empty(); } bool Equals(const value_type& rhs) const { return rhs.Type_ == TNotSimple::EType::Empty; } }; struct TNotSimpleDeletedMarker { using value_type = TNotSimple; value_type Create() const { return TNotSimple::Deleted(); } bool Equals(const value_type& rhs) const { return rhs.Type_ == TNotSimple::EType::Deleted; } }; template void CheckContainersEqual(const Container& a, const Container& b) { UNIT_ASSERT_EQUAL(a.Size(), b.Size()); UNIT_ASSERT_EQUAL(a.Taken(), b.Empty()); for (typename Container::size_type i = 0; i < a.Size(); ++i) { if (a.IsTaken(i)) { UNIT_ASSERT(b.IsTaken(i)); UNIT_ASSERT_EQUAL(a.Node(i), b.Node(i)); } } } template void SmokingTest(typename Container::size_type size, Args&&... args) { using size_type = typename Container::size_type; using value_type = typename Container::value_type; Container cont(size, std::forward(args)...); UNIT_ASSERT_EQUAL(cont.Size(), size); UNIT_ASSERT_EQUAL(cont.Taken(), 0); for (size_type i = 0; i < cont.Size(); ++i) { UNIT_ASSERT(cont.IsEmpty(i)); UNIT_ASSERT(!cont.IsTaken(i)); } // Filling the container till half TVector toInsert(cont.Size()); Iota(toInsert.begin(), toInsert.end(), 0); Shuffle(toInsert.begin(), toInsert.end()); toInsert.resize(toInsert.size() / 2); for (auto i : toInsert) { UNIT_ASSERT(cont.IsEmpty(i)); UNIT_ASSERT(!cont.IsTaken(i)); value_type value(RandomNumber(cont.Size())); cont.InitNode(i, value); UNIT_ASSERT(!cont.IsEmpty(i)); UNIT_ASSERT(cont.IsTaken(i)); UNIT_ASSERT_EQUAL(cont.Node(i), value); } UNIT_ASSERT_EQUAL(cont.Taken(), toInsert.size()); // Copy construction test auto cont2 = cont; CheckContainersEqual(cont, cont2); // Copy assignment test cont2 = cont2.Clone(0); UNIT_ASSERT_EQUAL(cont2.Size(), 0); UNIT_ASSERT_EQUAL(cont2.Taken(), 0); // Copy assignment test cont2 = cont; CheckContainersEqual(cont, cont2); // Move construction test auto cont3 = std::move(cont2); UNIT_ASSERT_EQUAL(cont2.Size(), 0); CheckContainersEqual(cont, cont3); // Move assignment test cont2 = std::move(cont3); UNIT_ASSERT_EQUAL(cont3.Size(), 0); CheckContainersEqual(cont, cont2); } } Y_UNIT_TEST_SUITE(TFlatContainerTest) { Y_UNIT_TEST(SmokingTest) { SmokingTest>(INIT_SIZE); } Y_UNIT_TEST(SmokingTestNotSimpleType) { TNotSimple::ResetStats(); SmokingTest>(INIT_SIZE); UNIT_ASSERT_EQUAL(TNotSimple::CtorCalls + TNotSimple::CopyCtorCalls + TNotSimple::MoveCtorCalls, TNotSimple::DtorCalls); UNIT_ASSERT_EQUAL(TNotSimple::CtorCalls, INIT_SIZE / 2 /* created while filling */); UNIT_ASSERT_EQUAL(TNotSimple::DtorCalls, INIT_SIZE / 2 /* removed filling temporary */ + INIT_SIZE / 2 /* removed while cloning */ + INIT_SIZE /* 3 containers dtors */); UNIT_ASSERT_EQUAL(TNotSimple::CopyCtorCalls, INIT_SIZE / 2 /* 3 created while filling */ + INIT_SIZE / 2 /* created while copy constructing */ + INIT_SIZE / 2/* created while copy assigning */); UNIT_ASSERT_EQUAL(TNotSimple::MoveCtorCalls, 0); } Y_UNIT_TEST(DummyHalfSizeTest) { using TContainer = TFlatContainer; using size_type = typename TContainer::size_type; { TContainer cont(INIT_SIZE); UNIT_ASSERT_EQUAL(TDummy::Count, 0); TVector toInsert(cont.Size()); Iota(toInsert.begin(), toInsert.end(), 0); Shuffle(toInsert.begin(), toInsert.end()); toInsert.resize(toInsert.size() / 2); for (auto i : toInsert) { UNIT_ASSERT(cont.IsEmpty(i)); UNIT_ASSERT(!cont.IsTaken(i)); cont.InitNode(i); UNIT_ASSERT_EQUAL(TDummy::Count, cont.Taken()); UNIT_ASSERT(!cont.IsEmpty(i)); UNIT_ASSERT(cont.IsTaken(i)); } UNIT_ASSERT_EQUAL(cont.Taken(), cont.Size() / 2); UNIT_ASSERT_EQUAL(TDummy::Count, cont.Taken()); } UNIT_ASSERT_EQUAL(TDummy::Count, 0); } Y_UNIT_TEST(DeleteTest) { using TContainer = TFlatContainer; using size_type = typename TContainer::size_type; TContainer cont(INIT_SIZE); auto idx = RandomNumber(INIT_SIZE); UNIT_ASSERT(!cont.IsTaken(idx)); UNIT_ASSERT(!cont.IsDeleted(idx)); UNIT_ASSERT_EQUAL(TDummy::Count, 0); cont.InitNode(idx); UNIT_ASSERT_EQUAL(cont.Taken(), 1); UNIT_ASSERT(cont.IsTaken(idx)); UNIT_ASSERT(!cont.IsDeleted(idx)); UNIT_ASSERT_EQUAL(TDummy::Count, 1); cont.DeleteNode(idx); UNIT_ASSERT(!cont.IsTaken(idx)); UNIT_ASSERT(cont.IsDeleted(idx)); UNIT_ASSERT_EQUAL(TDummy::Count, 0); } } Y_UNIT_TEST_SUITE(TDenseContainerTest) { Y_UNIT_TEST(SmokingTest) { SmokingTest>>(INIT_SIZE); } Y_UNIT_TEST(NotSimpleTypeSmokingTest) { TNotSimple::ResetStats(); SmokingTest>(INIT_SIZE); UNIT_ASSERT_EQUAL(TNotSimple::CtorCalls + TNotSimple::CopyCtorCalls + TNotSimple::MoveCtorCalls, TNotSimple::DtorCalls); UNIT_ASSERT_EQUAL(TNotSimple::CtorCalls, INIT_SIZE / 2 /* created while filling */ + 2 /* created by empty marker */); UNIT_ASSERT_EQUAL(TNotSimple::DtorCalls, 1 /* removed empty marker temporary */ + INIT_SIZE /* half removed while resetting in container, half removed inserted temporary */ + INIT_SIZE /* removed while cloning */ + 1 /* removed empty marker temporary */ + INIT_SIZE * 2 /* 3 containers dtors */); UNIT_ASSERT_EQUAL(TNotSimple::CopyCtorCalls, INIT_SIZE /* created while constructing */ + INIT_SIZE / 2 /* 3 created while filling */ + INIT_SIZE /* created while copy constructing */ + INIT_SIZE /* created while copy assigning */); UNIT_ASSERT_EQUAL(TNotSimple::MoveCtorCalls, 0); } Y_UNIT_TEST(RemovalContainerSmokingTest) { SmokingTest, NSet::TStaticValueMarker<-2>>>(INIT_SIZE); } Y_UNIT_TEST(NotSimpleTypeRemovalContainerSmokingTest) { TNotSimple::ResetStats(); SmokingTest>(INIT_SIZE); UNIT_ASSERT_EQUAL(TNotSimple::CtorCalls + TNotSimple::CopyCtorCalls + TNotSimple::MoveCtorCalls, TNotSimple::DtorCalls); UNIT_ASSERT_EQUAL(TNotSimple::CtorCalls, INIT_SIZE / 2 /* created while filling */ + 2 /* created by empty marker */); UNIT_ASSERT_EQUAL(TNotSimple::DtorCalls, 1 /* removed empty marker temporary */ + INIT_SIZE /* half removed while resetting in container, half removed inserted temporary */ + INIT_SIZE /* removed while cloning */ + 1 /* removed empty marker temporary */ + INIT_SIZE * 2 /* 3 containers dtors */); UNIT_ASSERT_EQUAL(TNotSimple::CopyCtorCalls, INIT_SIZE /* created while constructing */ + INIT_SIZE / 2 /* 3 created while filling */ + INIT_SIZE /* created while copy constructing */ + INIT_SIZE /* created while copy assigning */); UNIT_ASSERT_EQUAL(TNotSimple::MoveCtorCalls, 0); } Y_UNIT_TEST(DummyHalfSizeTest) { using TContainer = TDenseContainer>; using size_type = typename TContainer::size_type; { TContainer cont(INIT_SIZE, TAlmostDummy{-1}); UNIT_ASSERT_EQUAL(TAlmostDummy::Count, cont.Size() + 1); // 1 for empty marker TVector toInsert(cont.Size()); Iota(toInsert.begin(), toInsert.end(), 0); Shuffle(toInsert.begin(), toInsert.end()); toInsert.resize(toInsert.size() / 2); for (auto i : toInsert) { UNIT_ASSERT(cont.IsEmpty(i)); UNIT_ASSERT(!cont.IsTaken(i)); cont.InitNode(i, (int)RandomNumber(cont.Size())); UNIT_ASSERT_EQUAL(TAlmostDummy::Count, cont.Size() + 1); UNIT_ASSERT(!cont.IsEmpty(i)); UNIT_ASSERT(cont.IsTaken(i)); } UNIT_ASSERT_EQUAL(cont.Taken(), toInsert.size()); UNIT_ASSERT_EQUAL(TAlmostDummy::Count, cont.Size() + 1); } UNIT_ASSERT_EQUAL(TAlmostDummy::Count, 0); } Y_UNIT_TEST(DeleteTest) { using TContainer = TRemovalDenseContainer, NSet::TEqValueMarker>; using size_type = typename TContainer::size_type; TContainer cont(INIT_SIZE, TAlmostDummy{ -2 }, TAlmostDummy{ -1 }); auto idx = RandomNumber(cont.Size()); UNIT_ASSERT(!cont.IsTaken(idx)); UNIT_ASSERT(!cont.IsDeleted(idx)); UNIT_ASSERT_EQUAL(TAlmostDummy::Count, cont.Size() + 2); // 2 for markers cont.InitNode(idx, (int)RandomNumber(cont.Size())); UNIT_ASSERT_EQUAL(cont.Taken(), 1); UNIT_ASSERT(cont.IsTaken(idx)); UNIT_ASSERT(!cont.IsDeleted(idx)); UNIT_ASSERT_EQUAL(TAlmostDummy::Count, cont.Size() + 2); cont.DeleteNode(idx); UNIT_ASSERT(!cont.IsTaken(idx)); UNIT_ASSERT(cont.IsDeleted(idx)); UNIT_ASSERT_EQUAL(TAlmostDummy::Count, cont.Size() + 2); } Y_UNIT_TEST(FancyInitsTest) { { using TContainer = TDenseContainer; TContainer cont{ INIT_SIZE, -1 }; } { using TContainer = TDenseContainer>; TContainer cont{ INIT_SIZE }; static_assert(!std::is_constructible_v); } { using TContainer = TDenseContainer>; TContainer cont{ INIT_SIZE, -1 }; TContainer cont2{ INIT_SIZE, NSet::TEqValueMarker{ -1 } }; } { using TContainer = TRemovalDenseContainer; TContainer cont{ INIT_SIZE, -1, -2 }; TContainer cont2{ INIT_SIZE, NSet::TEqValueMarker{ -1 }, NSet::TEqValueMarker{ -2 } }; } { using TContainer = TRemovalDenseContainer, NSet::TStaticValueMarker<-1>>; TContainer cont{ INIT_SIZE }; static_assert(!std::is_constructible_v); static_assert(!std::is_constructible_v); } } }