Skip to content
This repository was archived by the owner on Mar 22, 2023. It is now read-only.

Commit 216012a

Browse files
authored
Merge pull request #927 from JanDorniak99/range_method_basic_string
add range methods to basic_string
2 parents 2f7bcb5 + dd9180a commit 216012a

3 files changed

Lines changed: 368 additions & 0 deletions

File tree

include/libpmemobj++/container/basic_string.hpp

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ class basic_string {
5858
using const_iterator = const_pointer;
5959
using reverse_iterator = std::reverse_iterator<iterator>;
6060
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
61+
using range_snapshotting_iterator =
62+
pmem::detail::range_snapshotting_iterator<CharT>;
6163
using for_each_ptr_function =
6264
std::function<void(persistent_ptr_base &)>;
6365

@@ -150,6 +152,13 @@ class basic_string {
150152
const CharT *c_str() const noexcept;
151153
void for_each_ptr(for_each_ptr_function func);
152154

155+
/* Range */
156+
slice<pointer> range(size_type p, size_type count);
157+
slice<range_snapshotting_iterator> range(size_type start, size_type n,
158+
size_type snapshot_size);
159+
slice<const_iterator> range(size_type start, size_type n) const;
160+
slice<const_iterator> crange(size_type start, size_type n) const;
161+
153162
/* Iterators */
154163
iterator begin();
155164
const_iterator begin() const noexcept;
@@ -1379,6 +1388,101 @@ typename basic_string<CharT, Traits>::const_reference
13791388
return is_sso_used() ? sso_data()[n] : non_sso_data()[n];
13801389
}
13811390

1391+
/**
1392+
* Returns slice and snapshots requested range. This method is not specified by
1393+
* STL standards.
1394+
*
1395+
* @param[in] start start index of requested range.
1396+
* @param[in] n number of elements in range.
1397+
*
1398+
* @return slice containing elements from <start, start + n).
1399+
*
1400+
* @throw std::out_of_range if any element of the range would be outside of the
1401+
* string.
1402+
* @throw pmem::transaction_error when snapshotting failed.
1403+
*/
1404+
template <typename CharT, typename Traits>
1405+
slice<typename basic_string<CharT, Traits>::pointer>
1406+
basic_string<CharT, Traits>::range(size_type start, size_type n)
1407+
{
1408+
if (start + n > size())
1409+
throw std::out_of_range("basic_string::range");
1410+
1411+
return is_sso_used() ? sso_data().range(start, n)
1412+
: non_sso_data().range(start, n);
1413+
}
1414+
1415+
/**
1416+
* Returns slice. This method is not specified by STL standards.
1417+
*
1418+
* @param[in] start start index of requested range.
1419+
* @param[in] n number of elements in range.
1420+
* @param[in] snapshot_size number of elements which should be snapshotted in a
1421+
* bulk while traversing this slice. If provided value is larger or equal to n,
1422+
* entire range is added to a transaction. If value is equal to 0 no
1423+
* snapshotting happens.
1424+
*
1425+
* @return slice containing elements from <start, start + n).
1426+
*
1427+
* @throw std::out_of_range if any element of the range would be outside of the
1428+
* string.
1429+
* @throw pmem::transaction_error when snapshotting failed.
1430+
*/
1431+
template <typename CharT, typename Traits>
1432+
slice<typename basic_string<CharT, Traits>::range_snapshotting_iterator>
1433+
basic_string<CharT, Traits>::range(size_type start, size_type n,
1434+
size_type snapshot_size)
1435+
{
1436+
if (start + n > size())
1437+
throw std::out_of_range("basic_string::range");
1438+
1439+
if (snapshot_size > n)
1440+
snapshot_size = n;
1441+
1442+
return is_sso_used() ? sso_data().range(start, n, snapshot_size)
1443+
: non_sso_data().range(start, n, snapshot_size);
1444+
}
1445+
1446+
/**
1447+
* Returns const slice. This method is not specified by STL standards.
1448+
*
1449+
* @param[in] start start index of requested range.
1450+
* @param[in] n number of elements in range.
1451+
*
1452+
* @return slice containing elements from <start, start + n).
1453+
*
1454+
* @throw std::out_of_range if any element of the range would be outside of the
1455+
* string.
1456+
*/
1457+
template <typename CharT, typename Traits>
1458+
slice<typename basic_string<CharT, Traits>::const_iterator>
1459+
basic_string<CharT, Traits>::range(size_type start, size_type n) const
1460+
{
1461+
return crange(start, n);
1462+
}
1463+
1464+
/**
1465+
* Returns const slice. This method is not specified by STL standards.
1466+
*
1467+
* @param[in] start start index of requested range.
1468+
* @param[in] n number of elements in range.
1469+
*
1470+
* @return slice containing elements from <start, start + n).
1471+
*
1472+
* @throw std::out_of_range if any element of the range would be outside of the
1473+
* string.
1474+
*/
1475+
template <typename CharT, typename Traits>
1476+
slice<typename basic_string<CharT, Traits>::const_iterator>
1477+
basic_string<CharT, Traits>::crange(size_type start, size_type n) const
1478+
{
1479+
if (start + n > size())
1480+
throw std::out_of_range("basic_string::range");
1481+
1482+
return {const_iterator(cdata() + start),
1483+
const_iterator(cdata() + start + n)};
1484+
}
1485+
13821486
/**
13831487
* Access first element and snapshot it if there is an
13841488
* active transaction.

tests/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,9 @@ if (TEST_STRING)
405405

406406
build_test(string_layout string_layout/string_layout.cpp)
407407
add_test_generic(NAME string_layout TRACERS none)
408+
409+
build_test(string_range string_range/string_range.cpp)
410+
add_test_generic(NAME string_range TRACERS none memcheck pmemcheck)
408411
endif()
409412

410413
if(TEST_CONCURRENT_HASHMAP)
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
/* Copyright 2020, Intel Corporation */
3+
4+
#include "../common/transaction_helpers.hpp"
5+
#include "../common/unittest.hpp"
6+
7+
#include <libpmemobj++/container/string.hpp>
8+
#include <libpmemobj++/make_persistent.hpp>
9+
#include <libpmemobj++/pool.hpp>
10+
#include <libpmemobj++/slice.hpp>
11+
#include <libpmemobj++/transaction.hpp>
12+
13+
#include <algorithm>
14+
15+
namespace nvobj = pmem::obj;
16+
17+
using S = pmem::obj::string;
18+
19+
struct root {
20+
nvobj::persistent_ptr<S> s;
21+
};
22+
23+
void
24+
test_out_of_range_exception(pmem::obj::pool<root> &pop, bool use_sso_only)
25+
{
26+
auto r = pop.root();
27+
28+
size_t bonus_size = use_sso_only ? 0 : 20;
29+
30+
try {
31+
nvobj::transaction::run(pop, [&] {
32+
r->s = nvobj::make_persistent<S>(10U + bonus_size, 'a');
33+
});
34+
} catch (std::exception &e) {
35+
UT_FATALexc(e);
36+
}
37+
38+
S &str = *(r->s);
39+
const S &const_str = *(r->s);
40+
41+
try {
42+
nvobj::transaction::run(pop, [&] {
43+
auto slice = str.range(
44+
0, 10 + bonus_size); /* should not throw */
45+
(void)slice;
46+
});
47+
} catch (std::exception &e) {
48+
UT_FATALexc(e);
49+
}
50+
51+
try {
52+
nvobj::transaction::run(pop, [&] {
53+
auto slice = str.crange(
54+
0, 10 + bonus_size); /* should not throw */
55+
(void)slice;
56+
});
57+
} catch (std::exception &e) {
58+
UT_FATALexc(e);
59+
}
60+
61+
try {
62+
nvobj::transaction::run(pop, [&] {
63+
auto slice = const_str.range(
64+
0, 10 + bonus_size); /* should not throw */
65+
(void)slice;
66+
});
67+
} catch (std::exception &e) {
68+
UT_FATALexc(e);
69+
}
70+
71+
try {
72+
nvobj::transaction::run(pop, [&] {
73+
auto slice = str.range(0, 10 + bonus_size,
74+
3); /* should not throw */
75+
(void)slice;
76+
});
77+
} catch (std::exception &e) {
78+
UT_FATALexc(e);
79+
}
80+
81+
bool exception_thrown = false;
82+
try {
83+
nvobj::transaction::run(pop, [&] {
84+
auto slice = str.range(
85+
0, 11 + bonus_size); /* should throw */
86+
(void)slice;
87+
});
88+
UT_ASSERT(0);
89+
} catch (std::out_of_range &) {
90+
exception_thrown = true;
91+
} catch (std::exception &e) {
92+
UT_FATALexc(e);
93+
}
94+
UT_ASSERT(exception_thrown);
95+
96+
exception_thrown = false;
97+
try {
98+
nvobj::transaction::run(pop, [&] {
99+
auto slice = str.range(0, 11 + bonus_size,
100+
3); /* should throw */
101+
(void)slice;
102+
});
103+
UT_ASSERT(0);
104+
} catch (std::out_of_range &) {
105+
exception_thrown = true;
106+
} catch (std::exception &e) {
107+
UT_FATALexc(e);
108+
}
109+
UT_ASSERT(exception_thrown);
110+
111+
exception_thrown = false;
112+
try {
113+
nvobj::transaction::run(pop, [&] {
114+
auto slice = const_str.range(
115+
0, 11 + bonus_size); /* should throw */
116+
(void)slice;
117+
});
118+
UT_ASSERT(0);
119+
} catch (std::out_of_range &) {
120+
exception_thrown = true;
121+
} catch (std::exception &e) {
122+
UT_FATALexc(e);
123+
}
124+
UT_ASSERT(exception_thrown);
125+
126+
exception_thrown = false;
127+
try {
128+
nvobj::transaction::run(pop, [&] {
129+
auto slice = str.crange(
130+
0, 11 + bonus_size); /* should throw */
131+
(void)slice;
132+
});
133+
UT_ASSERT(0);
134+
} catch (std::out_of_range &) {
135+
exception_thrown = true;
136+
} catch (std::exception &e) {
137+
UT_FATALexc(e);
138+
}
139+
UT_ASSERT(exception_thrown);
140+
141+
nvobj::transaction::run(pop,
142+
[&] { nvobj::delete_persistent<S>(r->s); });
143+
}
144+
145+
void
146+
test_returned_values(pmem::obj::pool<root> &pop, bool use_sso_only)
147+
{
148+
auto r = pop.root();
149+
150+
size_t bonus_size = use_sso_only ? 0 : 20;
151+
152+
try {
153+
nvobj::transaction::run(pop, [&] {
154+
r->s = nvobj::make_persistent<S>(10U + bonus_size, 'a');
155+
});
156+
} catch (std::exception &e) {
157+
UT_FATALexc(e);
158+
}
159+
160+
S &str = *(r->s);
161+
const S &const_str = *(r->s);
162+
163+
assert_tx_abort(pop, [&] {
164+
nvobj::slice<S::pointer> slice = str.range(0, 3);
165+
UT_ASSERTeq(&str.front(), slice.begin());
166+
UT_ASSERTeq(&str.front() + 3, slice.end());
167+
168+
size_t cnt = 0;
169+
for (auto &c : slice) {
170+
UT_ASSERTeq(c, 'a');
171+
c = 'b';
172+
UT_ASSERTeq(str[cnt++], 'b');
173+
UT_ASSERTeq(std::count(slice.begin(), slice.end(), 'b'),
174+
static_cast<long>(cnt));
175+
UT_ASSERTeq(str.size(), 10U + bonus_size);
176+
}
177+
});
178+
UT_ASSERTeq(str.find('b'), str.npos);
179+
180+
assert_tx_abort(pop, [&] {
181+
nvobj::slice<S::range_snapshotting_iterator> slice =
182+
str.range(2, 8 + bonus_size, 1);
183+
UT_ASSERTeq(&str.front() + 2, &*slice.begin());
184+
UT_ASSERTeq(&str.front() + 10 + bonus_size, &*slice.end());
185+
186+
size_t cnt = 2;
187+
for (auto &c : slice) {
188+
UT_ASSERTeq(c, 'a');
189+
c = 'b';
190+
UT_ASSERTeq(str[cnt++], 'b');
191+
UT_ASSERTeq(std::count(slice.begin(), slice.end(), 'b'),
192+
static_cast<long>(cnt - 2));
193+
UT_ASSERTeq(str.size(), 10U + bonus_size);
194+
}
195+
});
196+
UT_ASSERTeq(str.find('b'), str.npos);
197+
198+
assert_tx_abort(pop, [&] {
199+
nvobj::slice<S::range_snapshotting_iterator> slice =
200+
str.range(0, 10 + bonus_size, 11 + bonus_size);
201+
UT_ASSERTeq(&str.front(), &*slice.begin());
202+
UT_ASSERTeq(&str.front() + 10 + bonus_size, &*slice.end());
203+
204+
size_t cnt = 0;
205+
for (auto &c : slice) {
206+
UT_ASSERTeq(c, 'a');
207+
c = 'b';
208+
UT_ASSERTeq(str[cnt++], 'b');
209+
UT_ASSERTeq(std::count(slice.begin(), slice.end(), 'b'),
210+
static_cast<long>(cnt));
211+
UT_ASSERTeq(str.size(), 10U + bonus_size);
212+
}
213+
});
214+
UT_ASSERTeq(str.find('b'), str.npos);
215+
216+
nvobj::transaction::run(pop, [&] {
217+
nvobj::slice<S::pointer> slice = str.range(0, 10 + bonus_size);
218+
for (auto &c : slice) {
219+
c = 'b';
220+
}
221+
});
222+
UT_ASSERTeq(str.size(), 10U + bonus_size);
223+
UT_ASSERTeq(std::count(str.begin(), str.end(), 'b'),
224+
static_cast<long>(10 + bonus_size));
225+
226+
nvobj::slice<S::const_iterator> slice1 = const_str.range(0, 3);
227+
UT_ASSERTeq(&const_str.front(), slice1.begin());
228+
UT_ASSERTeq(&const_str.front() + 3, slice1.end());
229+
230+
nvobj::slice<S::const_iterator> slice2 = str.crange(0, 3);
231+
UT_ASSERTeq(&str.front(), slice2.begin());
232+
UT_ASSERTeq(&str.front() + 3, slice2.end());
233+
234+
nvobj::transaction::run(pop,
235+
[&] { nvobj::delete_persistent<S>(r->s); });
236+
}
237+
238+
static void
239+
test(int argc, char *argv[])
240+
{
241+
if (argc < 2) {
242+
UT_FATAL("usage: %s file-name", argv[0]);
243+
}
244+
245+
auto path = argv[1];
246+
auto pop = nvobj::pool<root>::create(
247+
path, "StringTest", PMEMOBJ_MIN_POOL, S_IWUSR | S_IRUSR);
248+
249+
test_out_of_range_exception(pop, true);
250+
test_out_of_range_exception(pop, false);
251+
test_returned_values(pop, true);
252+
test_returned_values(pop, false);
253+
254+
pop.close();
255+
}
256+
257+
int
258+
main(int argc, char *argv[])
259+
{
260+
return run_test([&] { test(argc, argv); });
261+
}

0 commit comments

Comments
 (0)