1 // Copyright 2021 The Abseil Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #ifndef ABSL_STRINGS_INTERNAL_CORD_REP_TEST_UTIL_H_
16 #define ABSL_STRINGS_INTERNAL_CORD_REP_TEST_UTIL_H_
17
18 #include <cassert>
19 #include <memory>
20 #include <random>
21 #include <string>
22 #include <vector>
23
24 #include "absl/base/config.h"
25 #include "absl/base/internal/raw_logging.h"
26 #include "absl/strings/internal/cord_internal.h"
27 #include "absl/strings/internal/cord_rep_btree.h"
28 #include "absl/strings/internal/cord_rep_flat.h"
29 #include "absl/strings/string_view.h"
30
31 namespace absl {
32 ABSL_NAMESPACE_BEGIN
33 namespace cordrep_testing {
34
MakeSubstring(size_t start,size_t len,cord_internal::CordRep * rep)35 inline cord_internal::CordRepSubstring* MakeSubstring(
36 size_t start, size_t len, cord_internal::CordRep* rep) {
37 auto* sub = new cord_internal::CordRepSubstring;
38 sub->tag = cord_internal::SUBSTRING;
39 sub->start = start;
40 sub->length = len <= 0 ? rep->length - start + len : len;
41 sub->child = rep;
42 return sub;
43 }
44
45 inline cord_internal::CordRepConcat* MakeConcat(cord_internal::CordRep* left,
46 cord_internal::CordRep* right,
47 int depth = 0) {
48 auto* concat = new cord_internal::CordRepConcat;
49 concat->tag = cord_internal::CONCAT;
50 concat->length = left->length + right->length;
51 concat->left = left;
52 concat->right = right;
53 concat->set_depth(depth);
54 return concat;
55 }
56
MakeFlat(absl::string_view value)57 inline cord_internal::CordRepFlat* MakeFlat(absl::string_view value) {
58 assert(value.length() <= cord_internal::kMaxFlatLength);
59 auto* flat = cord_internal::CordRepFlat::New(value.length());
60 flat->length = value.length();
61 memcpy(flat->Data(), value.data(), value.length());
62 return flat;
63 }
64
65 // Creates an external node for testing
MakeExternal(absl::string_view s)66 inline cord_internal::CordRepExternal* MakeExternal(absl::string_view s) {
67 struct Rep : public cord_internal::CordRepExternal {
68 std::string s;
69 explicit Rep(absl::string_view sv) : s(sv) {
70 this->tag = cord_internal::EXTERNAL;
71 this->base = s.data();
72 this->length = s.length();
73 this->releaser_invoker = [](cord_internal::CordRepExternal* self) {
74 delete static_cast<Rep*>(self);
75 };
76 }
77 };
78 return new Rep(s);
79 }
80
CreateRandomString(size_t n)81 inline std::string CreateRandomString(size_t n) {
82 absl::string_view data =
83 "abcdefghijklmnopqrstuvwxyz"
84 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
85 "0123456789~!@#$%^&*()_+=-<>?:\"{}[]|";
86 std::minstd_rand rnd;
87 std::uniform_int_distribution<size_t> dist(0, data.size() - 1);
88 std::string s(n, ' ');
89 for (size_t i = 0; i < n; ++i) {
90 s[i] = data[dist(rnd)];
91 }
92 return s;
93 }
94
95 // Creates an array of flats from the provided string, chopping
96 // the provided string up into flats of size `chunk_size` characters
97 // resulting in roughly `data.size() / chunk_size` total flats.
CreateFlatsFromString(absl::string_view data,size_t chunk_size)98 inline std::vector<cord_internal::CordRep*> CreateFlatsFromString(
99 absl::string_view data, size_t chunk_size) {
100 assert(chunk_size > 0);
101 std::vector<cord_internal::CordRep*> flats;
102 for (absl::string_view s = data; !s.empty(); s.remove_prefix(chunk_size)) {
103 flats.push_back(MakeFlat(s.substr(0, chunk_size)));
104 }
105 return flats;
106 }
107
CordRepBtreeFromFlats(absl::Span<cord_internal::CordRep * const> flats)108 inline cord_internal::CordRepBtree* CordRepBtreeFromFlats(
109 absl::Span<cord_internal::CordRep* const> flats) {
110 assert(!flats.empty());
111 auto* node = cord_internal::CordRepBtree::Create(flats[0]);
112 for (size_t i = 1; i < flats.size(); ++i) {
113 node = cord_internal::CordRepBtree::Append(node, flats[i]);
114 }
115 return node;
116 }
117
CordToString(cord_internal::CordRep * rep,std::string & s)118 inline void CordToString(cord_internal::CordRep* rep, std::string& s) {
119 size_t offset = 0;
120 size_t length = rep->length;
121 while (rep->tag == cord_internal::SUBSTRING) {
122 offset += rep->substring()->start;
123 rep = rep->substring()->child;
124 }
125 if (rep->tag == cord_internal::BTREE) {
126 for (cord_internal::CordRep* edge : rep->btree()->Edges()) {
127 CordToString(edge, s);
128 }
129 } else if (rep->tag >= cord_internal::FLAT) {
130 s.append(rep->flat()->Data() + offset, length);
131 } else if (rep->tag == cord_internal::EXTERNAL) {
132 s.append(rep->external()->base + offset, length);
133 } else {
134 ABSL_RAW_LOG(FATAL, "Unsupported tag %d", rep->tag);
135 }
136 }
137
CordToString(cord_internal::CordRep * rep)138 inline std::string CordToString(cord_internal::CordRep* rep) {
139 std::string s;
140 s.reserve(rep->length);
141 CordToString(rep, s);
142 return s;
143 }
144
145 // RAII Helper class to automatically unref reps on destruction.
146 class AutoUnref {
147 public:
~AutoUnref()148 ~AutoUnref() {
149 for (CordRep* rep : unrefs_) CordRep::Unref(rep);
150 }
151
152 // Adds `rep` to the list of reps to be unreffed at destruction.
153 template <typename CordRepType>
Add(CordRepType * rep)154 CordRepType* Add(CordRepType* rep) {
155 unrefs_.push_back(rep);
156 return rep;
157 }
158
159 // Increments the reference count of `rep` by one, and adds it to
160 // the list of reps to be unreffed at destruction.
161 template <typename CordRepType>
Ref(CordRepType * rep)162 CordRepType* Ref(CordRepType* rep) {
163 unrefs_.push_back(CordRep::Ref(rep));
164 return rep;
165 }
166
167 // Increments the reference count of `rep` by one if `condition` is true,
168 // and adds it to the list of reps to be unreffed at destruction.
169 template <typename CordRepType>
RefIf(bool condition,CordRepType * rep)170 CordRepType* RefIf(bool condition, CordRepType* rep) {
171 if (condition) unrefs_.push_back(CordRep::Ref(rep));
172 return rep;
173 }
174
175 private:
176 using CordRep = absl::cord_internal::CordRep;
177
178 std::vector<CordRep*> unrefs_;
179 };
180
181 } // namespace cordrep_testing
182 ABSL_NAMESPACE_END
183 } // namespace absl
184
185 #endif // ABSL_STRINGS_INTERNAL_CORD_REP_TEST_UTIL_H_
186