1 /*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <folly/portability/GTest.h>
18
19 #include <folly/ExceptionWrapper.h>
20 #include <folly/Singleton.h>
21 #include <folly/io/IOBuf.h>
22 #include <folly/io/async/DelayedDestruction.h>
23 #include <thrift/lib/cpp2/transport/rocket/framing/Parser.h>
24
25 namespace apache {
26 namespace thrift {
27 namespace rocket {
28
29 class FakeOwner : public folly::DelayedDestruction {
30 public:
handleFrame(std::unique_ptr<folly::IOBuf>)31 void handleFrame(std::unique_ptr<folly::IOBuf>) {}
close(folly::exception_wrapper)32 void close(folly::exception_wrapper) noexcept {}
scheduleTimeout(folly::HHWheelTimer::Callback *,const std::chrono::milliseconds &)33 void scheduleTimeout(
34 folly::HHWheelTimer::Callback*, const std::chrono::milliseconds&) {}
incMemoryUsage(uint32_t)35 bool incMemoryUsage(uint32_t) { return true; }
decMemoryUsage(uint32_t)36 void decMemoryUsage(uint32_t) {}
37
customAlloc(size_t)38 std::unique_ptr<folly::IOBuf> customAlloc(size_t) { return nullptr; }
39 };
40
41 // TODO: This should be removed once the new buffer logic controlled by
42 // THRIFT_FLAG(rocket_parser_dont_hold_buffer_enabled) is stable.
TEST(ParserTest,resizeBufferTest)43 TEST(ParserTest, resizeBufferTest) {
44 FakeOwner owner;
45 Parser<FakeOwner> parser(owner, std::chrono::milliseconds(0));
46 parser.setReadBufferSize(Parser<FakeOwner>::kMaxBufferSize * 2);
47
48 folly::IOBuf iobuf{folly::IOBuf::CreateOp(), parser.getReadBufferSize()};
49 // pretend there is something written into the buffer
50 iobuf.append(Parser<FakeOwner>::kMinBufferSize);
51
52 parser.setReadBuffer(std::move(iobuf));
53 parser.resizeBuffer();
54
55 EXPECT_EQ(parser.getReadBufferSize(), Parser<FakeOwner>::kMaxBufferSize);
56 }
57
58 // TODO: This should be removed once the new buffer logic controlled by
59 // THRIFT_FLAG(rocket_parser_dont_hold_buffer_enabled) is stable.
TEST(ParserTest,noResizeBufferReadBufGtMaxTest)60 TEST(ParserTest, noResizeBufferReadBufGtMaxTest) {
61 FakeOwner owner;
62 Parser<FakeOwner> parser(owner, std::chrono::milliseconds(0));
63 parser.setReadBufferSize(Parser<FakeOwner>::kMaxBufferSize * 2);
64
65 folly::IOBuf iobuf{folly::IOBuf::CreateOp(), parser.getReadBufferSize()};
66 // pretend there is something written into the buffer, but with size > max
67 iobuf.append(Parser<FakeOwner>::kMaxBufferSize * 1.5);
68
69 parser.setReadBuffer(std::move(iobuf));
70 parser.resizeBuffer();
71
72 EXPECT_EQ(parser.getReadBufferSize(), Parser<FakeOwner>::kMaxBufferSize * 2);
73 }
74
75 // TODO: This should be removed once the new buffer logic controlled by
76 // THRIFT_FLAG(rocket_parser_dont_hold_buffer_enabled) is stable.
TEST(ParserTest,noResizeBufferReadBufEqMaxTest)77 TEST(ParserTest, noResizeBufferReadBufEqMaxTest) {
78 FakeOwner owner;
79 Parser<FakeOwner> parser(owner, std::chrono::milliseconds(0));
80 parser.setReadBufferSize(Parser<FakeOwner>::kMaxBufferSize * 2);
81
82 folly::IOBuf iobuf{folly::IOBuf::CreateOp(), parser.getReadBufferSize()};
83 // pretend there is something written into the buffer, but with size = max
84 iobuf.append(Parser<FakeOwner>::kMaxBufferSize);
85
86 parser.setReadBuffer(std::move(iobuf));
87 parser.resizeBuffer();
88
89 EXPECT_EQ(parser.getReadBufferSize(), Parser<FakeOwner>::kMaxBufferSize * 2);
90 }
91
92 // TODO: This should be removed once the new buffer logic controlled by
93 // THRIFT_FLAG(rocket_parser_dont_hold_buffer_enabled) is stable.
TEST(ParserTest,AlignmentTest)94 TEST(ParserTest, AlignmentTest) {
95 std::string s = "1234567890";
96 auto iobuf = folly::IOBuf::copyBuffer(s);
97 auto res = alignTo4k(
98 *iobuf, 4 /* 4 should be the first on 4k aligned address */, 1000);
99 EXPECT_TRUE(res);
100 EXPECT_EQ(reinterpret_cast<std::uintptr_t>(iobuf->data() + 4) % 4096u, 0);
101 EXPECT_EQ(iobuf->length() + iobuf->tailroom(), 1000);
102 EXPECT_EQ(folly::StringPiece(iobuf->coalesce()), s);
103
104 iobuf = folly::IOBuf::copyBuffer(s);
105 // it is possible the aligned part has not been received yet
106 res = alignTo4k(*iobuf, 256, 1000);
107 EXPECT_TRUE(res);
108 EXPECT_EQ(reinterpret_cast<std::uintptr_t>(iobuf->data() + 256) % 4096u, 0);
109 EXPECT_EQ(iobuf->length() + iobuf->tailroom(), 1000);
110 EXPECT_EQ(folly::StringPiece(iobuf->coalesce()), s);
111
112 iobuf = folly::IOBuf::copyBuffer(s);
113 // it is also possible the frame is smaller than received data
114 res = alignTo4k(*iobuf, 4, 7);
115 EXPECT_EQ(reinterpret_cast<std::uintptr_t>(iobuf->data() + 4) % 4096u, 0);
116 EXPECT_EQ(iobuf->length() + iobuf->tailroom(), 10);
117 EXPECT_EQ(iobuf->tailroom(), 0);
118 EXPECT_EQ(folly::StringPiece(iobuf->coalesce()), s);
119 }
120
TEST(ParserTest,AlignmentIOBufQueueTest)121 TEST(ParserTest, AlignmentIOBufQueueTest) {
122 std::string s = "1234567890";
123 folly::IOBufQueue iobufQueue{folly::IOBufQueue::cacheChainLength()};
124 iobufQueue.append(folly::IOBuf::copyBuffer(s));
125 auto res = alignTo4kBufQueue(
126 iobufQueue, 4 /* 4 should be the first on 4k aligned address */, 1000);
127 EXPECT_TRUE(res);
128 auto iobuf = iobufQueue.move();
129 EXPECT_EQ(reinterpret_cast<std::uintptr_t>(iobuf->data() + 4) % 4096u, 0);
130 EXPECT_EQ(iobuf->length() + iobuf->tailroom(), 1000);
131 EXPECT_EQ(folly::StringPiece(iobuf->coalesce()), s);
132
133 iobufQueue.reset();
134 iobufQueue.append(folly::IOBuf::copyBuffer(s));
135 // it is possible the aligned part has not been received yet
136 res = alignTo4kBufQueue(iobufQueue, 256, 1000);
137 EXPECT_TRUE(res);
138 iobuf = iobufQueue.move();
139 EXPECT_EQ(reinterpret_cast<std::uintptr_t>(iobuf->data() + 256) % 4096u, 0);
140 EXPECT_EQ(iobuf->length() + iobuf->tailroom(), 1000);
141 EXPECT_EQ(folly::StringPiece(iobuf->coalesce()), s);
142
143 iobufQueue.reset();
144 iobufQueue.append(folly::IOBuf::copyBuffer(s));
145 // it is also possible the frame is smaller than received data
146 res = alignTo4kBufQueue(iobufQueue, 4, 7);
147 EXPECT_TRUE(res);
148 iobuf = iobufQueue.move();
149 EXPECT_EQ(reinterpret_cast<std::uintptr_t>(iobuf->data() + 4) % 4096u, 0);
150 EXPECT_EQ(iobuf->length() + iobuf->tailroom(), 10);
151 EXPECT_EQ(iobuf->tailroom(), 0);
152 EXPECT_EQ(folly::StringPiece(iobuf->coalesce()), s);
153 }
154
TEST(ParserTest,BufferIsChainedAlignmentTest)155 TEST(ParserTest, BufferIsChainedAlignmentTest) {
156 std::string s = "1234567890";
157 auto iobuf = folly::IOBuf::copyBuffer(s);
158 auto iobuf2 = folly::IOBuf::copyBuffer(s);
159 iobuf->appendChain(std::move(iobuf2));
160 EXPECT_TRUE(iobuf->isChained());
161
162 folly::IOBufQueue iobufQueue{folly::IOBufQueue::cacheChainLength()};
163 iobufQueue.append(std::move(iobuf));
164 auto res = alignTo4kBufQueue(
165 iobufQueue, 4 /* 4 should be the first on 4k aligned address */, 1000);
166 EXPECT_TRUE(res);
167 auto buf = iobufQueue.move();
168 EXPECT_EQ(reinterpret_cast<std::uintptr_t>(buf->data() + 4) % 4096u, 0);
169 EXPECT_EQ(buf->length() + buf->tailroom(), 1000);
170 EXPECT_EQ(folly::StringPiece(buf->coalesce()), s + s);
171 }
172
TEST(ParserTest,PageAlignedBufferTest)173 TEST(ParserTest, PageAlignedBufferTest) {
174 // total number of (usable) bytes to be allocated
175 constexpr size_t kNumBytes = 32;
176 // portion of kNumBytes to be placed before page boundary
177 constexpr size_t kStartOffset = 13;
178 // final length of the iobuf (must be <= kNumBytes)
179 size_t trimLength = 0;
180 auto buf = get4kAlignedBuf(kNumBytes, kStartOffset, trimLength);
181 ASSERT_NE(nullptr, buf.get());
182 EXPECT_EQ(
183 reinterpret_cast<std::uintptr_t>(buf->data() + kStartOffset) % 4096u, 0);
184 EXPECT_EQ(trimLength, buf->length());
185
186 trimLength = kStartOffset;
187 buf = get4kAlignedBuf(kNumBytes, kStartOffset, trimLength);
188 ASSERT_NE(nullptr, buf.get());
189 EXPECT_EQ(
190 reinterpret_cast<std::uintptr_t>(buf->data() + kStartOffset) % 4096u, 0);
191 EXPECT_EQ(trimLength, buf->length());
192 }
193
194 } // namespace rocket
195 } // namespace thrift
196 } // namespace apache
197
main(int argc,char ** argv)198 int main(int argc, char** argv) {
199 // Enable glog logging to stderr by default.
200 FLAGS_logtostderr = true;
201
202 ::testing::InitGoogleTest(&argc, argv);
203 folly::SingletonVault::singleton()->registrationComplete();
204
205 return RUN_ALL_TESTS();
206 }
207