0cc8da4c09
It didn't quite make sense before that maxsize == 0 meant no cleanup,
and now with items optionally being count as having size more than 1
it makes even less sense to limit the size to max_size() of the
containers. This comes from 16a338e173
,
so instead make that special caller pass a large size, which in
practice is the same.
Change-Id: Id875862126200ba889211f6e4079ae5921f27650
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133690
Tested-by: Jenkins
Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
338 lines
10 KiB
C++
338 lines
10 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
*/
|
|
|
|
#include <cppunit/TestAssert.h>
|
|
#include <cppunit/TestFixture.h>
|
|
#include <cppunit/extensions/HelperMacros.h>
|
|
|
|
#include <o3tl/lru_map.hxx>
|
|
|
|
#include <o3tl/hash_combine.hxx>
|
|
|
|
using namespace ::o3tl;
|
|
|
|
class lru_map_test : public CppUnit::TestFixture
|
|
{
|
|
public:
|
|
void testBaseUsage();
|
|
void testReplaceKey();
|
|
void testReplaceValue();
|
|
void testLruRemoval();
|
|
void testCustomHash();
|
|
void testRemoveIf();
|
|
void testChangeMaxSize();
|
|
void testCustomItemSize();
|
|
|
|
CPPUNIT_TEST_SUITE(lru_map_test);
|
|
CPPUNIT_TEST(testBaseUsage);
|
|
CPPUNIT_TEST(testReplaceKey);
|
|
CPPUNIT_TEST(testReplaceValue);
|
|
CPPUNIT_TEST(testLruRemoval);
|
|
CPPUNIT_TEST(testCustomHash);
|
|
CPPUNIT_TEST(testRemoveIf);
|
|
CPPUNIT_TEST(testChangeMaxSize);
|
|
CPPUNIT_TEST(testCustomItemSize);
|
|
CPPUNIT_TEST_SUITE_END();
|
|
};
|
|
|
|
void lru_map_test::testBaseUsage()
|
|
{
|
|
o3tl::lru_map<int, int> lru(10);
|
|
lru.insert(std::make_pair<int, int>(1, 1));
|
|
|
|
std::pair<int, int> pair;
|
|
for (int i = 2; i < 7; i++)
|
|
{
|
|
pair.first = pair.second = i;
|
|
lru.insert(pair);
|
|
}
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(6), lru.size());
|
|
|
|
o3tl::lru_map<int, int>::const_iterator it;
|
|
|
|
it = lru.find(2);
|
|
CPPUNIT_ASSERT(it != lru.end());
|
|
CPPUNIT_ASSERT_EQUAL(2, it->second);
|
|
|
|
it = lru.find(5);
|
|
CPPUNIT_ASSERT(it != lru.end());
|
|
CPPUNIT_ASSERT_EQUAL(5, it->second);
|
|
|
|
it = lru.find(0);
|
|
CPPUNIT_ASSERT(bool(it == lru.end()));
|
|
}
|
|
|
|
void lru_map_test::testReplaceValue()
|
|
{
|
|
o3tl::lru_map<int, int> lru(2);
|
|
// check if map is empty
|
|
CPPUNIT_ASSERT_EQUAL(size_t(0), lru.size());
|
|
|
|
// check if inserting entry with same key replaces the value
|
|
|
|
// inserting new entry
|
|
lru.insert(std::make_pair<int, int>(1, 2));
|
|
CPPUNIT_ASSERT_EQUAL(size_t(1), lru.size());
|
|
CPPUNIT_ASSERT_EQUAL(2, lru.find(1)->second);
|
|
|
|
// inserting new entry with key that already exists
|
|
lru.insert(std::make_pair<int, int>(1, 4));
|
|
CPPUNIT_ASSERT_EQUAL(size_t(1), lru.size());
|
|
CPPUNIT_ASSERT_EQUAL(4, lru.find(1)->second);
|
|
|
|
// inserting new entry
|
|
lru.insert(std::make_pair<int, int>(2, 200));
|
|
CPPUNIT_ASSERT_EQUAL(size_t(2), lru.size());
|
|
CPPUNIT_ASSERT_EQUAL(4, lru.find(1)->second);
|
|
CPPUNIT_ASSERT_EQUAL(200, lru.find(2)->second);
|
|
|
|
// check if insert with same key, moves the entry back of the lru queue
|
|
|
|
// inserting new entry with key that already exists
|
|
lru.insert(std::make_pair<int, int>(1, 6));
|
|
// inserting new entry, lru removed
|
|
lru.insert(std::make_pair<int, int>(3, 300));
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(2), lru.size());
|
|
CPPUNIT_ASSERT_EQUAL(6, lru.find(1)->second);
|
|
CPPUNIT_ASSERT_EQUAL(300, lru.find(3)->second);
|
|
}
|
|
|
|
void lru_map_test::testReplaceKey()
|
|
{
|
|
o3tl::lru_map<int, int> lru(2);
|
|
|
|
// inserting new entry
|
|
lru.insert(std::make_pair<int, int>(1, 100));
|
|
CPPUNIT_ASSERT_EQUAL(size_t(1), lru.size());
|
|
CPPUNIT_ASSERT_EQUAL(100, lru.find(1)->second);
|
|
CPPUNIT_ASSERT(bool(lru.find(2) == lru.end()));
|
|
CPPUNIT_ASSERT(bool(lru.find(3) == lru.end()));
|
|
|
|
// inserting new entry
|
|
lru.insert(std::make_pair<int, int>(2, 200));
|
|
CPPUNIT_ASSERT_EQUAL(size_t(2), lru.size());
|
|
CPPUNIT_ASSERT_EQUAL(100, lru.find(1)->second);
|
|
CPPUNIT_ASSERT_EQUAL(200, lru.find(2)->second);
|
|
CPPUNIT_ASSERT(bool(lru.find(3) == lru.end()));
|
|
|
|
// inserting new entry, lru entry is removed
|
|
lru.insert(std::make_pair<int, int>(3, 300));
|
|
CPPUNIT_ASSERT_EQUAL(size_t(2), lru.size());
|
|
CPPUNIT_ASSERT(bool(lru.find(1) == lru.end()));
|
|
CPPUNIT_ASSERT_EQUAL(200, lru.find(2)->second);
|
|
CPPUNIT_ASSERT_EQUAL(300, lru.find(3)->second);
|
|
|
|
// inserting new entry, lru entry is removed
|
|
std::pair<int, int> pair(4, 400);
|
|
lru.insert(pair);
|
|
CPPUNIT_ASSERT_EQUAL(size_t(2), lru.size());
|
|
CPPUNIT_ASSERT(bool(lru.find(1) == lru.end()));
|
|
CPPUNIT_ASSERT(bool(lru.find(2) == lru.end()));
|
|
CPPUNIT_ASSERT_EQUAL(300, lru.find(3)->second);
|
|
CPPUNIT_ASSERT_EQUAL(400, lru.find(4)->second);
|
|
}
|
|
|
|
void lru_map_test::testLruRemoval()
|
|
{
|
|
o3tl::lru_map<int, int> lru(5);
|
|
CPPUNIT_ASSERT_EQUAL(size_t(0), lru.size());
|
|
|
|
// fill up...
|
|
lru.insert(std::make_pair<int, int>(1, 100));
|
|
lru.insert(std::make_pair<int, int>(2, 200));
|
|
lru.insert(std::make_pair<int, int>(3, 300));
|
|
lru.insert(std::make_pair<int, int>(4, 400));
|
|
lru.insert(std::make_pair<int, int>(5, 500));
|
|
CPPUNIT_ASSERT_EQUAL(size_t(5), lru.size());
|
|
CPPUNIT_ASSERT_EQUAL(100, lru.find(1)->second);
|
|
CPPUNIT_ASSERT_EQUAL(200, lru.find(2)->second);
|
|
CPPUNIT_ASSERT_EQUAL(300, lru.find(3)->second);
|
|
CPPUNIT_ASSERT_EQUAL(400, lru.find(4)->second);
|
|
CPPUNIT_ASSERT_EQUAL(500, lru.find(5)->second);
|
|
|
|
// add one more entry - lru entry should be removed
|
|
lru.insert(std::make_pair<int, int>(6, 600));
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(5), lru.size());
|
|
CPPUNIT_ASSERT_EQUAL(200, lru.find(2)->second);
|
|
CPPUNIT_ASSERT_EQUAL(300, lru.find(3)->second);
|
|
CPPUNIT_ASSERT_EQUAL(400, lru.find(4)->second);
|
|
CPPUNIT_ASSERT_EQUAL(500, lru.find(5)->second);
|
|
CPPUNIT_ASSERT_EQUAL(600, lru.find(6)->second);
|
|
|
|
// access the lru entry to put it at the back of the lru queue
|
|
lru.find(2);
|
|
// add new entry - lru entry should be removed
|
|
lru.insert(std::make_pair<int, int>(7, 700));
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(5), lru.size());
|
|
CPPUNIT_ASSERT_EQUAL(200, lru.find(2)->second);
|
|
CPPUNIT_ASSERT_EQUAL(400, lru.find(4)->second);
|
|
CPPUNIT_ASSERT_EQUAL(500, lru.find(5)->second);
|
|
CPPUNIT_ASSERT_EQUAL(600, lru.find(6)->second);
|
|
CPPUNIT_ASSERT_EQUAL(700, lru.find(7)->second);
|
|
}
|
|
|
|
namespace
|
|
{
|
|
struct TestClassKey
|
|
{
|
|
int mA;
|
|
int mB;
|
|
|
|
TestClassKey(int a, int b)
|
|
: mA(a)
|
|
, mB(b)
|
|
{
|
|
}
|
|
|
|
bool operator==(TestClassKey const& aOther) const { return mA == aOther.mA && mB == aOther.mB; }
|
|
};
|
|
|
|
struct TestClassKeyHashFunction
|
|
{
|
|
std::size_t operator()(TestClassKey const& aKey) const
|
|
{
|
|
std::size_t seed = 0;
|
|
o3tl::hash_combine(seed, aKey.mA);
|
|
o3tl::hash_combine(seed, aKey.mB);
|
|
return seed;
|
|
}
|
|
};
|
|
}
|
|
|
|
void lru_map_test::testCustomHash()
|
|
{
|
|
// check lru_map with custom hash function
|
|
o3tl::lru_map<TestClassKey, int, TestClassKeyHashFunction> lru(2);
|
|
CPPUNIT_ASSERT_EQUAL(size_t(0), lru.size());
|
|
|
|
lru.insert(std::make_pair<TestClassKey, int>(TestClassKey(1, 1), 2));
|
|
CPPUNIT_ASSERT_EQUAL(size_t(1), lru.size());
|
|
|
|
lru.insert(std::make_pair<TestClassKey, int>(TestClassKey(1, 1), 7));
|
|
CPPUNIT_ASSERT_EQUAL(size_t(1), lru.size());
|
|
|
|
lru.insert(std::make_pair<TestClassKey, int>(TestClassKey(1, 2), 9));
|
|
CPPUNIT_ASSERT_EQUAL(size_t(2), lru.size());
|
|
|
|
CPPUNIT_ASSERT(bool(lru.end() == lru.find(TestClassKey(0, 0)))); // non existent
|
|
CPPUNIT_ASSERT_EQUAL(7, lru.find(TestClassKey(1, 1))->second);
|
|
CPPUNIT_ASSERT_EQUAL(9, lru.find(TestClassKey(1, 2))->second);
|
|
|
|
lru.insert(std::make_pair<TestClassKey, int>(TestClassKey(2, 1), 13));
|
|
|
|
CPPUNIT_ASSERT_EQUAL(size_t(2), lru.size());
|
|
|
|
CPPUNIT_ASSERT(bool(lru.end() == lru.find(TestClassKey(1, 1))));
|
|
CPPUNIT_ASSERT_EQUAL(9, lru.find(TestClassKey(1, 2))->second);
|
|
CPPUNIT_ASSERT_EQUAL(13, lru.find(TestClassKey(2, 1))->second);
|
|
}
|
|
|
|
void lru_map_test::testRemoveIf()
|
|
{
|
|
typedef o3tl::lru_map<int, int> IntMap;
|
|
typedef IntMap::key_value_pair_t IntMapPair;
|
|
struct limit_except : public std::exception
|
|
{
|
|
};
|
|
|
|
IntMap lru(6);
|
|
int i = 0;
|
|
for (; i < 8; i++)
|
|
lru.insert({ i, i });
|
|
CPPUNIT_ASSERT_EQUAL(size_t(6), lru.size());
|
|
// now contains 7..2
|
|
|
|
// remove everything < 4 from the back
|
|
try
|
|
{
|
|
lru.remove_if([](IntMapPair const& rPair) {
|
|
if (rPair.first >= 4)
|
|
throw limit_except();
|
|
return true;
|
|
});
|
|
CPPUNIT_ASSERT(false); // not reached
|
|
}
|
|
catch (limit_except&)
|
|
{
|
|
// contains 7..4
|
|
CPPUNIT_ASSERT_EQUAL(size_t(4), lru.size());
|
|
}
|
|
|
|
// remove all even numbers
|
|
lru.remove_if([](IntMapPair const& rPair) { return (0 == rPair.first % 2); });
|
|
CPPUNIT_ASSERT_EQUAL(size_t(2), lru.size());
|
|
// contains 7, 5
|
|
|
|
lru.insert({ 5, 5 });
|
|
// contains 5, 7
|
|
|
|
i = 5;
|
|
for (auto& rPair : lru)
|
|
{
|
|
CPPUNIT_ASSERT_EQUAL(i, rPair.first);
|
|
i += 2;
|
|
}
|
|
|
|
// remove the first item
|
|
lru.remove_if([](IntMapPair const& rPair) { return (rPair.first == 5); });
|
|
CPPUNIT_ASSERT_EQUAL(size_t(1), lru.size());
|
|
|
|
// remove the only item
|
|
lru.remove_if([](IntMapPair const& rPair) { return (rPair.first == 7); });
|
|
CPPUNIT_ASSERT_EQUAL(size_t(0), lru.size());
|
|
}
|
|
|
|
void lru_map_test::testChangeMaxSize()
|
|
{
|
|
o3tl::lru_map<int, int> lru(3);
|
|
CPPUNIT_ASSERT_EQUAL(size_t(0), lru.size());
|
|
lru.insert({ 0, 0 });
|
|
lru.insert({ 1, 1 });
|
|
lru.insert({ 2, 2 });
|
|
CPPUNIT_ASSERT_EQUAL(size_t(3), lru.size());
|
|
lru.setMaxSize(1);
|
|
CPPUNIT_ASSERT_EQUAL(size_t(1), lru.size());
|
|
}
|
|
|
|
void lru_map_test::testCustomItemSize()
|
|
{
|
|
struct cost_is_value
|
|
{
|
|
size_t operator()(int i) { return i; };
|
|
};
|
|
o3tl::lru_map<int, int, std::hash<int>, std::equal_to<int>, cost_is_value> lru(5);
|
|
lru.insert({ 1, 1 });
|
|
lru.insert({ 2, 2 });
|
|
// Adding this one will remove the first one, since then the total
|
|
// cost would be 6, more than the maximum 5.
|
|
lru.insert({ 3, 3 });
|
|
CPPUNIT_ASSERT_EQUAL(size_t(2), lru.size());
|
|
CPPUNIT_ASSERT_EQUAL(size_t(5), lru.total_size());
|
|
// Drop the last item.
|
|
lru.remove_if([](std::pair<int, int> i) { return i.first == 3; });
|
|
CPPUNIT_ASSERT_EQUAL(size_t(1), lru.size());
|
|
CPPUNIT_ASSERT_EQUAL(size_t(2), lru.total_size());
|
|
// This should drop everything except for keeping this one (an exception that
|
|
// keeps the last item inserted regardless of limit).
|
|
lru.insert({ 4, 4 });
|
|
CPPUNIT_ASSERT_EQUAL(size_t(1), lru.size());
|
|
CPPUNIT_ASSERT_EQUAL(size_t(4), lru.total_size());
|
|
lru.clear();
|
|
CPPUNIT_ASSERT_EQUAL(size_t(0), lru.size());
|
|
CPPUNIT_ASSERT_EQUAL(size_t(0), lru.total_size());
|
|
}
|
|
|
|
CPPUNIT_TEST_SUITE_REGISTRATION(lru_map_test);
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|