diff --git a/clang/unittests/Lex/HeaderMapTest.cpp b/clang/unittests/Lex/HeaderMapTest.cpp index c18ce79ef58f..4220edb2908e 100644 --- a/clang/unittests/Lex/HeaderMapTest.cpp +++ b/clang/unittests/Lex/HeaderMapTest.cpp @@ -6,89 +6,17 @@ // //===--------------------------------------------------------------===// -#include "clang/Basic/CharInfo.h" -#include "clang/Lex/HeaderMap.h" -#include "clang/Lex/HeaderMapTypes.h" +#include "HeaderMapTestUtils.h" #include "llvm/ADT/SmallString.h" -#include "llvm/Support/SwapByteOrder.h" #include "gtest/gtest.h" -#include #include using namespace clang; using namespace llvm; +using namespace clang::test; namespace { -// Lay out a header file for testing. -template struct MapFile { - HMapHeader Header; - HMapBucket Buckets[NumBuckets]; - unsigned char Bytes[NumBytes]; - - void init() { - memset(this, 0, sizeof(MapFile)); - Header.Magic = HMAP_HeaderMagicNumber; - Header.Version = HMAP_HeaderVersion; - Header.NumBuckets = NumBuckets; - Header.StringsOffset = sizeof(Header) + sizeof(Buckets); - } - - void swapBytes() { - using llvm::sys::getSwappedBytes; - Header.Magic = getSwappedBytes(Header.Magic); - Header.Version = getSwappedBytes(Header.Version); - Header.NumBuckets = getSwappedBytes(Header.NumBuckets); - Header.StringsOffset = getSwappedBytes(Header.StringsOffset); - } - - std::unique_ptr getBuffer() const { - return MemoryBuffer::getMemBuffer( - StringRef(reinterpret_cast(this), sizeof(MapFile)), - "header", - /* RequresNullTerminator */ false); - } -}; - -// The header map hash function. -static inline unsigned getHash(StringRef Str) { - unsigned Result = 0; - for (char C : Str) - Result += toLowercase(C) * 13; - return Result; -} - -template struct FileMaker { - FileTy &File; - unsigned SI = 1; - unsigned BI = 0; - FileMaker(FileTy &File) : File(File) {} - - unsigned addString(StringRef S) { - assert(SI + S.size() + 1 <= sizeof(File.Bytes)); - std::copy(S.begin(), S.end(), File.Bytes + SI); - auto OldSI = SI; - SI += S.size() + 1; - return OldSI; - } - void addBucket(unsigned Hash, unsigned Key, unsigned Prefix, unsigned Suffix) { - assert(!(File.Header.NumBuckets & (File.Header.NumBuckets - 1))); - unsigned I = Hash & (File.Header.NumBuckets - 1); - do { - if (!File.Buckets[I].Key) { - File.Buckets[I].Key = Key; - File.Buckets[I].Prefix = Prefix; - File.Buckets[I].Suffix = Suffix; - ++File.Header.NumEntries; - return; - } - ++I; - I &= File.Header.NumBuckets - 1; - } while (I != (Hash & (File.Header.NumBuckets - 1))); - llvm_unreachable("no empty buckets"); - } -}; - TEST(HeaderMapTest, checkHeaderEmpty) { bool NeedsSwap; ASSERT_FALSE(HeaderMapImpl::checkHeader( @@ -98,7 +26,7 @@ TEST(HeaderMapTest, checkHeaderEmpty) { } TEST(HeaderMapTest, checkHeaderMagic) { - MapFile<1, 1> File; + HMapFileMock<1, 1> File; File.init(); File.Header.Magic = 0; bool NeedsSwap; @@ -106,7 +34,7 @@ TEST(HeaderMapTest, checkHeaderMagic) { } TEST(HeaderMapTest, checkHeaderReserved) { - MapFile<1, 1> File; + HMapFileMock<1, 1> File; File.init(); File.Header.Reserved = 1; bool NeedsSwap; @@ -114,7 +42,7 @@ TEST(HeaderMapTest, checkHeaderReserved) { } TEST(HeaderMapTest, checkHeaderVersion) { - MapFile<1, 1> File; + HMapFileMock<1, 1> File; File.init(); ++File.Header.Version; bool NeedsSwap; @@ -122,7 +50,7 @@ TEST(HeaderMapTest, checkHeaderVersion) { } TEST(HeaderMapTest, checkHeaderValidButEmpty) { - MapFile<1, 1> File; + HMapFileMock<1, 1> File; File.init(); bool NeedsSwap; ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); @@ -134,7 +62,7 @@ TEST(HeaderMapTest, checkHeaderValidButEmpty) { } TEST(HeaderMapTest, checkHeader3Buckets) { - MapFile<3, 1> File; + HMapFileMock<3, 1> File; ASSERT_EQ(3 * sizeof(HMapBucket), sizeof(File.Buckets)); File.init(); @@ -144,7 +72,7 @@ TEST(HeaderMapTest, checkHeader3Buckets) { TEST(HeaderMapTest, checkHeader0Buckets) { // Create with 1 bucket to avoid 0-sized arrays. - MapFile<1, 1> File; + HMapFileMock<1, 1> File; File.init(); File.Header.NumBuckets = 0; bool NeedsSwap; @@ -152,7 +80,7 @@ TEST(HeaderMapTest, checkHeader0Buckets) { } TEST(HeaderMapTest, checkHeaderNotEnoughBuckets) { - MapFile<1, 1> File; + HMapFileMock<1, 1> File; File.init(); File.Header.NumBuckets = 8; bool NeedsSwap; @@ -160,15 +88,15 @@ TEST(HeaderMapTest, checkHeaderNotEnoughBuckets) { } TEST(HeaderMapTest, lookupFilename) { - typedef MapFile<2, 7> FileTy; + typedef HMapFileMock<2, 7> FileTy; FileTy File; File.init(); - FileMaker Maker(File); + HMapFileMockMaker Maker(File); auto a = Maker.addString("a"); auto b = Maker.addString("b"); auto c = Maker.addString("c"); - Maker.addBucket(getHash("a"), a, b, c); + Maker.addBucket("a", a, b, c); bool NeedsSwap; ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap)); @@ -185,7 +113,8 @@ template struct PaddedFile { }; TEST(HeaderMapTest, lookupFilenameTruncatedSuffix) { - typedef MapFile<2, 64 - sizeof(HMapHeader) - 2 * sizeof(HMapBucket)> FileTy; + typedef HMapFileMock<2, 64 - sizeof(HMapHeader) - 2 * sizeof(HMapBucket)> + FileTy; static_assert(std::is_standard_layout::value, "Expected standard layout"); static_assert(sizeof(FileTy) == 64, "check the math"); @@ -194,11 +123,11 @@ TEST(HeaderMapTest, lookupFilenameTruncatedSuffix) { auto &Padding = P.Padding; File.init(); - FileMaker Maker(File); + HMapFileMockMaker Maker(File); auto a = Maker.addString("a"); auto b = Maker.addString("b"); auto c = Maker.addString("c"); - Maker.addBucket(getHash("a"), a, b, c); + Maker.addBucket("a", a, b, c); // Add 'x' characters to cause an overflow into Padding. ASSERT_EQ('c', File.Bytes[5]); @@ -220,7 +149,8 @@ TEST(HeaderMapTest, lookupFilenameTruncatedSuffix) { } TEST(HeaderMapTest, lookupFilenameTruncatedPrefix) { - typedef MapFile<2, 64 - sizeof(HMapHeader) - 2 * sizeof(HMapBucket)> FileTy; + typedef HMapFileMock<2, 64 - sizeof(HMapHeader) - 2 * sizeof(HMapBucket)> + FileTy; static_assert(std::is_standard_layout::value, "Expected standard layout"); static_assert(sizeof(FileTy) == 64, "check the math"); @@ -229,11 +159,11 @@ TEST(HeaderMapTest, lookupFilenameTruncatedPrefix) { auto &Padding = P.Padding; File.init(); - FileMaker Maker(File); + HMapFileMockMaker Maker(File); auto a = Maker.addString("a"); auto c = Maker.addString("c"); auto b = Maker.addString("b"); // Store the prefix last. - Maker.addBucket(getHash("a"), a, b, c); + Maker.addBucket("a", a, b, c); // Add 'x' characters to cause an overflow into Padding. ASSERT_EQ('b', File.Bytes[5]); diff --git a/clang/unittests/Lex/HeaderMapTestUtils.h b/clang/unittests/Lex/HeaderMapTestUtils.h new file mode 100644 index 000000000000..271d0588fc80 --- /dev/null +++ b/clang/unittests/Lex/HeaderMapTestUtils.h @@ -0,0 +1,100 @@ +//===- unittests/Lex/HeaderMapTestUtils.h - HeaderMap utils -------===// +// +// 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 +// +//===--------------------------------------------------------------===// + +#ifndef LLVM_CLANG_UNITTESTS_LEX_HEADERMAPTESTUTILS_H +#define LLVM_CLANG_UNITTESTS_LEX_HEADERMAPTESTUTILS_H + +#include "clang/Basic/CharInfo.h" +#include "clang/Lex/HeaderMap.h" +#include "clang/Lex/HeaderMapTypes.h" +#include "llvm/Support/SwapByteOrder.h" +#include + +namespace clang { +namespace test { + +// Lay out a header file for testing. +template struct HMapFileMock { + HMapHeader Header; + HMapBucket Buckets[NumBuckets]; + unsigned char Bytes[NumBytes]; + + void init() { + memset(this, 0, sizeof(HMapFileMock)); + Header.Magic = HMAP_HeaderMagicNumber; + Header.Version = HMAP_HeaderVersion; + Header.NumBuckets = NumBuckets; + Header.StringsOffset = sizeof(Header) + sizeof(Buckets); + } + + void swapBytes() { + using llvm::sys::getSwappedBytes; + Header.Magic = getSwappedBytes(Header.Magic); + Header.Version = getSwappedBytes(Header.Version); + Header.NumBuckets = getSwappedBytes(Header.NumBuckets); + Header.StringsOffset = getSwappedBytes(Header.StringsOffset); + } + + std::unique_ptr getBuffer() { + return llvm::MemoryBuffer::getMemBuffer( + StringRef(reinterpret_cast(this), sizeof(HMapFileMock)), + "header", + /* RequresNullTerminator */ false); + } +}; + +template struct HMapFileMockMaker { + FileTy &File; + unsigned SI = 1; + unsigned BI = 0; + HMapFileMockMaker(FileTy &File) : File(File) {} + + unsigned addString(StringRef S) { + assert(SI + S.size() + 1 <= sizeof(File.Bytes)); + std::copy(S.begin(), S.end(), File.Bytes + SI); + auto OldSI = SI; + SI += S.size() + 1; + return OldSI; + } + + void addBucket(StringRef Str, unsigned Key, unsigned Prefix, + unsigned Suffix) { + addBucket(getHash(Str), Key, Prefix, Suffix); + } + + void addBucket(unsigned Hash, unsigned Key, unsigned Prefix, + unsigned Suffix) { + assert(!(File.Header.NumBuckets & (File.Header.NumBuckets - 1))); + unsigned I = Hash & (File.Header.NumBuckets - 1); + do { + if (!File.Buckets[I].Key) { + File.Buckets[I].Key = Key; + File.Buckets[I].Prefix = Prefix; + File.Buckets[I].Suffix = Suffix; + ++File.Header.NumEntries; + return; + } + ++I; + I &= File.Header.NumBuckets - 1; + } while (I != (Hash & (File.Header.NumBuckets - 1))); + llvm_unreachable("no empty buckets"); + } + + // The header map hash function. + static unsigned getHash(StringRef Str) { + unsigned Result = 0; + for (char C : Str) + Result += toLowercase(C) * 13; + return Result; + } +}; + +} // namespace test +} // namespace clang + +#endif