1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2023 Google Inc. All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 //
8 // This file defines tests for the internal StringBlock class
9
10 #include "google/protobuf/string_block.h"
11
12 #include <cstddef>
13 #include <memory>
14 #include <string>
15 #include <vector>
16
17 #include <gmock/gmock.h>
18 #include <gtest/gtest.h>
19
20 // Must be included last
21 #include "google/protobuf/port_def.inc"
22
23 using ::testing::Eq;
24 using ::testing::Ne;
25
26 namespace google {
27 namespace protobuf {
28 namespace internal {
29 namespace {
30
EffectiveSizeFor(int size)31 size_t EffectiveSizeFor(int size) {
32 size -= sizeof(StringBlock);
33 return static_cast<size_t>(size - (size % sizeof(std::string)));
34 }
35
AllocatedSizeFor(int size)36 size_t AllocatedSizeFor(int size) {
37 return EffectiveSizeFor(size) + sizeof(StringBlock);
38 }
39
TEST(StringBlockTest,HeapAllocateOneBlock)40 TEST(StringBlockTest, HeapAllocateOneBlock) {
41 StringBlock* block = StringBlock::New(nullptr);
42
43 ASSERT_THAT(block, Ne(nullptr));
44 EXPECT_THAT(block->next(), Eq(nullptr));
45 ASSERT_TRUE(block->heap_allocated());
46 EXPECT_THAT(block->allocated_size(), Eq(AllocatedSizeFor(256)));
47 EXPECT_THAT(block->effective_size(), Eq(EffectiveSizeFor(256)));
48 EXPECT_THAT(block->begin(), Eq(block->AtOffset(0)));
49 EXPECT_THAT(block->end(), Eq(block->AtOffset(block->effective_size())));
50
51 EXPECT_THAT(StringBlock::Delete(block), Eq(AllocatedSizeFor(256)));
52 }
53
TEST(StringBlockTest,EmplaceOneBlock)54 TEST(StringBlockTest, EmplaceOneBlock) {
55 // NextSize() returns unrounded 'min_size()' on first call.
56 size_t size = StringBlock::NextSize(nullptr);
57 EXPECT_THAT(size, Eq(256));
58
59 auto buffer = std::make_unique<char[]>(size);
60 StringBlock* block = StringBlock::Emplace(buffer.get(), size, nullptr);
61
62 ASSERT_THAT(block, Ne(nullptr));
63 EXPECT_THAT(block->next(), Eq(nullptr));
64 ASSERT_FALSE(block->heap_allocated());
65 EXPECT_THAT(block->allocated_size(), Eq(AllocatedSizeFor(256)));
66 EXPECT_THAT(block->effective_size(), Eq(EffectiveSizeFor(256)));
67 EXPECT_THAT(block->begin(), Eq(block->AtOffset(0)));
68 EXPECT_THAT(block->end(), Eq(block->AtOffset(block->effective_size())));
69
70 EXPECT_THAT(StringBlock::Delete(block), Eq(0));
71 }
72
TEST(StringBlockTest,HeapAllocateMultipleBlocks)73 TEST(StringBlockTest, HeapAllocateMultipleBlocks) {
74 // Note: first two blocks are 256
75 StringBlock* previous = StringBlock::New(nullptr);
76
77 for (int size = 256; size <= 8192; size *= 2) {
78 StringBlock* block = StringBlock::New(previous);
79 ASSERT_THAT(block, Ne(nullptr));
80 ASSERT_THAT(block->next(), Eq(previous));
81 ASSERT_TRUE(block->heap_allocated());
82 ASSERT_THAT(block->allocated_size(), Eq(AllocatedSizeFor(size)));
83 ASSERT_THAT(block->effective_size(), Eq(EffectiveSizeFor(size)));
84 ASSERT_THAT(block->begin(), Eq(block->AtOffset(0)));
85 ASSERT_THAT(block->end(), Eq(block->AtOffset(block->effective_size())));
86 previous = block;
87 }
88
89 // Capped at 8K from here on
90 StringBlock* block = StringBlock::New(previous);
91 ASSERT_THAT(block, Ne(nullptr));
92 ASSERT_THAT(block->next(), Eq(previous));
93 ASSERT_TRUE(block->heap_allocated());
94 ASSERT_THAT(block->allocated_size(), Eq(AllocatedSizeFor(8192)));
95 ASSERT_THAT(block->effective_size(), Eq(EffectiveSizeFor(8192)));
96 ASSERT_THAT(block->begin(), Eq(block->AtOffset(0)));
97 ASSERT_THAT(block->end(), Eq(block->AtOffset(block->effective_size())));
98
99 while (block) {
100 size_t size = block->allocated_size();
101 StringBlock* next = block->next();
102 EXPECT_THAT(StringBlock::Delete(block), Eq(AllocatedSizeFor(size)));
103 block = next;
104 }
105 }
106
TEST(StringBlockTest,EmplaceMultipleBlocks)107 TEST(StringBlockTest, EmplaceMultipleBlocks) {
108 std::vector<std::unique_ptr<char[]>> buffers;
109
110 // Convenience lambda to allocate a buffer and invoke Emplace on it.
111 auto EmplaceBlock = [&](StringBlock* previous) {
112 size_t size = StringBlock::NextSize(previous);
113 buffers.push_back(std::make_unique<char[]>(size));
114 return StringBlock::Emplace(buffers.back().get(), size, previous);
115 };
116
117 // Note: first two blocks are 256
118 StringBlock* previous = EmplaceBlock(nullptr);
119
120 for (int size = 256; size <= 8192; size *= 2) {
121 StringBlock* block = EmplaceBlock(previous);
122 ASSERT_THAT(block, Ne(nullptr));
123 ASSERT_THAT(block->next(), Eq(previous));
124 ASSERT_FALSE(block->heap_allocated());
125 ASSERT_THAT(block->allocated_size(), Eq(AllocatedSizeFor(size)));
126 ASSERT_THAT(block->effective_size(), Eq(EffectiveSizeFor(size)));
127 ASSERT_THAT(block->begin(), Eq(block->AtOffset(0)));
128 ASSERT_THAT(block->end(), Eq(block->AtOffset(block->effective_size())));
129 previous = block;
130 }
131
132 // Capped at 8K from here on
133 StringBlock* block = EmplaceBlock(previous);
134 ASSERT_THAT(block, Ne(nullptr));
135 EXPECT_THAT(block->next(), Eq(previous));
136 ASSERT_FALSE(block->heap_allocated());
137 ASSERT_THAT(block->allocated_size(), Eq(AllocatedSizeFor(8192)));
138 ASSERT_THAT(block->effective_size(), Eq(EffectiveSizeFor(8192)));
139 ASSERT_THAT(block->begin(), Eq(block->AtOffset(0)));
140 ASSERT_THAT(block->end(), Eq(block->AtOffset(block->effective_size())));
141
142 while (block) {
143 StringBlock* next = block->next();
144 EXPECT_THAT(StringBlock::Delete(block), Eq(0));
145 block = next;
146 }
147 }
148
149 } // namespace
150 } // namespace internal
151 } // namespace protobuf
152 } // namespace google
153
154 #include "google/protobuf/port_undef.inc"
155