1
2 /**
3 * Copyright (C) 2018-present MongoDB, Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the Server Side Public License, version 1,
7 * as published by MongoDB, Inc.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * Server Side Public License for more details.
13 *
14 * You should have received a copy of the Server Side Public License
15 * along with this program. If not, see
16 * <http://www.mongodb.com/licensing/server-side-public-license>.
17 *
18 * As a special exception, the copyright holders give permission to link the
19 * code of portions of this program with the OpenSSL library under certain
20 * conditions as described in each individual source file and distribute
21 * linked combinations including the program with the OpenSSL library. You
22 * must comply with the Server Side Public License in all respects for
23 * all of the code used other than as permitted herein. If you modify file(s)
24 * with this exception, you may extend this exception to your version of the
25 * file(s), but you are not obligated to do so. If you do not wish to do so,
26 * delete this exception statement from your version. If you delete this
27 * exception statement from all source files in the program, then also delete
28 * it in the license file.
29 */
30
31 #include "mongo/base/data_type_terminated.h"
32
33 #include "mongo/base/data_range.h"
34 #include "mongo/base/data_range_cursor.h"
35 #include "mongo/unittest/unittest.h"
36 #include "mongo/util/stringutils.h"
37 #include <string>
38
39 namespace mongo {
40 namespace {
41
42 // For testing purposes, a type that has a fixed load and store size, and some
43 // arbitrary serialization format of 'd' repeated N times.
44 template <size_t N>
45 struct Dummy {
46 static constexpr size_t extent = N;
47 };
48 } // namespace
49 // Pop the anonymous namespace to specialize mongo::DataType::Handler.
50 // Template specialization is a drag.
51
52 template <size_t N>
53 struct DataType::Handler<Dummy<N>> {
54 using handledType = Dummy<N>;
55 static constexpr size_t extent = handledType::extent;
56
loadmongo::DataType::Handler57 static Status load(handledType* sdata,
58 const char* ptr,
59 size_t length,
60 size_t* advanced,
61 std::ptrdiff_t debug_offset) {
62 if (length < extent) {
63 return Status(ErrorCodes::Overflow, "too short for Dummy");
64 }
65 for (size_t i = 0; i < extent; ++i) {
66 if (*ptr++ != 'd') {
67 return Status(ErrorCodes::Overflow, "load of invalid Dummy object");
68 }
69 }
70 *advanced = extent;
71 return Status::OK();
72 }
73
storemongo::DataType::Handler74 static Status store(const handledType& sdata,
75 char* ptr,
76 size_t length,
77 size_t* advanced,
78 std::ptrdiff_t debug_offset) {
79 if (length < extent) {
80 return Status(ErrorCodes::Overflow, "insufficient space for Dummy");
81 }
82 for (size_t i = 0; i < extent; ++i) {
83 *ptr++ = 'd';
84 }
85 *advanced = extent;
86 return Status::OK();
87 }
88
defaultConstructmongo::DataType::Handler89 static handledType defaultConstruct() {
90 return {};
91 }
92 };
93
94 // Re-push the anonymous namespace.
95 namespace {
96
97 /**
98 * Tests specifically for Terminated, unrelated to the DataRange
99 * or DataRangeCursor classes that call it.
100 */
101
TEST(DataTypeTerminated,StringDataNormalStore)102 TEST(DataTypeTerminated, StringDataNormalStore) {
103 const StringData writes[] = {StringData("a"), StringData("bb"), StringData("ccc")};
104 std::string buf(100, '\xff');
105 char* const bufBegin = &*buf.begin();
106 char* ptr = bufBegin;
107 size_t avail = buf.size();
108 std::string expected;
109 for (const auto& w : writes) {
110 size_t adv;
111 ASSERT_OK(
112 DataType::store(Terminated<'\0', StringData>(w), ptr, avail, &adv, ptr - bufBegin));
113 ASSERT_EQ(adv, w.size() + 1);
114 ptr += adv;
115 avail -= adv;
116 expected += w.toString();
117 expected += '\0';
118 }
119 ASSERT_EQUALS(expected, buf.substr(0, buf.size() - avail));
120 }
121
TEST(DataTypeTerminated,StringDataNormalLoad)122 TEST(DataTypeTerminated, StringDataNormalLoad) {
123 const StringData writes[] = {StringData("a"), StringData("bb"), StringData("ccc")};
124 std::string buf;
125 for (const auto& w : writes) {
126 buf += w.toString();
127 buf += '\0';
128 }
129 const char* const bufBegin = &*buf.begin();
130 const char* ptr = bufBegin;
131 size_t avail = buf.size();
132
133 for (const auto& w : writes) {
134 size_t adv;
135 auto term = Terminated<'\0', StringData>{};
136 ASSERT_OK(DataType::load(&term, ptr, avail, &adv, ptr - bufBegin));
137 ASSERT_EQ(adv, term.value.size() + 1);
138 ptr += adv;
139 avail -= adv;
140 ASSERT_EQUALS(term.value, w);
141 }
142 }
143
TEST(DataTypeTerminated,LoadStatusOkPropagation)144 TEST(DataTypeTerminated, LoadStatusOkPropagation) {
145 // Test that the nested type's .load complaints are surfaced.
146 const char buf[] = {'d', 'd', 'd', '\0'};
147 size_t advanced = 123;
148 auto x = Terminated<'\0', Dummy<3>>();
149 Status s = DataType::load(&x, buf, sizeof(buf), &advanced, 0);
150 ASSERT_OK(s);
151 ASSERT_EQUALS(advanced, 4u); // OK must overwrite advanced
152 }
153
TEST(DataTypeTerminated,StoreStatusOkAdvanced)154 TEST(DataTypeTerminated, StoreStatusOkAdvanced) {
155 // Test that an OK .store sets proper 'advanced'.
156 char buf[4] = {};
157 size_t advanced = 123; // should be overwritten
158 Status s = DataType::store(Terminated<'\0', Dummy<3>>(), buf, sizeof(buf), &advanced, 0);
159 ASSERT_OK(s);
160 ASSERT_EQ(StringData(buf, 4), StringData(std::string{'d', 'd', 'd', '\0'}));
161 ASSERT_EQUALS(advanced, 4u); // OK must overwrite advanced
162 }
163
TEST(DataTypeTerminated,ErrorUnterminatedRead)164 TEST(DataTypeTerminated, ErrorUnterminatedRead) {
165 const char buf[] = {'h', 'e', 'l', 'l', 'o'};
166 size_t advanced = 123;
167 auto x = Terminated<'\0', StringData>();
168 Status s = DataType::load(&x, buf, sizeof(buf), &advanced, 0);
169 ASSERT_EQ(s.codeString(), "Overflow");
170 ASSERT_STRING_CONTAINS(s.reason(), "couldn't locate");
171 ASSERT_STRING_CONTAINS(s.reason(), "terminal char (\\u0000)");
172 ASSERT_EQUALS(advanced, 123u); // fails must not overwrite advanced
173 }
174
TEST(DataTypeTerminated,LoadStatusPropagation)175 TEST(DataTypeTerminated, LoadStatusPropagation) {
176 // Test that the nested type's .load complaints are surfaced.
177 const char buf[] = {'d', 'd', '\0'};
178 size_t advanced = 123;
179 auto x = Terminated<'\0', Dummy<3>>();
180 Status s = DataType::load(&x, buf, sizeof(buf), &advanced, 0);
181 ASSERT_EQ(s.codeString(), "Overflow");
182 ASSERT_STRING_CONTAINS(s.reason(), "too short for Dummy");
183 // ASSERT_STRING_CONTAINS(s.reason(), "terminal char (\\u0000)");
184 ASSERT_EQUALS(advanced, 123u); // fails must not overwrite advanced
185 }
186
TEST(DataTypeTerminated,StoreStatusPropagation)187 TEST(DataTypeTerminated, StoreStatusPropagation) {
188 // Test that the nested type's .store complaints are surfaced.
189 char in[2]; // Not big enough to hold a Dummy<3>.
190 size_t advanced = 123;
191 Status s = DataType::store(Terminated<'\0', Dummy<3>>(), in, sizeof(in), &advanced, 0);
192 ASSERT_EQ(s.codeString(), "Overflow");
193 ASSERT_STRING_CONTAINS(s.reason(), "insufficient space for Dummy");
194 ASSERT_EQUALS(advanced, 123u); // fails must not overwrite advanced
195 }
196
TEST(DataTypeTerminated,ErrorShortRead)197 TEST(DataTypeTerminated, ErrorShortRead) {
198 // The span before the '\0' is passed to Dummy<3>'s load.
199 // This consumes only the first 3 bytes, so Terminated complains
200 // about the unconsumed 'X'.
201 const char buf[] = {'d', 'd', 'd', 'X', '\0'};
202 size_t advanced = 123;
203 auto x = Terminated<'\0', Dummy<3>>();
204 Status s = DataType::load(&x, buf, sizeof(buf), &advanced, 0);
205 ASSERT_EQ(s.codeString(), "Overflow");
206 ASSERT_STRING_CONTAINS(s.reason(), "only read");
207 ASSERT_STRING_CONTAINS(s.reason(), "terminal char (\\u0000)");
208 ASSERT_EQUALS(advanced, 123u); // fails must not overwrite advanced
209 }
210
TEST(DataTypeTerminated,ErrorShortWrite)211 TEST(DataTypeTerminated, ErrorShortWrite) {
212 char in[3] = {};
213 auto x = Terminated<'\0', Dummy<3>>();
214 size_t advanced = 123;
215 Status s = DataType::store(x, in, sizeof(in), &advanced, 0);
216 ASSERT_EQ(s.codeString(), "Overflow");
217 ASSERT_STRING_CONTAINS(s.reason(), "couldn't write");
218 ASSERT_STRING_CONTAINS(s.reason(), "terminal char (\\u0000)");
219 ASSERT_EQUALS(advanced, 123u); // fails must not overwrite advanced
220 }
221
TEST(DataTypeTerminated,ThroughDataRangeCursor)222 TEST(DataTypeTerminated, ThroughDataRangeCursor) {
223 char buf[100];
224 const std::string parts[] = {"a", "bb", "ccc"};
225 std::string serialized;
226 for (const std::string& s : parts) {
227 serialized += s + '\0';
228 }
229 {
230 auto buf_writer = DataRangeCursor(buf, buf + sizeof(buf));
231 for (const std::string& s : parts) {
232 Terminated<'\0', ConstDataRange> tcdr(ConstDataRange(s.data(), s.data() + s.size()));
233 ASSERT_OK(buf_writer.writeAndAdvance(tcdr));
234 }
235 const auto written = std::string(static_cast<const char*>(buf), buf_writer.data());
236 ASSERT_EQUALS(written, serialized);
237 }
238 {
239 auto buf_source = ConstDataRangeCursor(buf, buf + sizeof(buf));
240 for (const std::string& s : parts) {
241 Terminated<'\0', ConstDataRange> tcdr;
242 ASSERT_OK(buf_source.readAndAdvance(&tcdr));
243 std::string read(tcdr.value.data(), tcdr.value.data() + tcdr.value.length());
244 ASSERT_EQUALS(s, read);
245 }
246 }
247 }
248
249 } // namespace
250 } // namespace mongo
251