Skip to content

Commit 8310056

Browse files
authored
Merge pull request #18887 from ahoppen/exp-growth-stream
[SourceKit] Use an exponentially growing appending binary stream to serialize the syntax tree
2 parents ab68f0d + 077d8e8 commit 8310056

File tree

6 files changed

+282
-1
lines changed

6 files changed

+282
-1
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
//===--- ExponentialGrowthAppendingBinaryByteStream.h -----------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
/// Defines an \c llvm::WritableBinaryStream whose internal buffer grows
14+
/// exponentially in size as data is written to it. It is thus more efficient
15+
/// than llvm::AppendingBinaryByteStream if a lot of small data gets appended to
16+
/// it.
17+
///
18+
//===----------------------------------------------------------------------===//
19+
20+
#ifndef SWIFT_BASIC_EXPONENTIALGROWTHAPPENDINGBINARYBYTESTREAM_H
21+
#define SWIFT_BASIC_EXPONENTIALGROWTHAPPENDINGBINARYBYTESTREAM_H
22+
23+
#include "swift/Basic/LLVM.h"
24+
#include "llvm/ADT/ArrayRef.h"
25+
#include "llvm/Support/BinaryByteStream.h"
26+
27+
namespace swift {
28+
29+
/// \brief An implementation of WritableBinaryStream which can write at its end
30+
/// causing the underlying data to grow. This class owns the underlying data.
31+
class ExponentialGrowthAppendingBinaryByteStream
32+
: public llvm::WritableBinaryStream {
33+
/// The buffer holding the data.
34+
SmallVector<uint8_t, 0> Data;
35+
36+
llvm::support::endianness Endian;
37+
public:
38+
ExponentialGrowthAppendingBinaryByteStream()
39+
: ExponentialGrowthAppendingBinaryByteStream(
40+
llvm::support::endianness::little) {}
41+
ExponentialGrowthAppendingBinaryByteStream(llvm::support::endianness Endian)
42+
: Endian(Endian) {}
43+
44+
void reserve(size_t Size);
45+
46+
llvm::support::endianness getEndian() const override { return Endian; }
47+
48+
llvm::Error readBytes(uint32_t Offset, uint32_t Size,
49+
ArrayRef<uint8_t> &Buffer) override;
50+
51+
llvm::Error readLongestContiguousChunk(uint32_t Offset,
52+
ArrayRef<uint8_t> &Buffer) override;
53+
54+
MutableArrayRef<uint8_t> data() { return Data; }
55+
56+
uint32_t getLength() override { return Data.size(); }
57+
58+
llvm::Error writeBytes(uint32_t Offset, ArrayRef<uint8_t> Buffer) override;
59+
60+
llvm::Error commit() override { return llvm::Error::success(); }
61+
62+
virtual llvm::BinaryStreamFlags getFlags() const override {
63+
return llvm::BSF_Write | llvm::BSF_Append;
64+
}
65+
};
66+
67+
} // end namespace swift
68+
69+
#endif /* SWIFT_BASIC_EXPONENTIALGROWTHAPPENDINGBINARYBYTESTREAM_H */

lib/Basic/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ add_swift_library(swiftBasic STATIC
7171
DiverseStack.cpp
7272
Edit.cpp
7373
EditorPlaceholder.cpp
74+
ExponentialGrowthAppendingBinaryByteStream.cpp
7475
FileSystem.cpp
7576
FileTypes.cpp
7677
JSONSerialization.cpp
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//===-------- ExponentialGrowthAppendingBinaryByteStream.cpp --------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "swift/Basic/ExponentialGrowthAppendingBinaryByteStream.h"
14+
15+
using namespace llvm;
16+
using namespace swift;
17+
18+
Error ExponentialGrowthAppendingBinaryByteStream::readBytes(
19+
uint32_t Offset, uint32_t Size, ArrayRef<uint8_t> &Buffer) {
20+
if (auto Error = checkOffsetForRead(Offset, Size)) {
21+
return Error;
22+
}
23+
24+
Buffer = ArrayRef<uint8_t>(Data.data() + Offset, Size);
25+
return Error::success();
26+
}
27+
28+
Error ExponentialGrowthAppendingBinaryByteStream::readLongestContiguousChunk(
29+
uint32_t Offset, ArrayRef<uint8_t> &Buffer) {
30+
if (auto Error = checkOffsetForRead(Offset, 0)) {
31+
return Error;
32+
}
33+
34+
Buffer = ArrayRef<uint8_t>(Data.data() + Offset, Data.size() - Offset);
35+
return Error::success();
36+
}
37+
38+
void ExponentialGrowthAppendingBinaryByteStream::reserve(size_t Size) {
39+
Data.reserve(Size);
40+
}
41+
42+
Error ExponentialGrowthAppendingBinaryByteStream::writeBytes(
43+
uint32_t Offset, ArrayRef<uint8_t> Buffer) {
44+
if (Buffer.empty())
45+
return Error::success();
46+
47+
if (auto Error = checkOffsetForWrite(Offset, Buffer.size())) {
48+
return Error;
49+
}
50+
51+
// Resize the internal buffer if needed.
52+
uint32_t RequiredSize = Offset + Buffer.size();
53+
if (RequiredSize > Data.size()) {
54+
Data.resize(RequiredSize);
55+
}
56+
57+
::memcpy(Data.data() + Offset, Buffer.data(), Buffer.size());
58+
59+
return Error::success();
60+
}

tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "SourceKit/Support/UIdent.h"
2727
#include "SourceKit/SwiftLang/Factory.h"
2828

29+
#include "swift/Basic/ExponentialGrowthAppendingBinaryByteStream.h"
2930
#include "swift/Basic/Mangler.h"
3031
#include "swift/Demangling/Demangler.h"
3132
#include "swift/Demangling/ManglingMacros.h"
@@ -2442,7 +2443,9 @@ void serializeSyntaxTreeAsByteTree(
24422443
ResponseBuilder::Dictionary &Dict) {
24432444
auto StartClock = clock();
24442445
// Serialize the syntax tree as a ByteTree
2445-
llvm::AppendingBinaryByteStream Stream(llvm::support::endianness::little);
2446+
swift::ExponentialGrowthAppendingBinaryByteStream Stream(
2447+
llvm::support::endianness::little);
2448+
Stream.reserve(32 * 1024);
24462449
llvm::BinaryStreamWriter Writer(Stream);
24472450
std::map<void *, void *> UserInfo;
24482451
UserInfo[swift::byteTree::UserInfoKeyReusedNodeIds] = &ReusedNodeIds;

unittests/Basic/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ add_swift_unittest(SwiftBasicTests
1313
DiverseStackTest.cpp
1414
EditorPlaceholderTest.cpp
1515
EncodedSequenceTest.cpp
16+
ExponentialGrowthAppendingBinaryByteStreamTests.cpp
1617
FileSystemTest.cpp
1718
ImmutablePointerSetTest.cpp
1819
JSONSerialization.cpp
@@ -38,4 +39,5 @@ target_link_libraries(SwiftBasicTests
3839
PRIVATE
3940
swiftBasic
4041
clangBasic
42+
LLVMTestingSupport
4143
)
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
//===--- ExponentialGrowthAppendingBinaryByteStreamTests.cpp -------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#include "swift/Basic/ExponentialGrowthAppendingBinaryByteStream.h"
14+
#include "llvm/Support/BinaryStreamReader.h"
15+
#include "llvm/Support/BinaryStreamWriter.h"
16+
#include "llvm/Testing/Support/Error.h"
17+
18+
#include "gtest/gtest.h"
19+
20+
using namespace llvm;
21+
using namespace llvm::support;
22+
using namespace swift;
23+
24+
class ExponentialGrowthAppendingBinaryByteStreamTest : public testing::Test {};
25+
26+
// This test case has been taken and adapted from
27+
// unittests/BinaryStreamTests.cpp in the LLVM project
28+
TEST_F(ExponentialGrowthAppendingBinaryByteStreamTest, ReadAndWrite) {
29+
StringRef Strings[] = {"1", "2", "3", "4"};
30+
ExponentialGrowthAppendingBinaryByteStream Stream(support::little);
31+
32+
BinaryStreamWriter Writer(Stream);
33+
BinaryStreamReader Reader(Stream);
34+
const uint8_t *Byte;
35+
StringRef Str;
36+
37+
// When the stream is empty, it should report a 0 length and we should get an
38+
// error trying to read even 1 byte from it.
39+
BinaryStreamRef ConstRef(Stream);
40+
EXPECT_EQ(0U, ConstRef.getLength());
41+
EXPECT_THAT_ERROR(Reader.readObject(Byte), Failed());
42+
43+
// But if we write to it, its size should increase and we should be able to
44+
// read not just a byte, but the string that was written.
45+
EXPECT_THAT_ERROR(Writer.writeCString(Strings[0]), Succeeded());
46+
EXPECT_EQ(2U, ConstRef.getLength());
47+
EXPECT_THAT_ERROR(Reader.readObject(Byte), Succeeded());
48+
49+
Reader.setOffset(0);
50+
EXPECT_THAT_ERROR(Reader.readCString(Str), Succeeded());
51+
EXPECT_EQ(Str, Strings[0]);
52+
53+
// If we drop some bytes from the front, we should still track the length as
54+
// the
55+
// underlying stream grows.
56+
BinaryStreamRef Dropped = ConstRef.drop_front(1);
57+
EXPECT_EQ(1U, Dropped.getLength());
58+
59+
EXPECT_THAT_ERROR(Writer.writeCString(Strings[1]), Succeeded());
60+
EXPECT_EQ(4U, ConstRef.getLength());
61+
EXPECT_EQ(3U, Dropped.getLength());
62+
63+
// If we drop zero bytes from the back, we should continue tracking the
64+
// length.
65+
Dropped = Dropped.drop_back(0);
66+
EXPECT_THAT_ERROR(Writer.writeCString(Strings[2]), Succeeded());
67+
EXPECT_EQ(6U, ConstRef.getLength());
68+
EXPECT_EQ(5U, Dropped.getLength());
69+
70+
// If we drop non-zero bytes from the back, we should stop tracking the
71+
// length.
72+
Dropped = Dropped.drop_back(1);
73+
EXPECT_THAT_ERROR(Writer.writeCString(Strings[3]), Succeeded());
74+
EXPECT_EQ(8U, ConstRef.getLength());
75+
EXPECT_EQ(4U, Dropped.getLength());
76+
}
77+
78+
TEST_F(ExponentialGrowthAppendingBinaryByteStreamTest, WriteAtInvalidOffset) {
79+
ExponentialGrowthAppendingBinaryByteStream Stream(llvm::support::little);
80+
EXPECT_EQ(0U, Stream.getLength());
81+
82+
std::vector<uint8_t> InputData = {'T', 'e', 's', 't', 'T', 'e', 's', 't'};
83+
auto Test = makeArrayRef(InputData).take_front(4);
84+
// Writing past the end of the stream is an error.
85+
EXPECT_THAT_ERROR(Stream.writeBytes(4, Test), Failed());
86+
87+
// Writing exactly at the end of the stream is ok.
88+
EXPECT_THAT_ERROR(Stream.writeBytes(0, Test), Succeeded());
89+
EXPECT_EQ(Test, Stream.data());
90+
91+
// And now that the end of the stream is where we couldn't write before, now
92+
// we can write.
93+
EXPECT_THAT_ERROR(Stream.writeBytes(4, Test), Succeeded());
94+
EXPECT_EQ(MutableArrayRef<uint8_t>(InputData), Stream.data());
95+
}
96+
97+
TEST_F(ExponentialGrowthAppendingBinaryByteStreamTest, InitialSizeZero) {
98+
// Test that the stream also works with an initial size of 0, which doesn't
99+
// grow when doubled.
100+
ExponentialGrowthAppendingBinaryByteStream Stream(llvm::support::little);
101+
102+
std::vector<uint8_t> InputData = {'T', 'e', 's', 't'};
103+
auto Test = makeArrayRef(InputData).take_front(4);
104+
EXPECT_THAT_ERROR(Stream.writeBytes(0, Test), Succeeded());
105+
EXPECT_EQ(Test, Stream.data());
106+
}
107+
108+
TEST_F(ExponentialGrowthAppendingBinaryByteStreamTest, GrowMultipleSteps) {
109+
ExponentialGrowthAppendingBinaryByteStream Stream(llvm::support::little);
110+
111+
// Test that the buffer can grow multiple steps at once, e.g. 1 -> 2 -> 4
112+
std::vector<uint8_t> InputData = {'T', 'e', 's', 't'};
113+
auto Test = makeArrayRef(InputData).take_front(4);
114+
EXPECT_THAT_ERROR(Stream.writeBytes(0, Test), Succeeded());
115+
EXPECT_EQ(Test, Stream.data());
116+
}
117+
118+
TEST_F(ExponentialGrowthAppendingBinaryByteStreamTest, WriteIntoMiddle) {
119+
// Test that the stream resizes correctly if we write into its middle
120+
121+
ExponentialGrowthAppendingBinaryByteStream Stream(llvm::support::little);
122+
123+
// Test that the buffer can grow multiple steps at once, e.g. 1 -> 2 -> 4
124+
std::vector<uint8_t> InitialData = {'T', 'e', 's', 't'};
125+
auto InitialDataRef = makeArrayRef(InitialData);
126+
EXPECT_THAT_ERROR(Stream.writeBytes(0, InitialDataRef), Succeeded());
127+
EXPECT_EQ(InitialDataRef, Stream.data());
128+
129+
std::vector<uint8_t> UpdateData = {'u'};
130+
auto UpdateDataRef = makeArrayRef(UpdateData);
131+
EXPECT_THAT_ERROR(Stream.writeBytes(1, UpdateDataRef), Succeeded());
132+
133+
std::vector<uint8_t> DataAfterUpdate = {'T', 'u', 's', 't'};
134+
auto DataAfterUpdateRef = makeArrayRef(DataAfterUpdate);
135+
EXPECT_EQ(DataAfterUpdateRef, Stream.data());
136+
EXPECT_EQ(4u, Stream.getLength());
137+
138+
std::vector<uint8_t> InsertData = {'e', 'r'};
139+
auto InsertDataRef = makeArrayRef(InsertData);
140+
EXPECT_THAT_ERROR(Stream.writeBytes(4, InsertDataRef), Succeeded());
141+
142+
std::vector<uint8_t> DataAfterInsert = {'T', 'u', 's', 't', 'e', 'r'};
143+
auto DataAfterInsertRef = makeArrayRef(DataAfterInsert);
144+
EXPECT_EQ(DataAfterInsertRef, Stream.data());
145+
EXPECT_EQ(6u, Stream.getLength());
146+
}

0 commit comments

Comments
 (0)