Skip to content

Commit dbbc4f4

Browse files
committed
SourceManager: Encapsulate line number mapping into SrcMgr::LineOffsetMapping
Put the guts of `ComputeLineNumbers` into `LineOffsetMapping::get` and `LineOffsetMapping::LineOffsetMapping`. As a drive-by, store the number of lines directly in the bump-ptr-allocated array. Differential Revision: https://reviews.llvm.org/D89913
1 parent 5668eda commit dbbc4f4

File tree

5 files changed

+140
-29
lines changed

5 files changed

+140
-29
lines changed

clang/include/clang/Basic/SourceManager.h

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,35 @@ namespace SrcMgr {
9090
return CK == C_User_ModuleMap || CK == C_System_ModuleMap;
9191
}
9292

93+
/// Mapping of line offsets into a source file. This does not own the storage
94+
/// for the line numbers.
95+
class LineOffsetMapping {
96+
public:
97+
explicit operator bool() const { return Storage; }
98+
unsigned size() const {
99+
assert(Storage);
100+
return Storage[0];
101+
}
102+
ArrayRef<unsigned> getLines() const {
103+
assert(Storage);
104+
return ArrayRef<unsigned>(Storage + 1, Storage + 1 + size());
105+
}
106+
const unsigned *begin() const { return getLines().begin(); }
107+
const unsigned *end() const { return getLines().end(); }
108+
const unsigned &operator[](int I) const { return getLines()[I]; }
109+
110+
static LineOffsetMapping get(llvm::MemoryBufferRef Buffer,
111+
llvm::BumpPtrAllocator &Alloc);
112+
113+
LineOffsetMapping() = default;
114+
LineOffsetMapping(ArrayRef<unsigned> LineOffsets,
115+
llvm::BumpPtrAllocator &Alloc);
116+
117+
private:
118+
/// First element is the size, followed by elements at off-by-one indexes.
119+
unsigned *Storage = nullptr;
120+
};
121+
93122
/// One instance of this struct is kept for every file loaded or used.
94123
///
95124
/// This object owns the MemoryBuffer object.
@@ -115,14 +144,9 @@ namespace SrcMgr {
115144

116145
/// A bump pointer allocated array of offsets for each source line.
117146
///
118-
/// This is lazily computed. This is owned by the SourceManager
147+
/// This is lazily computed. The lines are owned by the SourceManager
119148
/// BumpPointerAllocator object.
120-
unsigned *SourceLineCache = nullptr;
121-
122-
/// The number of lines in this ContentCache.
123-
///
124-
/// This is only valid if SourceLineCache is non-null.
125-
unsigned NumLines = 0;
149+
LineOffsetMapping SourceLineCache;
126150

127151
/// Indicates whether the buffer itself was provided to override
128152
/// the actual file contents.
@@ -157,10 +181,8 @@ namespace SrcMgr {
157181
OrigEntry = RHS.OrigEntry;
158182
ContentsEntry = RHS.ContentsEntry;
159183

160-
assert(!RHS.Buffer && RHS.SourceLineCache == nullptr &&
184+
assert(!RHS.Buffer && !RHS.SourceLineCache &&
161185
"Passed ContentCache object cannot own a buffer.");
162-
163-
NumLines = RHS.NumLines;
164186
}
165187

166188
ContentCache &operator=(const ContentCache& RHS) = delete;

clang/lib/Basic/SourceManager.cpp

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,10 +1200,10 @@ unsigned SourceManager::getColumnNumber(FileID FID, unsigned FilePos,
12001200
const char *Buf = MemBuf->getBufferStart();
12011201
// See if we just calculated the line number for this FilePos and can use
12021202
// that to lookup the start of the line instead of searching for it.
1203-
if (LastLineNoFileIDQuery == FID &&
1204-
LastLineNoContentCache->SourceLineCache != nullptr &&
1205-
LastLineNoResult < LastLineNoContentCache->NumLines) {
1206-
unsigned *SourceLineCache = LastLineNoContentCache->SourceLineCache;
1203+
if (LastLineNoFileIDQuery == FID && LastLineNoContentCache->SourceLineCache &&
1204+
LastLineNoResult < LastLineNoContentCache->SourceLineCache.size()) {
1205+
const unsigned *SourceLineCache =
1206+
LastLineNoContentCache->SourceLineCache.begin();
12071207
unsigned LineStart = SourceLineCache[LastLineNoResult - 1];
12081208
unsigned LineEnd = SourceLineCache[LastLineNoResult];
12091209
if (FilePos >= LineStart && FilePos < LineEnd) {
@@ -1274,15 +1274,20 @@ static void ComputeLineNumbers(DiagnosticsEngine &Diag, ContentCache *FI,
12741274
if (Invalid)
12751275
return;
12761276

1277+
FI->SourceLineCache = LineOffsetMapping::get(*Buffer, Alloc);
1278+
}
1279+
1280+
LineOffsetMapping LineOffsetMapping::get(llvm::MemoryBufferRef Buffer,
1281+
llvm::BumpPtrAllocator &Alloc) {
12771282
// Find the file offsets of all of the *physical* source lines. This does
12781283
// not look at trigraphs, escaped newlines, or anything else tricky.
12791284
SmallVector<unsigned, 256> LineOffsets;
12801285

12811286
// Line #1 starts at char 0.
12821287
LineOffsets.push_back(0);
12831288

1284-
const unsigned char *Buf = (const unsigned char *)Buffer->getBufferStart();
1285-
const unsigned char *End = (const unsigned char *)Buffer->getBufferEnd();
1289+
const unsigned char *Buf = (const unsigned char *)Buffer.getBufferStart();
1290+
const unsigned char *End = (const unsigned char *)Buffer.getBufferEnd();
12861291
const std::size_t BufLen = End - Buf;
12871292
unsigned I = 0;
12881293
while (I < BufLen) {
@@ -1297,10 +1302,14 @@ static void ComputeLineNumbers(DiagnosticsEngine &Diag, ContentCache *FI,
12971302
++I;
12981303
}
12991304

1300-
// Copy the offsets into the FileInfo structure.
1301-
FI->NumLines = LineOffsets.size();
1302-
FI->SourceLineCache = Alloc.Allocate<unsigned>(LineOffsets.size());
1303-
std::copy(LineOffsets.begin(), LineOffsets.end(), FI->SourceLineCache);
1305+
return LineOffsetMapping(LineOffsets, Alloc);
1306+
}
1307+
1308+
LineOffsetMapping::LineOffsetMapping(ArrayRef<unsigned> LineOffsets,
1309+
llvm::BumpPtrAllocator &Alloc)
1310+
: Storage(Alloc.Allocate<unsigned>(LineOffsets.size() + 1)) {
1311+
Storage[0] = LineOffsets.size();
1312+
std::copy(LineOffsets.begin(), LineOffsets.end(), Storage + 1);
13041313
}
13051314

13061315
/// getLineNumber - Given a SourceLocation, return the spelling line number
@@ -1344,9 +1353,9 @@ unsigned SourceManager::getLineNumber(FileID FID, unsigned FilePos,
13441353

13451354
// Okay, we know we have a line number table. Do a binary search to find the
13461355
// line number that this character position lands on.
1347-
unsigned *SourceLineCache = Content->SourceLineCache;
1348-
unsigned *SourceLineCacheStart = SourceLineCache;
1349-
unsigned *SourceLineCacheEnd = SourceLineCache + Content->NumLines;
1356+
const unsigned *SourceLineCache = Content->SourceLineCache.begin();
1357+
const unsigned *SourceLineCacheStart = SourceLineCache;
1358+
const unsigned *SourceLineCacheEnd = Content->SourceLineCache.end();
13501359

13511360
unsigned QueriedFilePos = FilePos+1;
13521361

@@ -1385,13 +1394,13 @@ unsigned SourceManager::getLineNumber(FileID FID, unsigned FilePos,
13851394
}
13861395
}
13871396
} else {
1388-
if (LastLineNoResult < Content->NumLines)
1397+
if (LastLineNoResult < Content->SourceLineCache.size())
13891398
SourceLineCacheEnd = SourceLineCache+LastLineNoResult+1;
13901399
}
13911400
}
13921401

1393-
unsigned *Pos
1394-
= std::lower_bound(SourceLineCache, SourceLineCacheEnd, QueriedFilePos);
1402+
const unsigned *Pos =
1403+
std::lower_bound(SourceLineCache, SourceLineCacheEnd, QueriedFilePos);
13951404
unsigned LineNo = Pos-SourceLineCacheStart;
13961405

13971406
LastLineNoFileIDQuery = FID;
@@ -1693,7 +1702,7 @@ SourceLocation SourceManager::translateLineCol(FileID FID,
16931702
if (!Buffer)
16941703
return SourceLocation();
16951704

1696-
if (Line > Content->NumLines) {
1705+
if (Line > Content->SourceLineCache.size()) {
16971706
unsigned Size = Buffer->getBufferSize();
16981707
if (Size > 0)
16991708
--Size;
@@ -2105,7 +2114,7 @@ void SourceManager::PrintStats() const {
21052114
unsigned NumLineNumsComputed = 0;
21062115
unsigned NumFileBytesMapped = 0;
21072116
for (fileinfo_iterator I = fileinfo_begin(), E = fileinfo_end(); I != E; ++I){
2108-
NumLineNumsComputed += I->second->SourceLineCache != nullptr;
2117+
NumLineNumsComputed += bool(I->second->SourceLineCache);
21092118
NumFileBytesMapped += I->second->getSizeBytesMapped();
21102119
}
21112120
unsigned NumMacroArgsComputed = MacroArgsCacheMap.size();

clang/lib/Lex/ScratchBuffer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ SourceLocation ScratchBuffer::getToken(const char *Buf, unsigned Len,
4141
&SourceMgr.getSLocEntry(SourceMgr.getFileID(BufferStartLoc))
4242
.getFile()
4343
.getContentCache());
44-
ContentCache->SourceLineCache = nullptr;
44+
ContentCache->SourceLineCache = SrcMgr::LineOffsetMapping();
4545
}
4646

4747
// Prefix the token with a \n, so that it looks like it is the first thing on

clang/unittests/Basic/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ add_clang_unittest(BasicTests
66
CharInfoTest.cpp
77
DiagnosticTest.cpp
88
FileManagerTest.cpp
9+
LineOffsetMappingTest.cpp
910
SourceManagerTest.cpp
1011
)
1112

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//===- unittests/Basic/LineOffsetMappingTest.cpp - Test LineOffsetMapping -===//
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+
9+
#include "clang/Basic/SourceManager.h"
10+
#include "gtest/gtest.h"
11+
12+
using namespace clang;
13+
using namespace clang::SrcMgr;
14+
using namespace llvm;
15+
16+
namespace {
17+
18+
TEST(LineOffsetMappingTest, empty) {
19+
LineOffsetMapping Mapping;
20+
EXPECT_FALSE(Mapping);
21+
22+
#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
23+
EXPECT_DEATH((void)Mapping.getLines(), "Assertion");
24+
#endif
25+
}
26+
27+
TEST(LineOffsetMappingTest, construct) {
28+
BumpPtrAllocator Alloc;
29+
unsigned Offsets[] = {0, 10, 20};
30+
LineOffsetMapping Mapping(Offsets, Alloc);
31+
EXPECT_EQ(3u, Mapping.size());
32+
EXPECT_EQ(0u, Mapping[0]);
33+
EXPECT_EQ(10u, Mapping[1]);
34+
EXPECT_EQ(20u, Mapping[2]);
35+
36+
#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
37+
EXPECT_DEATH((void)Mapping[3], "Assertion");
38+
#endif
39+
}
40+
41+
TEST(LineOffsetMappingTest, constructTwo) {
42+
// Confirm allocation size is big enough, convering an off-by-one bug.
43+
BumpPtrAllocator Alloc;
44+
unsigned Offsets1[] = {0, 10};
45+
unsigned Offsets2[] = {0, 20};
46+
LineOffsetMapping Mapping1(Offsets1, Alloc);
47+
LineOffsetMapping Mapping2(Offsets2, Alloc);
48+
49+
// Need to check Mapping1 *after* building Mapping2.
50+
EXPECT_EQ(2u, Mapping1.size());
51+
EXPECT_EQ(0u, Mapping1[0]);
52+
EXPECT_EQ(10u, Mapping1[1]);
53+
EXPECT_EQ(2u, Mapping2.size());
54+
EXPECT_EQ(0u, Mapping2[0]);
55+
EXPECT_EQ(20u, Mapping2[1]);
56+
}
57+
58+
TEST(LineOffsetMappingTest, get) {
59+
BumpPtrAllocator Alloc;
60+
StringRef Source = "first line\n"
61+
"second line\n";
62+
auto Mapping = LineOffsetMapping::get(MemoryBufferRef(Source, ""), Alloc);
63+
EXPECT_EQ(3u, Mapping.size());
64+
EXPECT_EQ(0u, Mapping[0]);
65+
EXPECT_EQ(11u, Mapping[1]);
66+
EXPECT_EQ(23u, Mapping[2]);
67+
}
68+
69+
TEST(LineOffsetMappingTest, getMissingFinalNewline) {
70+
BumpPtrAllocator Alloc;
71+
StringRef Source = "first line\n"
72+
"second line";
73+
auto Mapping = LineOffsetMapping::get(MemoryBufferRef(Source, ""), Alloc);
74+
EXPECT_EQ(2u, Mapping.size());
75+
EXPECT_EQ(0u, Mapping[0]);
76+
EXPECT_EQ(11u, Mapping[1]);
77+
}
78+
79+
} // end namespace

0 commit comments

Comments
 (0)