1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
//===- OnDiskHashMappedTrieTest.cpp ---------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/CAS/OnDiskHashMappedTrie.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/Support/Alignment.h"
#include "llvm/Testing/Support/Error.h"
#include "llvm/Testing/Support/SupportHelpers.h"
#include "gtest/gtest.h"
#if LLVM_ENABLE_ONDISK_CAS
using namespace llvm;
using namespace llvm::cas;
namespace {
TEST(OnDiskHashMappedTrieTest, Insertion) {
unittest::TempDir Temp("on-disk-hash-mapped-trie", /*Unique=*/true);
// Create tries with various sizes of hash and with data.
//
// NOTE: The check related to \a recoverFromFileOffset() catches a potential
// off-by-one bounds-checking bug when the trie record size (data + hash) add
// up to a multiple of 8B. Iterate through a few different hash sizes to
// check it both ways.
constexpr size_t MB = 1024u * 1024u;
constexpr size_t DataSize = 8; // Multiple of 8B.
for (size_t NumHashBytes : {1, 2, 4, 8}) {
size_t NumHashBits = NumHashBytes * 8;
auto createTrie = [&]() {
return OnDiskHashMappedTrie::create(
Temp.path((Twine(NumHashBytes) + "B").str()), "index",
/*NumHashBits=*/NumHashBits, DataSize, /*MaxFileSize=*/MB,
/*NewInitialFileSize=*/std::nullopt);
};
std::optional<OnDiskHashMappedTrie> Trie1;
ASSERT_THAT_ERROR(createTrie().moveInto(Trie1), Succeeded());
std::optional<OnDiskHashMappedTrie> Trie2;
ASSERT_THAT_ERROR(createTrie().moveInto(Trie2), Succeeded());
uint8_t Hash0Bytes[8] = {0, 0, 0, 0, 0, 0, 0, 0};
uint8_t Hash1Bytes[8] = {1, 0, 0, 0, 0, 0, 0, 0};
auto Hash0 = ArrayRef(Hash0Bytes).take_front(NumHashBytes);
auto Hash1 = ArrayRef(Hash1Bytes).take_front(NumHashBytes);
constexpr StringLiteral Data0v1Bytes = "data0.v1";
constexpr StringLiteral Data0v2Bytes = "data0.v2";
constexpr StringLiteral Data1Bytes = "data1...";
static_assert(Data0v1Bytes.size() == DataSize, "math error");
static_assert(Data0v2Bytes.size() == DataSize, "math error");
static_assert(Data1Bytes.size() == DataSize, "math error");
ArrayRef<char> Data0v1 =
ArrayRef(Data0v1Bytes.data(), Data0v1Bytes.size());
ArrayRef<char> Data0v2 =
ArrayRef(Data0v2Bytes.data(), Data0v2Bytes.size());
ArrayRef<char> Data1 = ArrayRef(Data1Bytes.data(), Data1Bytes.size());
// Lookup when trie is empty.
EXPECT_FALSE(Trie1->find(Hash0));
// Insert.
std::optional<FileOffset> Offset;
std::optional<MutableArrayRef<char>> Data;
{
auto Insertion = Trie1->insert({Hash0, Data0v1});
ASSERT_TRUE(Insertion);
EXPECT_EQ(Hash0, Insertion->Hash);
EXPECT_EQ(Data0v1, Insertion->Data);
EXPECT_TRUE(isAddrAligned(Align(8), Insertion->Data.data()));
Offset = Insertion.getOffset();
Data = Insertion->Data;
}
// Find.
{
auto Lookup = Trie1->find(Hash0);
ASSERT_TRUE(Lookup);
EXPECT_EQ(Hash0, Lookup->Hash);
EXPECT_EQ(Data0v1, Lookup->Data);
EXPECT_EQ(Offset->get(), Lookup.getOffset().get());
}
// Find in a different instance of the same on-disk trie that existed
// before the insertion.
{
auto Lookup = Trie2->find(Hash0);
ASSERT_TRUE(Lookup);
EXPECT_EQ(Hash0, Lookup->Hash);
EXPECT_EQ(Data0v1, Lookup->Data);
EXPECT_EQ(Offset->get(), Lookup.getOffset().get());
}
// Create a new instance and check that too.
Trie2.reset();
ASSERT_THAT_ERROR(createTrie().moveInto(Trie2), Succeeded());
{
auto Lookup = Trie2->find(Hash0);
ASSERT_TRUE(Lookup);
EXPECT_EQ(Hash0, Lookup->Hash);
EXPECT_EQ(Data0v1, Lookup->Data);
EXPECT_EQ(Offset->get(), Lookup.getOffset().get());
}
// Change the data.
llvm::copy(Data0v2, Data->data());
{
auto Lookup = Trie2->find(Hash0);
ASSERT_TRUE(Lookup);
EXPECT_EQ(Hash0, Lookup->Hash);
EXPECT_EQ(Data0v2, Lookup->Data);
EXPECT_EQ(Offset->get(), Lookup.getOffset().get());
}
// Find different hash.
EXPECT_FALSE(Trie1->find(Hash1));
EXPECT_FALSE(Trie2->find(Hash1));
// Recover from an offset.
{
auto Recovered = Trie1->recoverFromFileOffset(*Offset);
ASSERT_TRUE(Recovered);
EXPECT_EQ(Offset->get(), Recovered.getOffset().get());
EXPECT_EQ(Hash0, Recovered->Hash);
EXPECT_EQ(Data0v2, Recovered->Data);
}
// Insert another thing.
{
auto Insertion = Trie1->insert({Hash1, Data1});
ASSERT_TRUE(Insertion);
EXPECT_EQ(Hash1, Insertion->Hash);
EXPECT_EQ(Data1, Insertion->Data);
EXPECT_TRUE(isAddrAligned(Align(8), Insertion->Data.data()));
EXPECT_NE(Offset->get(), Insertion.getOffset().get());
}
}
}
} // namespace
#endif
|