HeaderMapTest.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. //===- unittests/Lex/HeaderMapTest.cpp - HeaderMap tests ----------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===--------------------------------------------------------------===//
  8. #include "clang/Basic/CharInfo.h"
  9. #include "clang/Lex/HeaderMap.h"
  10. #include "clang/Lex/HeaderMapTypes.h"
  11. #include "llvm/ADT/SmallString.h"
  12. #include "llvm/Support/SwapByteOrder.h"
  13. #include "gtest/gtest.h"
  14. #include <cassert>
  15. #include <type_traits>
  16. using namespace clang;
  17. using namespace llvm;
  18. namespace {
  19. // Lay out a header file for testing.
  20. template <unsigned NumBuckets, unsigned NumBytes> struct MapFile {
  21. HMapHeader Header;
  22. HMapBucket Buckets[NumBuckets];
  23. unsigned char Bytes[NumBytes];
  24. void init() {
  25. memset(this, 0, sizeof(MapFile));
  26. Header.Magic = HMAP_HeaderMagicNumber;
  27. Header.Version = HMAP_HeaderVersion;
  28. Header.NumBuckets = NumBuckets;
  29. Header.StringsOffset = sizeof(Header) + sizeof(Buckets);
  30. }
  31. void swapBytes() {
  32. using llvm::sys::getSwappedBytes;
  33. Header.Magic = getSwappedBytes(Header.Magic);
  34. Header.Version = getSwappedBytes(Header.Version);
  35. Header.NumBuckets = getSwappedBytes(Header.NumBuckets);
  36. Header.StringsOffset = getSwappedBytes(Header.StringsOffset);
  37. }
  38. std::unique_ptr<const MemoryBuffer> getBuffer() const {
  39. return MemoryBuffer::getMemBuffer(
  40. StringRef(reinterpret_cast<const char *>(this), sizeof(MapFile)),
  41. "header",
  42. /* RequresNullTerminator */ false);
  43. }
  44. };
  45. // The header map hash function.
  46. static inline unsigned getHash(StringRef Str) {
  47. unsigned Result = 0;
  48. for (char C : Str)
  49. Result += toLowercase(C) * 13;
  50. return Result;
  51. }
  52. template <class FileTy> struct FileMaker {
  53. FileTy &File;
  54. unsigned SI = 1;
  55. unsigned BI = 0;
  56. FileMaker(FileTy &File) : File(File) {}
  57. unsigned addString(StringRef S) {
  58. assert(SI + S.size() + 1 <= sizeof(File.Bytes));
  59. std::copy(S.begin(), S.end(), File.Bytes + SI);
  60. auto OldSI = SI;
  61. SI += S.size() + 1;
  62. return OldSI;
  63. }
  64. void addBucket(unsigned Hash, unsigned Key, unsigned Prefix, unsigned Suffix) {
  65. assert(!(File.Header.NumBuckets & (File.Header.NumBuckets - 1)));
  66. unsigned I = Hash & (File.Header.NumBuckets - 1);
  67. do {
  68. if (!File.Buckets[I].Key) {
  69. File.Buckets[I].Key = Key;
  70. File.Buckets[I].Prefix = Prefix;
  71. File.Buckets[I].Suffix = Suffix;
  72. ++File.Header.NumEntries;
  73. return;
  74. }
  75. ++I;
  76. I &= File.Header.NumBuckets - 1;
  77. } while (I != (Hash & (File.Header.NumBuckets - 1)));
  78. llvm_unreachable("no empty buckets");
  79. }
  80. };
  81. TEST(HeaderMapTest, checkHeaderEmpty) {
  82. bool NeedsSwap;
  83. ASSERT_FALSE(HeaderMapImpl::checkHeader(
  84. *MemoryBuffer::getMemBufferCopy("", "empty"), NeedsSwap));
  85. ASSERT_FALSE(HeaderMapImpl::checkHeader(
  86. *MemoryBuffer::getMemBufferCopy("", "empty"), NeedsSwap));
  87. }
  88. TEST(HeaderMapTest, checkHeaderMagic) {
  89. MapFile<1, 1> File;
  90. File.init();
  91. File.Header.Magic = 0;
  92. bool NeedsSwap;
  93. ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
  94. }
  95. TEST(HeaderMapTest, checkHeaderReserved) {
  96. MapFile<1, 1> File;
  97. File.init();
  98. File.Header.Reserved = 1;
  99. bool NeedsSwap;
  100. ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
  101. }
  102. TEST(HeaderMapTest, checkHeaderVersion) {
  103. MapFile<1, 1> File;
  104. File.init();
  105. ++File.Header.Version;
  106. bool NeedsSwap;
  107. ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
  108. }
  109. TEST(HeaderMapTest, checkHeaderValidButEmpty) {
  110. MapFile<1, 1> File;
  111. File.init();
  112. bool NeedsSwap;
  113. ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
  114. ASSERT_FALSE(NeedsSwap);
  115. File.swapBytes();
  116. ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
  117. ASSERT_TRUE(NeedsSwap);
  118. }
  119. TEST(HeaderMapTest, checkHeader3Buckets) {
  120. MapFile<3, 1> File;
  121. ASSERT_EQ(3 * sizeof(HMapBucket), sizeof(File.Buckets));
  122. File.init();
  123. bool NeedsSwap;
  124. ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
  125. }
  126. TEST(HeaderMapTest, checkHeader0Buckets) {
  127. // Create with 1 bucket to avoid 0-sized arrays.
  128. MapFile<1, 1> File;
  129. File.init();
  130. File.Header.NumBuckets = 0;
  131. bool NeedsSwap;
  132. ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
  133. }
  134. TEST(HeaderMapTest, checkHeaderNotEnoughBuckets) {
  135. MapFile<1, 1> File;
  136. File.init();
  137. File.Header.NumBuckets = 8;
  138. bool NeedsSwap;
  139. ASSERT_FALSE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
  140. }
  141. TEST(HeaderMapTest, lookupFilename) {
  142. typedef MapFile<2, 7> FileTy;
  143. FileTy File;
  144. File.init();
  145. FileMaker<FileTy> Maker(File);
  146. auto a = Maker.addString("a");
  147. auto b = Maker.addString("b");
  148. auto c = Maker.addString("c");
  149. Maker.addBucket(getHash("a"), a, b, c);
  150. bool NeedsSwap;
  151. ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
  152. ASSERT_FALSE(NeedsSwap);
  153. HeaderMapImpl Map(File.getBuffer(), NeedsSwap);
  154. SmallString<8> DestPath;
  155. ASSERT_EQ("bc", Map.lookupFilename("a", DestPath));
  156. }
  157. template <class FileTy, class PaddingTy> struct PaddedFile {
  158. FileTy File;
  159. PaddingTy Padding;
  160. };
  161. TEST(HeaderMapTest, lookupFilenameTruncatedSuffix) {
  162. typedef MapFile<2, 64 - sizeof(HMapHeader) - 2 * sizeof(HMapBucket)> FileTy;
  163. static_assert(std::is_standard_layout<FileTy>::value,
  164. "Expected standard layout");
  165. static_assert(sizeof(FileTy) == 64, "check the math");
  166. PaddedFile<FileTy, uint64_t> P;
  167. auto &File = P.File;
  168. auto &Padding = P.Padding;
  169. File.init();
  170. FileMaker<FileTy> Maker(File);
  171. auto a = Maker.addString("a");
  172. auto b = Maker.addString("b");
  173. auto c = Maker.addString("c");
  174. Maker.addBucket(getHash("a"), a, b, c);
  175. // Add 'x' characters to cause an overflow into Padding.
  176. ASSERT_EQ('c', File.Bytes[5]);
  177. for (unsigned I = 6; I < sizeof(File.Bytes); ++I) {
  178. ASSERT_EQ(0, File.Bytes[I]);
  179. File.Bytes[I] = 'x';
  180. }
  181. Padding = 0xffffffff; // Padding won't stop it either.
  182. bool NeedsSwap;
  183. ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
  184. ASSERT_FALSE(NeedsSwap);
  185. HeaderMapImpl Map(File.getBuffer(), NeedsSwap);
  186. // The string for "c" runs to the end of File. Check that the suffix
  187. // ("cxxxx...") is detected as truncated, and an empty string is returned.
  188. SmallString<24> DestPath;
  189. ASSERT_EQ("", Map.lookupFilename("a", DestPath));
  190. }
  191. TEST(HeaderMapTest, lookupFilenameTruncatedPrefix) {
  192. typedef MapFile<2, 64 - sizeof(HMapHeader) - 2 * sizeof(HMapBucket)> FileTy;
  193. static_assert(std::is_standard_layout<FileTy>::value,
  194. "Expected standard layout");
  195. static_assert(sizeof(FileTy) == 64, "check the math");
  196. PaddedFile<FileTy, uint64_t> P;
  197. auto &File = P.File;
  198. auto &Padding = P.Padding;
  199. File.init();
  200. FileMaker<FileTy> Maker(File);
  201. auto a = Maker.addString("a");
  202. auto c = Maker.addString("c");
  203. auto b = Maker.addString("b"); // Store the prefix last.
  204. Maker.addBucket(getHash("a"), a, b, c);
  205. // Add 'x' characters to cause an overflow into Padding.
  206. ASSERT_EQ('b', File.Bytes[5]);
  207. for (unsigned I = 6; I < sizeof(File.Bytes); ++I) {
  208. ASSERT_EQ(0, File.Bytes[I]);
  209. File.Bytes[I] = 'x';
  210. }
  211. Padding = 0xffffffff; // Padding won't stop it either.
  212. bool NeedsSwap;
  213. ASSERT_TRUE(HeaderMapImpl::checkHeader(*File.getBuffer(), NeedsSwap));
  214. ASSERT_FALSE(NeedsSwap);
  215. HeaderMapImpl Map(File.getBuffer(), NeedsSwap);
  216. // The string for "b" runs to the end of File. Check that the prefix
  217. // ("bxxxx...") is detected as truncated, and an empty string is returned.
  218. SmallString<24> DestPath;
  219. ASSERT_EQ("", Map.lookupFilename("a", DestPath));
  220. }
  221. } // end namespace