1*61c4878aSAndroid Build Coastguard Worker // Copyright 2023 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker // https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker
15*61c4878aSAndroid Build Coastguard Worker #include "pw_multibuf/chunk.h"
16*61c4878aSAndroid Build Coastguard Worker
17*61c4878aSAndroid Build Coastguard Worker #include <memory>
18*61c4878aSAndroid Build Coastguard Worker
19*61c4878aSAndroid Build Coastguard Worker #if __cplusplus >= 202002L
20*61c4878aSAndroid Build Coastguard Worker #include <ranges>
21*61c4878aSAndroid Build Coastguard Worker #endif // __cplusplus >= 202002L
22*61c4878aSAndroid Build Coastguard Worker
23*61c4878aSAndroid Build Coastguard Worker #include "pw_allocator/testing.h"
24*61c4878aSAndroid Build Coastguard Worker #include "pw_multibuf/header_chunk_region_tracker.h"
25*61c4878aSAndroid Build Coastguard Worker #include "pw_unit_test/framework.h"
26*61c4878aSAndroid Build Coastguard Worker
27*61c4878aSAndroid Build Coastguard Worker namespace pw::multibuf {
28*61c4878aSAndroid Build Coastguard Worker namespace {
29*61c4878aSAndroid Build Coastguard Worker
30*61c4878aSAndroid Build Coastguard Worker using ::pw::allocator::test::AllocatorForTest;
31*61c4878aSAndroid Build Coastguard Worker
32*61c4878aSAndroid Build Coastguard Worker /// Returns literal with ``_size`` suffix as a ``size_t``.
33*61c4878aSAndroid Build Coastguard Worker ///
34*61c4878aSAndroid Build Coastguard Worker /// This is useful for writing size-related test assertions without
35*61c4878aSAndroid Build Coastguard Worker /// explicit (verbose) casts.
operator ""_size(unsigned long long n)36*61c4878aSAndroid Build Coastguard Worker constexpr size_t operator""_size(unsigned long long n) { return n; }
37*61c4878aSAndroid Build Coastguard Worker
38*61c4878aSAndroid Build Coastguard Worker const size_t kArbitraryAllocatorSize = 1024;
39*61c4878aSAndroid Build Coastguard Worker const size_t kArbitraryChunkSize = 32;
40*61c4878aSAndroid Build Coastguard Worker
41*61c4878aSAndroid Build Coastguard Worker #if __cplusplus >= 202002L
42*61c4878aSAndroid Build Coastguard Worker static_assert(std::ranges::contiguous_range<Chunk>);
43*61c4878aSAndroid Build Coastguard Worker #endif // __cplusplus >= 202002L
44*61c4878aSAndroid Build Coastguard Worker
TakesSpan(ByteSpan span)45*61c4878aSAndroid Build Coastguard Worker void TakesSpan([[maybe_unused]] ByteSpan span) {}
46*61c4878aSAndroid Build Coastguard Worker
TEST(Chunk,IsImplicitlyConvertibleToSpan)47*61c4878aSAndroid Build Coastguard Worker TEST(Chunk, IsImplicitlyConvertibleToSpan) {
48*61c4878aSAndroid Build Coastguard Worker AllocatorForTest<kArbitraryAllocatorSize> allocator;
49*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk =
50*61c4878aSAndroid Build Coastguard Worker HeaderChunkRegionTracker::AllocateRegionAsChunk(allocator,
51*61c4878aSAndroid Build Coastguard Worker kArbitraryChunkSize);
52*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk.has_value());
53*61c4878aSAndroid Build Coastguard Worker // ``Chunk`` should convert to ``ByteSpan``.
54*61c4878aSAndroid Build Coastguard Worker TakesSpan(**chunk);
55*61c4878aSAndroid Build Coastguard Worker }
56*61c4878aSAndroid Build Coastguard Worker
TEST(OwnedChunk,ReleaseDestroysChunkRegion)57*61c4878aSAndroid Build Coastguard Worker TEST(OwnedChunk, ReleaseDestroysChunkRegion) {
58*61c4878aSAndroid Build Coastguard Worker AllocatorForTest<kArbitraryAllocatorSize> allocator;
59*61c4878aSAndroid Build Coastguard Worker const auto& metrics = allocator.metrics();
60*61c4878aSAndroid Build Coastguard Worker auto tracker =
61*61c4878aSAndroid Build Coastguard Worker HeaderChunkRegionTracker::AllocateRegion(allocator, kArbitraryChunkSize);
62*61c4878aSAndroid Build Coastguard Worker ASSERT_NE(tracker, nullptr);
63*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(metrics.num_allocations.value(), 1_size);
64*61c4878aSAndroid Build Coastguard Worker
65*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_opt = tracker->CreateFirstChunk();
66*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_opt.has_value());
67*61c4878aSAndroid Build Coastguard Worker auto& chunk = *chunk_opt;
68*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(metrics.num_allocations.value(), 2_size);
69*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk.size(), kArbitraryChunkSize);
70*61c4878aSAndroid Build Coastguard Worker
71*61c4878aSAndroid Build Coastguard Worker chunk.Release();
72*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk.size(), 0_size);
73*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(metrics.num_deallocations.value(), 2_size);
74*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(metrics.allocated_bytes.value(), 0_size);
75*61c4878aSAndroid Build Coastguard Worker }
76*61c4878aSAndroid Build Coastguard Worker
TEST(OwnedChunk,DestructorDestroysChunkRegion)77*61c4878aSAndroid Build Coastguard Worker TEST(OwnedChunk, DestructorDestroysChunkRegion) {
78*61c4878aSAndroid Build Coastguard Worker AllocatorForTest<kArbitraryAllocatorSize> allocator;
79*61c4878aSAndroid Build Coastguard Worker const auto& metrics = allocator.metrics();
80*61c4878aSAndroid Build Coastguard Worker auto tracker =
81*61c4878aSAndroid Build Coastguard Worker HeaderChunkRegionTracker::AllocateRegion(allocator, kArbitraryChunkSize);
82*61c4878aSAndroid Build Coastguard Worker ASSERT_NE(tracker, nullptr);
83*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(metrics.num_allocations.value(), 1_size);
84*61c4878aSAndroid Build Coastguard Worker
85*61c4878aSAndroid Build Coastguard Worker {
86*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk = tracker->CreateFirstChunk();
87*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk.has_value());
88*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(metrics.num_allocations.value(), 2_size);
89*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk->size(), kArbitraryChunkSize);
90*61c4878aSAndroid Build Coastguard Worker }
91*61c4878aSAndroid Build Coastguard Worker
92*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(metrics.num_deallocations.value(), 2_size);
93*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(metrics.allocated_bytes.value(), 0_size);
94*61c4878aSAndroid Build Coastguard Worker }
95*61c4878aSAndroid Build Coastguard Worker
TEST(Chunk,DiscardPrefixDiscardsPrefixOfSpan)96*61c4878aSAndroid Build Coastguard Worker TEST(Chunk, DiscardPrefixDiscardsPrefixOfSpan) {
97*61c4878aSAndroid Build Coastguard Worker AllocatorForTest<kArbitraryAllocatorSize> allocator;
98*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_opt =
99*61c4878aSAndroid Build Coastguard Worker HeaderChunkRegionTracker::AllocateRegionAsChunk(allocator,
100*61c4878aSAndroid Build Coastguard Worker kArbitraryChunkSize);
101*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_opt.has_value());
102*61c4878aSAndroid Build Coastguard Worker auto& chunk = *chunk_opt;
103*61c4878aSAndroid Build Coastguard Worker ConstByteSpan old_span = chunk;
104*61c4878aSAndroid Build Coastguard Worker const size_t kDiscarded = 4;
105*61c4878aSAndroid Build Coastguard Worker chunk->DiscardPrefix(kDiscarded);
106*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk.size(), old_span.size() - kDiscarded);
107*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk.data(), old_span.data() + kDiscarded);
108*61c4878aSAndroid Build Coastguard Worker }
109*61c4878aSAndroid Build Coastguard Worker
TEST(Chunk,TakePrefixTakesPrefixOfSpan)110*61c4878aSAndroid Build Coastguard Worker TEST(Chunk, TakePrefixTakesPrefixOfSpan) {
111*61c4878aSAndroid Build Coastguard Worker AllocatorForTest<kArbitraryAllocatorSize> allocator;
112*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_opt =
113*61c4878aSAndroid Build Coastguard Worker HeaderChunkRegionTracker::AllocateRegionAsChunk(allocator,
114*61c4878aSAndroid Build Coastguard Worker kArbitraryChunkSize);
115*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_opt.has_value());
116*61c4878aSAndroid Build Coastguard Worker auto& chunk = *chunk_opt;
117*61c4878aSAndroid Build Coastguard Worker ConstByteSpan old_span = chunk;
118*61c4878aSAndroid Build Coastguard Worker const size_t kTaken = 4;
119*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> front_opt = chunk->TakePrefix(kTaken);
120*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(front_opt.has_value());
121*61c4878aSAndroid Build Coastguard Worker auto& front = *front_opt;
122*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(front->size(), kTaken);
123*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(front->data(), old_span.data());
124*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk.size(), old_span.size() - kTaken);
125*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk.data(), old_span.data() + kTaken);
126*61c4878aSAndroid Build Coastguard Worker }
127*61c4878aSAndroid Build Coastguard Worker
TEST(Chunk,TruncateDiscardsEndOfSpan)128*61c4878aSAndroid Build Coastguard Worker TEST(Chunk, TruncateDiscardsEndOfSpan) {
129*61c4878aSAndroid Build Coastguard Worker AllocatorForTest<kArbitraryAllocatorSize> allocator;
130*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_opt =
131*61c4878aSAndroid Build Coastguard Worker HeaderChunkRegionTracker::AllocateRegionAsChunk(allocator,
132*61c4878aSAndroid Build Coastguard Worker kArbitraryChunkSize);
133*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_opt.has_value());
134*61c4878aSAndroid Build Coastguard Worker auto& chunk = *chunk_opt;
135*61c4878aSAndroid Build Coastguard Worker ConstByteSpan old_span = chunk;
136*61c4878aSAndroid Build Coastguard Worker const size_t kShorter = 5;
137*61c4878aSAndroid Build Coastguard Worker chunk->Truncate(old_span.size() - kShorter);
138*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk.size(), old_span.size() - kShorter);
139*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk.data(), old_span.data());
140*61c4878aSAndroid Build Coastguard Worker }
141*61c4878aSAndroid Build Coastguard Worker
TEST(Chunk,TakeSuffixTakesEndOfSpan)142*61c4878aSAndroid Build Coastguard Worker TEST(Chunk, TakeSuffixTakesEndOfSpan) {
143*61c4878aSAndroid Build Coastguard Worker AllocatorForTest<kArbitraryAllocatorSize> allocator;
144*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_opt =
145*61c4878aSAndroid Build Coastguard Worker HeaderChunkRegionTracker::AllocateRegionAsChunk(allocator,
146*61c4878aSAndroid Build Coastguard Worker kArbitraryChunkSize);
147*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_opt.has_value());
148*61c4878aSAndroid Build Coastguard Worker auto& chunk = *chunk_opt;
149*61c4878aSAndroid Build Coastguard Worker ConstByteSpan old_span = chunk;
150*61c4878aSAndroid Build Coastguard Worker const size_t kTaken = 5;
151*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> tail_opt = chunk->TakeSuffix(kTaken);
152*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(tail_opt.has_value());
153*61c4878aSAndroid Build Coastguard Worker auto& tail = *tail_opt;
154*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(tail.size(), kTaken);
155*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(tail.data(), old_span.data() + old_span.size() - kTaken);
156*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk.size(), old_span.size() - kTaken);
157*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk.data(), old_span.data());
158*61c4878aSAndroid Build Coastguard Worker }
159*61c4878aSAndroid Build Coastguard Worker
TEST(Chunk,SliceRemovesSidesOfSpan)160*61c4878aSAndroid Build Coastguard Worker TEST(Chunk, SliceRemovesSidesOfSpan) {
161*61c4878aSAndroid Build Coastguard Worker AllocatorForTest<kArbitraryAllocatorSize> allocator;
162*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_opt =
163*61c4878aSAndroid Build Coastguard Worker HeaderChunkRegionTracker::AllocateRegionAsChunk(allocator,
164*61c4878aSAndroid Build Coastguard Worker kArbitraryChunkSize);
165*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_opt.has_value());
166*61c4878aSAndroid Build Coastguard Worker auto& chunk = *chunk_opt;
167*61c4878aSAndroid Build Coastguard Worker ConstByteSpan old_span = chunk;
168*61c4878aSAndroid Build Coastguard Worker const size_t kBegin = 4;
169*61c4878aSAndroid Build Coastguard Worker const size_t kEnd = 9;
170*61c4878aSAndroid Build Coastguard Worker chunk->Slice(kBegin, kEnd);
171*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk.data(), old_span.data() + kBegin);
172*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk.size(), kEnd - kBegin);
173*61c4878aSAndroid Build Coastguard Worker }
174*61c4878aSAndroid Build Coastguard Worker
TEST(Chunk,RegionPersistsUntilAllChunksReleased)175*61c4878aSAndroid Build Coastguard Worker TEST(Chunk, RegionPersistsUntilAllChunksReleased) {
176*61c4878aSAndroid Build Coastguard Worker AllocatorForTest<kArbitraryAllocatorSize> allocator;
177*61c4878aSAndroid Build Coastguard Worker const auto& metrics = allocator.metrics();
178*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_opt =
179*61c4878aSAndroid Build Coastguard Worker HeaderChunkRegionTracker::AllocateRegionAsChunk(allocator,
180*61c4878aSAndroid Build Coastguard Worker kArbitraryChunkSize);
181*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_opt.has_value());
182*61c4878aSAndroid Build Coastguard Worker auto& chunk = *chunk_opt;
183*61c4878aSAndroid Build Coastguard Worker // One allocation for the region tracker, one for the chunk.
184*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(metrics.num_allocations.value(), 2_size);
185*61c4878aSAndroid Build Coastguard Worker const size_t kSplitPoint = 13;
186*61c4878aSAndroid Build Coastguard Worker auto split_opt = chunk->TakePrefix(kSplitPoint);
187*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(split_opt.has_value());
188*61c4878aSAndroid Build Coastguard Worker auto& split = *split_opt;
189*61c4878aSAndroid Build Coastguard Worker // One allocation for the region tracker, one for each of two chunks.
190*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(metrics.num_allocations.value(), 3_size);
191*61c4878aSAndroid Build Coastguard Worker chunk.Release();
192*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(metrics.num_deallocations.value(), 1_size);
193*61c4878aSAndroid Build Coastguard Worker split.Release();
194*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(metrics.num_deallocations.value(), 3_size);
195*61c4878aSAndroid Build Coastguard Worker }
196*61c4878aSAndroid Build Coastguard Worker
TEST(Chunk,ClaimPrefixReclaimsDiscardedPrefix)197*61c4878aSAndroid Build Coastguard Worker TEST(Chunk, ClaimPrefixReclaimsDiscardedPrefix) {
198*61c4878aSAndroid Build Coastguard Worker AllocatorForTest<kArbitraryAllocatorSize> allocator;
199*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_opt =
200*61c4878aSAndroid Build Coastguard Worker HeaderChunkRegionTracker::AllocateRegionAsChunk(allocator,
201*61c4878aSAndroid Build Coastguard Worker kArbitraryChunkSize);
202*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_opt.has_value());
203*61c4878aSAndroid Build Coastguard Worker auto& chunk = *chunk_opt;
204*61c4878aSAndroid Build Coastguard Worker ConstByteSpan old_span = chunk;
205*61c4878aSAndroid Build Coastguard Worker const size_t kDiscarded = 4;
206*61c4878aSAndroid Build Coastguard Worker chunk->DiscardPrefix(kDiscarded);
207*61c4878aSAndroid Build Coastguard Worker EXPECT_TRUE(chunk->ClaimPrefix(kDiscarded));
208*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk.size(), old_span.size());
209*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk.data(), old_span.data());
210*61c4878aSAndroid Build Coastguard Worker }
211*61c4878aSAndroid Build Coastguard Worker
TEST(Chunk,ClaimPrefixFailsOnFullRegionChunk)212*61c4878aSAndroid Build Coastguard Worker TEST(Chunk, ClaimPrefixFailsOnFullRegionChunk) {
213*61c4878aSAndroid Build Coastguard Worker AllocatorForTest<kArbitraryAllocatorSize> allocator;
214*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_opt =
215*61c4878aSAndroid Build Coastguard Worker HeaderChunkRegionTracker::AllocateRegionAsChunk(allocator,
216*61c4878aSAndroid Build Coastguard Worker kArbitraryChunkSize);
217*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_opt.has_value());
218*61c4878aSAndroid Build Coastguard Worker auto& chunk = *chunk_opt;
219*61c4878aSAndroid Build Coastguard Worker EXPECT_FALSE(chunk->ClaimPrefix(1));
220*61c4878aSAndroid Build Coastguard Worker }
221*61c4878aSAndroid Build Coastguard Worker
TEST(Chunk,ClaimPrefixFailsOnNeighboringChunk)222*61c4878aSAndroid Build Coastguard Worker TEST(Chunk, ClaimPrefixFailsOnNeighboringChunk) {
223*61c4878aSAndroid Build Coastguard Worker AllocatorForTest<kArbitraryAllocatorSize> allocator;
224*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_opt =
225*61c4878aSAndroid Build Coastguard Worker HeaderChunkRegionTracker::AllocateRegionAsChunk(allocator,
226*61c4878aSAndroid Build Coastguard Worker kArbitraryChunkSize);
227*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_opt.has_value());
228*61c4878aSAndroid Build Coastguard Worker auto& chunk = *chunk_opt;
229*61c4878aSAndroid Build Coastguard Worker const size_t kSplitPoint = 22;
230*61c4878aSAndroid Build Coastguard Worker auto front = chunk->TakePrefix(kSplitPoint);
231*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(front.has_value());
232*61c4878aSAndroid Build Coastguard Worker EXPECT_FALSE(chunk->ClaimPrefix(1));
233*61c4878aSAndroid Build Coastguard Worker }
234*61c4878aSAndroid Build Coastguard Worker
TEST(Chunk,ClaimPrefixFailsAtStartOfRegionEvenAfterReleasingChunkAtEndOfRegion)235*61c4878aSAndroid Build Coastguard Worker TEST(Chunk,
236*61c4878aSAndroid Build Coastguard Worker ClaimPrefixFailsAtStartOfRegionEvenAfterReleasingChunkAtEndOfRegion) {
237*61c4878aSAndroid Build Coastguard Worker AllocatorForTest<kArbitraryAllocatorSize> allocator;
238*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_opt =
239*61c4878aSAndroid Build Coastguard Worker HeaderChunkRegionTracker::AllocateRegionAsChunk(allocator,
240*61c4878aSAndroid Build Coastguard Worker kArbitraryChunkSize);
241*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_opt.has_value());
242*61c4878aSAndroid Build Coastguard Worker auto& chunk = *chunk_opt;
243*61c4878aSAndroid Build Coastguard Worker const size_t kTaken = 13;
244*61c4878aSAndroid Build Coastguard Worker auto split = chunk->TakeSuffix(kTaken);
245*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(split.has_value());
246*61c4878aSAndroid Build Coastguard Worker split->Release();
247*61c4878aSAndroid Build Coastguard Worker EXPECT_FALSE(chunk->ClaimPrefix(1));
248*61c4878aSAndroid Build Coastguard Worker }
249*61c4878aSAndroid Build Coastguard Worker
TEST(Chunk,ClaimPrefixReclaimsPrecedingChunksDiscardedSuffix)250*61c4878aSAndroid Build Coastguard Worker TEST(Chunk, ClaimPrefixReclaimsPrecedingChunksDiscardedSuffix) {
251*61c4878aSAndroid Build Coastguard Worker AllocatorForTest<kArbitraryAllocatorSize> allocator;
252*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_opt =
253*61c4878aSAndroid Build Coastguard Worker HeaderChunkRegionTracker::AllocateRegionAsChunk(allocator,
254*61c4878aSAndroid Build Coastguard Worker kArbitraryChunkSize);
255*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_opt.has_value());
256*61c4878aSAndroid Build Coastguard Worker auto& chunk = *chunk_opt;
257*61c4878aSAndroid Build Coastguard Worker const size_t kSplitPoint = 13;
258*61c4878aSAndroid Build Coastguard Worker auto split_opt = chunk->TakePrefix(kSplitPoint);
259*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(split_opt.has_value());
260*61c4878aSAndroid Build Coastguard Worker auto& split = *split_opt;
261*61c4878aSAndroid Build Coastguard Worker const size_t kDiscard = 3;
262*61c4878aSAndroid Build Coastguard Worker split->Truncate(split.size() - kDiscard);
263*61c4878aSAndroid Build Coastguard Worker EXPECT_TRUE(chunk->ClaimPrefix(kDiscard));
264*61c4878aSAndroid Build Coastguard Worker EXPECT_FALSE(chunk->ClaimPrefix(1));
265*61c4878aSAndroid Build Coastguard Worker }
266*61c4878aSAndroid Build Coastguard Worker
TEST(Chunk,ClaimSuffixReclaimsTruncatedEnd)267*61c4878aSAndroid Build Coastguard Worker TEST(Chunk, ClaimSuffixReclaimsTruncatedEnd) {
268*61c4878aSAndroid Build Coastguard Worker AllocatorForTest<kArbitraryAllocatorSize> allocator;
269*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_opt =
270*61c4878aSAndroid Build Coastguard Worker HeaderChunkRegionTracker::AllocateRegionAsChunk(allocator,
271*61c4878aSAndroid Build Coastguard Worker kArbitraryChunkSize);
272*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_opt.has_value());
273*61c4878aSAndroid Build Coastguard Worker auto& chunk = *chunk_opt;
274*61c4878aSAndroid Build Coastguard Worker ConstByteSpan old_span = *chunk;
275*61c4878aSAndroid Build Coastguard Worker const size_t kDiscarded = 4;
276*61c4878aSAndroid Build Coastguard Worker chunk->Truncate(old_span.size() - kDiscarded);
277*61c4878aSAndroid Build Coastguard Worker EXPECT_TRUE(chunk->ClaimSuffix(kDiscarded));
278*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk->size(), old_span.size());
279*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk->data(), old_span.data());
280*61c4878aSAndroid Build Coastguard Worker }
281*61c4878aSAndroid Build Coastguard Worker
TEST(Chunk,ClaimSuffixFailsOnFullRegionChunk)282*61c4878aSAndroid Build Coastguard Worker TEST(Chunk, ClaimSuffixFailsOnFullRegionChunk) {
283*61c4878aSAndroid Build Coastguard Worker AllocatorForTest<kArbitraryAllocatorSize> allocator;
284*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_opt =
285*61c4878aSAndroid Build Coastguard Worker HeaderChunkRegionTracker::AllocateRegionAsChunk(allocator,
286*61c4878aSAndroid Build Coastguard Worker kArbitraryChunkSize);
287*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_opt.has_value());
288*61c4878aSAndroid Build Coastguard Worker auto& chunk = *chunk_opt;
289*61c4878aSAndroid Build Coastguard Worker EXPECT_FALSE(chunk->ClaimSuffix(1));
290*61c4878aSAndroid Build Coastguard Worker }
291*61c4878aSAndroid Build Coastguard Worker
TEST(Chunk,ClaimSuffixFailsWithNeighboringChunk)292*61c4878aSAndroid Build Coastguard Worker TEST(Chunk, ClaimSuffixFailsWithNeighboringChunk) {
293*61c4878aSAndroid Build Coastguard Worker AllocatorForTest<kArbitraryAllocatorSize> allocator;
294*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_opt =
295*61c4878aSAndroid Build Coastguard Worker HeaderChunkRegionTracker::AllocateRegionAsChunk(allocator,
296*61c4878aSAndroid Build Coastguard Worker kArbitraryChunkSize);
297*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_opt.has_value());
298*61c4878aSAndroid Build Coastguard Worker auto& chunk = *chunk_opt;
299*61c4878aSAndroid Build Coastguard Worker const size_t kSplitPoint = 22;
300*61c4878aSAndroid Build Coastguard Worker auto split_opt = chunk->TakePrefix(kSplitPoint);
301*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(split_opt.has_value());
302*61c4878aSAndroid Build Coastguard Worker auto& split = *split_opt;
303*61c4878aSAndroid Build Coastguard Worker EXPECT_FALSE(split->ClaimSuffix(1));
304*61c4878aSAndroid Build Coastguard Worker }
305*61c4878aSAndroid Build Coastguard Worker
TEST(Chunk,ClaimSuffixFailsAtEndOfRegionEvenAfterReleasingFirstChunkInRegion)306*61c4878aSAndroid Build Coastguard Worker TEST(Chunk, ClaimSuffixFailsAtEndOfRegionEvenAfterReleasingFirstChunkInRegion) {
307*61c4878aSAndroid Build Coastguard Worker AllocatorForTest<kArbitraryAllocatorSize> allocator;
308*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_opt =
309*61c4878aSAndroid Build Coastguard Worker HeaderChunkRegionTracker::AllocateRegionAsChunk(allocator,
310*61c4878aSAndroid Build Coastguard Worker kArbitraryChunkSize);
311*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_opt.has_value());
312*61c4878aSAndroid Build Coastguard Worker auto& chunk = *chunk_opt;
313*61c4878aSAndroid Build Coastguard Worker const size_t kTaken = 22;
314*61c4878aSAndroid Build Coastguard Worker auto split_opt = chunk->TakeSuffix(kTaken);
315*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(split_opt.has_value());
316*61c4878aSAndroid Build Coastguard Worker auto& split = *split_opt;
317*61c4878aSAndroid Build Coastguard Worker EXPECT_FALSE(split->ClaimSuffix(1));
318*61c4878aSAndroid Build Coastguard Worker }
319*61c4878aSAndroid Build Coastguard Worker
TEST(Chunk,ClaimSuffixReclaimsFollowingChunksDiscardedPrefix)320*61c4878aSAndroid Build Coastguard Worker TEST(Chunk, ClaimSuffixReclaimsFollowingChunksDiscardedPrefix) {
321*61c4878aSAndroid Build Coastguard Worker AllocatorForTest<kArbitraryAllocatorSize> allocator;
322*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_opt =
323*61c4878aSAndroid Build Coastguard Worker HeaderChunkRegionTracker::AllocateRegionAsChunk(allocator,
324*61c4878aSAndroid Build Coastguard Worker kArbitraryChunkSize);
325*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_opt.has_value());
326*61c4878aSAndroid Build Coastguard Worker auto& chunk = *chunk_opt;
327*61c4878aSAndroid Build Coastguard Worker const size_t kSplitPoint = 22;
328*61c4878aSAndroid Build Coastguard Worker auto split_opt = chunk->TakePrefix(kSplitPoint);
329*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(split_opt.has_value());
330*61c4878aSAndroid Build Coastguard Worker auto& split = *split_opt;
331*61c4878aSAndroid Build Coastguard Worker const size_t kDiscarded = 3;
332*61c4878aSAndroid Build Coastguard Worker chunk->DiscardPrefix(kDiscarded);
333*61c4878aSAndroid Build Coastguard Worker EXPECT_TRUE(split->ClaimSuffix(kDiscarded));
334*61c4878aSAndroid Build Coastguard Worker EXPECT_FALSE(split->ClaimSuffix(1));
335*61c4878aSAndroid Build Coastguard Worker }
336*61c4878aSAndroid Build Coastguard Worker
TEST(Chunk,MergeReturnsFalseForChunksFromDifferentRegions)337*61c4878aSAndroid Build Coastguard Worker TEST(Chunk, MergeReturnsFalseForChunksFromDifferentRegions) {
338*61c4878aSAndroid Build Coastguard Worker AllocatorForTest<kArbitraryAllocatorSize> allocator;
339*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_1_opt =
340*61c4878aSAndroid Build Coastguard Worker HeaderChunkRegionTracker::AllocateRegionAsChunk(allocator,
341*61c4878aSAndroid Build Coastguard Worker kArbitraryChunkSize);
342*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_1_opt.has_value());
343*61c4878aSAndroid Build Coastguard Worker OwnedChunk& chunk_1 = *chunk_1_opt;
344*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_2_opt =
345*61c4878aSAndroid Build Coastguard Worker HeaderChunkRegionTracker::AllocateRegionAsChunk(allocator,
346*61c4878aSAndroid Build Coastguard Worker kArbitraryChunkSize);
347*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_2_opt.has_value());
348*61c4878aSAndroid Build Coastguard Worker OwnedChunk& chunk_2 = *chunk_2_opt;
349*61c4878aSAndroid Build Coastguard Worker EXPECT_FALSE(chunk_1->CanMerge(*chunk_2));
350*61c4878aSAndroid Build Coastguard Worker EXPECT_FALSE(chunk_1->Merge(chunk_2));
351*61c4878aSAndroid Build Coastguard Worker // Ensure that neither chunk was modified
352*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk_1.size(), kArbitraryChunkSize);
353*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk_2.size(), kArbitraryChunkSize);
354*61c4878aSAndroid Build Coastguard Worker }
355*61c4878aSAndroid Build Coastguard Worker
TEST(Chunk,MergeReturnsFalseForNonAdjacentChunksFromSameRegion)356*61c4878aSAndroid Build Coastguard Worker TEST(Chunk, MergeReturnsFalseForNonAdjacentChunksFromSameRegion) {
357*61c4878aSAndroid Build Coastguard Worker const size_t kTakenFromOne = 8;
358*61c4878aSAndroid Build Coastguard Worker const size_t kTakenFromTwo = 4;
359*61c4878aSAndroid Build Coastguard Worker
360*61c4878aSAndroid Build Coastguard Worker AllocatorForTest<kArbitraryAllocatorSize> allocator;
361*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_1_opt =
362*61c4878aSAndroid Build Coastguard Worker HeaderChunkRegionTracker::AllocateRegionAsChunk(allocator,
363*61c4878aSAndroid Build Coastguard Worker kArbitraryChunkSize);
364*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_1_opt.has_value());
365*61c4878aSAndroid Build Coastguard Worker OwnedChunk& chunk_1 = *chunk_1_opt;
366*61c4878aSAndroid Build Coastguard Worker
367*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_2_opt = chunk_1->TakeSuffix(kTakenFromOne);
368*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_2_opt.has_value());
369*61c4878aSAndroid Build Coastguard Worker OwnedChunk& chunk_2 = *chunk_2_opt;
370*61c4878aSAndroid Build Coastguard Worker
371*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_3_opt = chunk_2->TakeSuffix(kTakenFromTwo);
372*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_3_opt.has_value());
373*61c4878aSAndroid Build Coastguard Worker OwnedChunk& chunk_3 = *chunk_3_opt;
374*61c4878aSAndroid Build Coastguard Worker
375*61c4878aSAndroid Build Coastguard Worker EXPECT_FALSE(chunk_1->CanMerge(*chunk_3));
376*61c4878aSAndroid Build Coastguard Worker EXPECT_FALSE(chunk_1->Merge(chunk_3));
377*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk_1.size(), kArbitraryChunkSize - kTakenFromOne);
378*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk_2.size(), kTakenFromOne - kTakenFromTwo);
379*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk_3.size(), kTakenFromTwo);
380*61c4878aSAndroid Build Coastguard Worker }
381*61c4878aSAndroid Build Coastguard Worker
TEST(Chunk,MergeJoinsMultipleAdjacentChunksFromSameRegion)382*61c4878aSAndroid Build Coastguard Worker TEST(Chunk, MergeJoinsMultipleAdjacentChunksFromSameRegion) {
383*61c4878aSAndroid Build Coastguard Worker const size_t kTakenFromOne = 8;
384*61c4878aSAndroid Build Coastguard Worker const size_t kTakenFromTwo = 4;
385*61c4878aSAndroid Build Coastguard Worker
386*61c4878aSAndroid Build Coastguard Worker AllocatorForTest<kArbitraryAllocatorSize> allocator;
387*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_1_opt =
388*61c4878aSAndroid Build Coastguard Worker HeaderChunkRegionTracker::AllocateRegionAsChunk(allocator,
389*61c4878aSAndroid Build Coastguard Worker kArbitraryChunkSize);
390*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_1_opt.has_value());
391*61c4878aSAndroid Build Coastguard Worker OwnedChunk& chunk_1 = *chunk_1_opt;
392*61c4878aSAndroid Build Coastguard Worker
393*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_2_opt = chunk_1->TakeSuffix(kTakenFromOne);
394*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_2_opt.has_value());
395*61c4878aSAndroid Build Coastguard Worker OwnedChunk& chunk_2 = *chunk_2_opt;
396*61c4878aSAndroid Build Coastguard Worker
397*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_3_opt = chunk_2->TakeSuffix(kTakenFromTwo);
398*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_3_opt.has_value());
399*61c4878aSAndroid Build Coastguard Worker OwnedChunk& chunk_3 = *chunk_3_opt;
400*61c4878aSAndroid Build Coastguard Worker
401*61c4878aSAndroid Build Coastguard Worker EXPECT_TRUE(chunk_1->CanMerge(*chunk_2));
402*61c4878aSAndroid Build Coastguard Worker EXPECT_TRUE(chunk_1->Merge(chunk_2));
403*61c4878aSAndroid Build Coastguard Worker EXPECT_TRUE(chunk_1->CanMerge(*chunk_3));
404*61c4878aSAndroid Build Coastguard Worker EXPECT_TRUE(chunk_1->Merge(chunk_3));
405*61c4878aSAndroid Build Coastguard Worker
406*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk_1.size(), kArbitraryChunkSize);
407*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk_2.size(), 0_size);
408*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk_3.size(), 0_size);
409*61c4878aSAndroid Build Coastguard Worker }
410*61c4878aSAndroid Build Coastguard Worker
TEST(Chunk,MergeJoinsAdjacentChunksFromSameRegion)411*61c4878aSAndroid Build Coastguard Worker TEST(Chunk, MergeJoinsAdjacentChunksFromSameRegion) {
412*61c4878aSAndroid Build Coastguard Worker const size_t kTaken = 4;
413*61c4878aSAndroid Build Coastguard Worker
414*61c4878aSAndroid Build Coastguard Worker AllocatorForTest<kArbitraryAllocatorSize> allocator;
415*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_1_opt =
416*61c4878aSAndroid Build Coastguard Worker HeaderChunkRegionTracker::AllocateRegionAsChunk(allocator,
417*61c4878aSAndroid Build Coastguard Worker kArbitraryChunkSize);
418*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_1_opt.has_value());
419*61c4878aSAndroid Build Coastguard Worker OwnedChunk& chunk_1 = *chunk_1_opt;
420*61c4878aSAndroid Build Coastguard Worker std::optional<OwnedChunk> chunk_2_opt = chunk_1->TakeSuffix(kTaken);
421*61c4878aSAndroid Build Coastguard Worker ASSERT_TRUE(chunk_2_opt.has_value());
422*61c4878aSAndroid Build Coastguard Worker OwnedChunk& chunk_2 = *chunk_2_opt;
423*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk_1.size(), kArbitraryChunkSize - kTaken);
424*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk_2.size(), kTaken);
425*61c4878aSAndroid Build Coastguard Worker
426*61c4878aSAndroid Build Coastguard Worker EXPECT_TRUE(chunk_1->CanMerge(*chunk_2));
427*61c4878aSAndroid Build Coastguard Worker EXPECT_TRUE(chunk_1->Merge(chunk_2));
428*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk_1.size(), kArbitraryChunkSize);
429*61c4878aSAndroid Build Coastguard Worker EXPECT_EQ(chunk_2.size(), 0_size);
430*61c4878aSAndroid Build Coastguard Worker }
431*61c4878aSAndroid Build Coastguard Worker
432*61c4878aSAndroid Build Coastguard Worker } // namespace
433*61c4878aSAndroid Build Coastguard Worker } // namespace pw::multibuf
434