Skip to content

Commit b4bcbe9

Browse files
committed
Merge branch 'MungoG-issue-50-iterator-invalidation' into develop
2 parents 2b001d9 + 2666e37 commit b4bcbe9

File tree

4 files changed

+163
-20
lines changed

4 files changed

+163
-20
lines changed

include/boost/buffers/slice.hpp

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,12 @@ class slice_of
6464
using iter_type = decltype(
6565
std::declval<BufferSequence const&>().begin());
6666

67+
using difference_type =
68+
typename std::iterator_traits<iter_type>::difference_type;
69+
6770
BufferSequence bs_;
68-
iter_type begin_;
69-
iter_type end_;
71+
difference_type begin_ = 0; // index of first buffer in sequence
72+
difference_type end_ = 0; // 1 + index of last buffer in sequence
7073
std::size_t len_ = 0; // length of bs_
7174
std::size_t size_ = 0; // total bytes
7275
std::size_t prefix_ = 0; // used prefix bytes
@@ -92,11 +95,12 @@ class slice_of
9295
slice_of(
9396
BufferSequence const& bs)
9497
: bs_(bs)
95-
, begin_(buffers::begin(bs_))
96-
, end_(buffers::end(bs_))
9798
{
98-
auto it = begin_;
99-
while(it != end_)
99+
iter_type it = buffers::begin(bs_);
100+
iter_type eit = buffers::end(bs_);
101+
begin_ = 0;
102+
end_ = std::distance(it, eit);
103+
while(it != eit)
100104
{
101105
value_type b(*it);
102106
size_ += b.size();
@@ -127,18 +131,39 @@ class slice_of
127131
}
128132

129133
private:
134+
iter_type
135+
begin_iter_impl() const noexcept
136+
{
137+
iter_type it = buffers::begin(bs_);
138+
std::advance(it, begin_);
139+
return it;
140+
}
141+
142+
iter_type
143+
end_iter_impl() const noexcept
144+
{
145+
iter_type it = buffers::begin(bs_);
146+
std::advance(it, end_);
147+
return it;
148+
}
149+
130150
void
131151
remove_prefix_impl(
132152
std::size_t n)
133153
{
154+
if(n > size_)
155+
n = size_;
156+
134157
// nice hack to simplify the loop (M. Nejati)
135158
n += prefix_;
136159
size_ += prefix_;
137160
prefix_ = 0;
138161

162+
iter_type it = begin_iter_impl();
163+
139164
while(n > 0 && begin_ != end_)
140165
{
141-
value_type b = *begin_;
166+
value_type b = *it;
142167
if(n < b.size())
143168
{
144169
prefix_ = n;
@@ -148,6 +173,7 @@ class slice_of
148173
n -= b.size();
149174
size_ -= b.size();
150175
++begin_;
176+
++it;
151177
--len_;
152178
}
153179
}
@@ -162,12 +188,19 @@ class slice_of
162188
return;
163189
}
164190
BOOST_ASSERT(begin_ != end_);
191+
192+
if(n > size_)
193+
n = size_;
194+
165195
n += suffix_;
166196
size_ += suffix_;
167197
suffix_ = 0;
168-
iter_type it = end_;
169-
--it;
170-
while(it != begin_)
198+
199+
iter_type bit = begin_iter_impl();
200+
iter_type it = end_iter_impl();
201+
it--;
202+
203+
while(it != bit)
171204
{
172205
value_type b = *it;
173206
if(n < b.size())
@@ -192,6 +225,7 @@ class slice_of
192225
}
193226
end_ = begin_;
194227
len_ = 0;
228+
size_ = 0;
195229
}
196230

197231
void
@@ -376,7 +410,7 @@ begin() const noexcept ->
376410
const_iterator
377411
{
378412
return const_iterator(
379-
this->begin_, prefix_, suffix_, 0, len_);
413+
begin_iter_impl(), prefix_, suffix_, 0, len_);
380414
}
381415

382416
template<class BufferSequence>
@@ -386,7 +420,7 @@ end() const noexcept ->
386420
const_iterator
387421
{
388422
return const_iterator(
389-
this->end_, prefix_, suffix_, len_, len_);
423+
end_iter_impl(), prefix_, suffix_, len_, len_);
390424
}
391425

392426
//------------------------------------------------

test/unit/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
# Official repository: https://github.com/cppalliance/buffers
88
#
99

10-
add_subdirectory(../../../url/extra/test_suite test_suite)
10+
if (NOT TARGET boost_url_test_suite)
11+
add_subdirectory(../../../url/extra/test_suite test_suite)
12+
endif()
1113

1214
file(GLOB_RECURSE PFILES CONFIGURE_DEPENDS *.cpp *.hpp)
1315
list(APPEND PFILES

test/unit/slice.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <boost/static_assert.hpp>
1818

1919
#include <array>
20+
#include <vector>
2021

2122
#include "test_buffers.hpp"
2223
#include "test_suite.hpp"
@@ -119,7 +120,8 @@ struct slice_test
119120
test::check_iterators(b, s);
120121
}
121122

122-
using seq_type = std::array<const_buffer, 3>;
123+
// Use a vector so that iterator invalidation is observable during testing.
124+
using seq_type = std::vector<const_buffer>;
123125

124126
void
125127
grind_back(
@@ -176,8 +178,8 @@ struct slice_test
176178
run()
177179
{
178180
std::string s;
179-
seq_type bs = make_buffers(s,
180-
"boost.", "buffers.", "slice_" );
181+
auto a = make_buffers(s, "boost.", "buffers.", "slice_");
182+
seq_type bs(a.begin(), a.end());
181183
test::check_sequence(bs, s);
182184
//check(bs, s);
183185
//grind(bs, s);

test/unit/test_buffers.hpp

Lines changed: 109 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <boost/core/detail/string_view.hpp>
1818
#include <boost/assert.hpp>
1919
#include <boost/static_assert.hpp>
20+
2021
#include <string>
2122

2223
// Trick boostdep into requiring URL
@@ -255,7 +256,8 @@ template<class ConstBufferSequence>
255256
void
256257
grind_front(
257258
ConstBufferSequence const& bs0,
258-
core::string_view pat0)
259+
core::string_view pat0,
260+
int levels)
259261
{
260262
for(std::size_t n = 0; n <= pat0.size() + 1; ++n)
261263
{
@@ -265,13 +267,37 @@ grind_front(
265267
remove_prefix(bs, n);
266268
check_eq(bs, pat);
267269
check_iterators(bs, pat);
270+
271+
if(levels)
272+
{
273+
// Take a copy, blank out the original to invalidate any
274+
// iterators, and redo the test
275+
slice_type<ConstBufferSequence> bsc(bs);
276+
{
277+
slice_type<ConstBufferSequence> dummy{};
278+
std::swap(bs, dummy);
279+
}
280+
grind_front(bsc, pat, levels-1);
281+
}
268282
}
269283
{
270284
auto pat = kept_front(pat0, n);
271285
slice_type<ConstBufferSequence> bs(bs0);
272286
keep_prefix(bs, n);
273287
check_eq(bs, pat);
274288
check_iterators(bs, pat);
289+
290+
if(levels)
291+
{
292+
// Take a copy, blank out the original to invalidate any
293+
// iterators, and redo the test
294+
slice_type<ConstBufferSequence> bsc(bs);
295+
{
296+
slice_type<ConstBufferSequence> dummy{};
297+
std::swap(bs, dummy);
298+
}
299+
grind_front(bsc, pat, levels-1);
300+
}
275301
}
276302
}
277303
}
@@ -280,7 +306,8 @@ template<class ConstBufferSequence>
280306
void
281307
grind_back(
282308
ConstBufferSequence const& bs0,
283-
core::string_view pat0)
309+
core::string_view pat0,
310+
int levels)
284311
{
285312
for(std::size_t n = 0; n <= pat0.size() + 1; ++n)
286313
{
@@ -290,13 +317,89 @@ grind_back(
290317
remove_suffix(bs, n);
291318
check_eq(bs, pat);
292319
check_iterators(bs, pat);
320+
321+
if(levels)
322+
{
323+
// Take a copy, blank out the original to invalidate any
324+
// iterators, and redo the test
325+
slice_type<ConstBufferSequence> bsc(bs);
326+
{
327+
slice_type<ConstBufferSequence> dummy{};
328+
std::swap(bs, dummy);
329+
}
330+
grind_back(bsc, pat, levels-1);
331+
}
293332
}
294333
{
295334
auto pat = kept_back(pat0, n);
296335
slice_type<ConstBufferSequence> bs(bs0);
297336
keep_suffix(bs, n);
298337
check_eq(bs, pat);
299338
check_iterators(bs, pat);
339+
340+
if(levels)
341+
{
342+
// Take a copy, blank out the original to invalidate any
343+
// iterators, and redo the test
344+
slice_type<ConstBufferSequence> bsc(bs);
345+
{
346+
slice_type<ConstBufferSequence> dummy{};
347+
std::swap(bs, dummy);
348+
}
349+
grind_back(bsc, pat, levels-1);
350+
}
351+
}
352+
}
353+
}
354+
355+
template<class ConstBufferSequence>
356+
void
357+
grind_both(
358+
ConstBufferSequence const& bs0,
359+
core::string_view pat0,
360+
int levels)
361+
{
362+
for(std::size_t n = 0; n <= pat0.size() / 2 + 2; ++n)
363+
{
364+
{
365+
auto pat = trimmed_back(trimmed_front(pat0, n), n);
366+
slice_type<ConstBufferSequence> bs(bs0);
367+
remove_prefix(bs, n);
368+
remove_suffix(bs, n);
369+
check_eq(bs, pat);
370+
check_iterators(bs, pat);
371+
372+
if(levels)
373+
{
374+
// Take a copy, blank out the original to invalidate any
375+
// iterators, and redo the test
376+
slice_type<ConstBufferSequence> bsc(bs);
377+
{
378+
slice_type<ConstBufferSequence> dummy{};
379+
std::swap(bs, dummy);
380+
}
381+
grind_both(bsc, pat, levels - 1);
382+
}
383+
}
384+
{
385+
auto pat = kept_back(kept_front(pat0, n), n);
386+
slice_type<ConstBufferSequence> bs(bs0);
387+
keep_prefix(bs, n);
388+
keep_suffix(bs, n);
389+
check_eq(bs, pat);
390+
check_iterators(bs, pat);
391+
392+
if(levels)
393+
{
394+
// Take a copy, blank out the original to invalidate any
395+
// iterators, and redo the test
396+
slice_type<ConstBufferSequence> bsc(bs);
397+
{
398+
slice_type<ConstBufferSequence> dummy{};
399+
std::swap(bs, dummy);
400+
}
401+
grind_both(bsc, pat, levels - 1);
402+
}
300403
}
301404
}
302405
}
@@ -307,8 +410,9 @@ check_slice(
307410
ConstBufferSequence const& bs,
308411
core::string_view pat)
309412
{
310-
grind_front(bs, pat);
311-
grind_back(bs, pat);
413+
grind_front(bs, pat, 1);
414+
grind_back(bs, pat, 1);
415+
grind_both(bs, pat, 1);
312416
}
313417

314418
// Test API and behavior of a BufferSequence
@@ -320,6 +424,7 @@ check_sequence(
320424
BOOST_STATIC_ASSERT(is_const_buffer_sequence<T>::value);
321425

322426
check_iterators(t, pat);
427+
323428
check_slice(t, pat);
324429
}
325430

0 commit comments

Comments
 (0)