Skip to content

Commit 17412ac

Browse files
committed
Preserve transform_width and remove_whitespace state across a copy
Same defect class as the `xml_escape` / `xml_unescape` fix in the previous commit: a hand-written copy constructor, written for Intel 7.1, that silently dropped part of the iterator's state. The bug is not reachable through the archive save/load paths, which drive the base64 pipeline with a single iterator instance, but these iterators are public and meant to be composed, and `std::copy` and buffered adaptors (as in issue #229) copy an iterator in mid stream. Remove both hand-written copy constructors and use the compiler- generated ones, which copy the full state. Refs #229.
1 parent f3ececb commit 17412ac

4 files changed

Lines changed: 108 additions & 13 deletions

File tree

include/boost/archive/iterators/remove_whitespace.hpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,10 +154,6 @@ class remove_whitespace :
154154
remove_whitespace(T start) :
155155
super_t(Base(static_cast< T >(start)))
156156
{}
157-
// intel 7.1 doesn't like default copy constructor
158-
remove_whitespace(const remove_whitespace & rhs) :
159-
super_t(rhs.base_reference())
160-
{}
161157
};
162158

163159
} // namespace iterators

include/boost/archive/iterators/transform_width.hpp

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,6 @@ class transform_width :
119119
m_remaining_bits(0),
120120
m_end_of_sequence(false)
121121
{}
122-
// intel 7.1 doesn't like default copy constructor
123-
transform_width(const transform_width & rhs) :
124-
super_t(rhs.base_reference()),
125-
m_buffer_out_full(rhs.m_buffer_out_full),
126-
m_buffer_out(rhs.m_buffer_out),
127-
m_buffer_in(rhs.m_buffer_in),
128-
m_remaining_bits(rhs.m_remaining_bits),
129-
m_end_of_sequence(false)
130-
{}
131122
};
132123

133124
template<

test/Jamfile.v2

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ if ! $(BOOST_ARCHIVE_LIST) {
161161
[ test-bsl-run test_mult_archive_types : : : [ requires std_wstreambuf ] ]
162162
[ test-bsl-run test_iterators : : : [ requires std_wstreambuf ] ]
163163
[ test-bsl-run test_iterators_base64 ]
164+
[ test-bsl-run test_iterators_copy ]
164165
[ test-bsl-run test_smart_cast ]
165166
[ test-bsl-run test_codecvt_null ]
166167
[ test-bsl-run test_singleton ]

test/test_iterators_copy.cpp

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
2+
// test_iterators_copy.cpp
3+
4+
// Copyright 2026 Gennaro Prota
5+
// Distributed under the Boost Software License, Version 1.0.
6+
// (See accompanying file LICENSE_1_0.txt or copy at
7+
// https://www.boost.org/LICENSE_1_0.txt)
8+
9+
// Regression test for issue #229.
10+
11+
#include <algorithm>
12+
#include <cstdlib>
13+
#include <cstddef>
14+
#include <list>
15+
16+
#include <boost/config.hpp>
17+
18+
#include <boost/archive/iterators/binary_from_base64.hpp>
19+
#include <boost/archive/iterators/base64_from_binary.hpp>
20+
#include <boost/archive/iterators/insert_linebreaks.hpp>
21+
#include <boost/archive/iterators/remove_whitespace.hpp>
22+
#include <boost/archive/iterators/transform_width.hpp>
23+
24+
#include "test_tools.hpp"
25+
26+
// Traverse [it, end) but copy construct the iterator at every step and
27+
// continue from the copy, the way std::copy unwraps an iterator. The `== end`
28+
// comparison is what makes transform_width set its end-of-sequence flag, so we
29+
// must keep comparing against end for the copy to have that state to preserve.
30+
template<class Iterator, class Output>
31+
void drain_to_end_by_copy(Iterator it, Iterator end, Output out){
32+
while(! (it == end)){
33+
Iterator cur = it; // copy constructor under test
34+
*out++ = *cur;
35+
++cur;
36+
it = cur;
37+
}
38+
}
39+
40+
// Read n elements, copy constructing the iterator at every step. Used for the
41+
// decode pipeline, which has no end-of-sequence padding but does drive
42+
// remove_whitespace (whose copy must preserve its cached-value flag).
43+
template<class Iterator, class Output>
44+
void drain_n_by_copy(Iterator it, std::size_t n, Output out){
45+
for(std::size_t i = 0; i < n; ++i){
46+
Iterator cur = it; // copy constructor under test
47+
*out++ = *cur;
48+
++cur;
49+
it = cur;
50+
}
51+
}
52+
53+
template<class CharType>
54+
void test_base64_copy(unsigned int size){
55+
CharType rawdata[150];
56+
for(unsigned int i = 0; i < size; ++i)
57+
rawdata[i] = static_cast<CharType>(std::rand() & 0xff);
58+
59+
typedef boost::archive::iterators::insert_linebreaks<
60+
boost::archive::iterators::base64_from_binary<
61+
boost::archive::iterators::transform_width<
62+
CharType *, 6, sizeof(CharType) * 8
63+
>
64+
>, 76
65+
> encode;
66+
67+
// Straight encode (single iterator instance, as std::copy drives it).
68+
std::list<CharType> plain;
69+
std::copy(
70+
encode(rawdata), encode(rawdata + size), std::back_inserter(plain)
71+
);
72+
73+
// Same encode, but copy constructing the iterator at every step. Without
74+
// a correct transform_width copy constructor the final (zero padded)
75+
// group is produced from lost state, so the tails differ.
76+
std::list<CharType> copied;
77+
drain_to_end_by_copy(
78+
encode(rawdata), encode(rawdata + size), std::back_inserter(copied)
79+
);
80+
BOOST_CHECK(plain == copied);
81+
82+
// Decode back to the original bytes, again copy constructing at every
83+
// step. This drives remove_whitespace over the line breaks inserted
84+
// above (present once the base64 exceeds 76 characters).
85+
typedef boost::archive::iterators::transform_width<
86+
boost::archive::iterators::binary_from_base64<
87+
boost::archive::iterators::remove_whitespace<
88+
typename std::list<CharType>::iterator
89+
>
90+
>, sizeof(CharType) * 8, 6
91+
> decode;
92+
std::list<CharType> decoded;
93+
drain_n_by_copy(decode(plain.begin()), size, std::back_inserter(decoded));
94+
BOOST_CHECK(std::equal(rawdata, rawdata + size, decoded.begin()));
95+
}
96+
97+
int test_main(int /* argc */, char * /* argv */ []){
98+
for(unsigned int s = 1; s <= 4; ++s)
99+
test_base64_copy<char>(s);
100+
test_base64_copy<char>(150);
101+
#ifndef BOOST_NO_CWCHAR
102+
for(unsigned int s = 1; s <= 4; ++s)
103+
test_base64_copy<wchar_t>(s);
104+
test_base64_copy<wchar_t>(150);
105+
#endif
106+
return EXIT_SUCCESS;
107+
}

0 commit comments

Comments
 (0)