blob: 2ad7b5da7bc3f0af4044d3a13acf1b76ce7b6e2e [file] [log] [blame]
Jeff Thompson86b6d642013-10-17 15:01:56 -07001// (C) Copyright 2008 CodeRage, LLC (turkanis at coderage dot com)
2// (C) Copyright 2005-2007 Jonathan Turkanis
3// Distributed under the Boost Software License, Version 1.0. (See accompanying
4// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.)
5
6// See http://www.boost.org/libs/iostreams for documentation.
7
8#ifndef NDNBOOST_IOSTREAMS_FILTER_TEST_HPP_INCLUDED
9
10#if defined(_MSC_VER) && (_MSC_VER >= 1020)
11# pragma once
12#endif
13
14#include <ndnboost/config.hpp> // NDNBOOST_MSVC,put size_t in std.
15#include <ndnboost/detail/workaround.hpp>
16#include <algorithm> // min.
17#include <cstddef> // size_t.
18#if NDNBOOST_WORKAROUND(NDNBOOST_MSVC, <= 1300) || \
19 NDNBOOST_WORKAROUND(__BORLANDC__, NDNBOOST_TESTED_AT(0x564)) || \
20 NDNBOOST_WORKAROUND(__MWERKS__, <= 0x3003) \
21 /**/
22# include <cstdlib> // rand.
23#endif
24#include <cstring> // memcpy, strlen.
25#include <iterator>
26#include <string>
27#include <vector>
28#if !NDNBOOST_WORKAROUND(NDNBOOST_MSVC, <= 1300) && \
29 !NDNBOOST_WORKAROUND(__BORLANDC__, NDNBOOST_TESTED_AT(0x564)) && \
30 !NDNBOOST_WORKAROUND(__MWERKS__, <= 0x3003) \
31 /**/
32# include <ndnboost/random/linear_congruential.hpp>
33# include <ndnboost/random/uniform_smallint.hpp>
34#endif
35#include <ndnboost/iostreams/categories.hpp>
36#include <ndnboost/iostreams/compose.hpp>
37#include <ndnboost/iostreams/copy.hpp>
38#include <ndnboost/iostreams/detail/bool_trait_def.hpp>
39#include <ndnboost/iostreams/detail/ios.hpp>
40#include <ndnboost/iostreams/device/array.hpp>
41#include <ndnboost/iostreams/device/back_inserter.hpp>
42#include <ndnboost/iostreams/operations.hpp>
43#include <ndnboost/mpl/bool.hpp>
44#include <ndnboost/type_traits/is_array.hpp>
45#include <ndnboost/type_traits/is_same.hpp>
46
47#undef memcpy
48#undef rand
49#undef strlen
50
51#if defined(NDNBOOST_NO_STDC_NAMESPACE) && !defined(__LIBCOMO__)
52namespace std {
53 using ::memcpy;
54 using ::strlen;
55 #if NDNBOOST_WORKAROUND(NDNBOOST_MSVC, <= 1300) || \
56 NDNBOOST_WORKAROUND(__BORLANDC__, NDNBOOST_TESTED_AT(0x564)) || \
57 NDNBOOST_WORKAROUND(__MWERKS__, <= 0x3003) \
58 /**/
59 using ::rand;
60 #endif
61}
62#endif
63
64namespace ndnboost { namespace iostreams {
65
66NDNBOOST_IOSTREAMS_BOOL_TRAIT_DEF(is_string, std::basic_string, 3)
67
68const std::streamsize default_increment = 5;
69
70#if !NDNBOOST_WORKAROUND(NDNBOOST_MSVC, <= 1300) && \
71 !NDNBOOST_WORKAROUND(__BORLANDC__, NDNBOOST_TESTED_AT(0x564)) && \
72 !NDNBOOST_WORKAROUND(__MWERKS__, <= 0x3003) \
73 /**/
74 std::streamsize rand(int inc)
75 {
76 static rand48 random_gen;
77 static uniform_smallint<int> random_dist(0, inc);
78 return random_dist(random_gen);
79 }
80#else
81 std::streamsize rand(int inc)
82 {
83 return (std::rand() * inc + 1) / RAND_MAX;
84 }
85#endif
86
87class non_blocking_source {
88public:
89 typedef char char_type;
90 struct category
91 : source_tag,
92 peekable_tag
93 { };
94 explicit non_blocking_source( const std::string& data,
95 std::streamsize inc = default_increment )
96 : data_(data), inc_(inc), pos_(0)
97 { }
98 std::streamsize read(char* s, std::streamsize n)
99 {
100 if (pos_ == static_cast<std::streamsize>(data_.size()))
101 return -1;
102 std::streamsize avail =
103 (std::min) (n, static_cast<std::streamsize>(data_.size() - pos_));
104 std::streamsize amt = (std::min) (rand(inc_), avail);
105 if (amt)
106 std::memcpy(s, data_.c_str() + pos_, amt);
107 pos_ += amt;
108 return amt;
109 }
110
111 bool putback(char c)
112 {
113 if (pos_ > 0) {
114 data_[--pos_] = c;
115 return true;
116 }
117 return false;
118 }
119private:
120 std::string data_;
121 std::streamsize inc_, pos_;
122};
123
124class non_blocking_sink : public sink {
125public:
126 non_blocking_sink( std::string& dest,
127 std::streamsize inc = default_increment )
128 : dest_(dest), inc_(inc)
129 { }
130 std::streamsize write(const char* s, std::streamsize n)
131 {
132 std::streamsize amt = (std::min) (rand(inc_), n);
133 dest_.insert(dest_.end(), s, s + amt);
134 return amt;
135 }
136private:
137 non_blocking_sink& operator=(const non_blocking_sink&);
138 std::string& dest_;
139 std::streamsize inc_;
140};
141
142//--------------Definition of test_input_filter-------------------------------//
143
144template<typename Filter>
145bool test_input_filter( Filter filter,
146 const std::string& input,
147 const std::string& output,
148 mpl::true_ )
149{
150 for ( int inc = default_increment;
151 inc < default_increment * 40;
152 inc += default_increment )
153 {
154 non_blocking_source src(input, inc);
155 std::string dest;
156 iostreams::copy(compose(filter, src), iostreams::back_inserter(dest));
157 if (dest != output)
158 return false;
159 }
160 return true;
161}
162
163template<typename Filter, typename Source1, typename Source2>
164bool test_input_filter( Filter filter,
165 const Source1& input,
166 const Source2& output,
167 mpl::false_ )
168{
169 std::string in;
170 std::string out;
171 iostreams::copy(input, iostreams::back_inserter(in));
172 iostreams::copy(output, iostreams::back_inserter(out));
173 return test_input_filter(filter, in, out);
174}
175
176template<typename Filter, typename Source1, typename Source2>
177bool test_input_filter( Filter filter,
178 const Source1& input,
179 const Source2& output )
180{
181 // Use tag dispatch to compensate for bad overload resolution.
182 return test_input_filter( filter, input, output,
183 is_string<Source1>() );
184}
185
186//--------------Definition of test_output_filter------------------------------//
187
188template<typename Filter>
189bool test_output_filter( Filter filter,
190 const std::string& input,
191 const std::string& output,
192 mpl::true_ )
193{
194 for ( int inc = default_increment;
195 inc < default_increment * 40;
196 inc += default_increment )
197 {
198 array_source src(input.data(), input.data() + input.size());
199 std::string dest;
200 iostreams::copy(src, compose(filter, non_blocking_sink(dest, inc)));
201 if (dest != output )
202 return false;
203 }
204 return true;
205}
206
207template<typename Filter, typename Source1, typename Source2>
208bool test_output_filter( Filter filter,
209 const Source1& input,
210 const Source2& output,
211 mpl::false_ )
212{
213 std::string in;
214 std::string out;
215 iostreams::copy(input, iostreams::back_inserter(in));
216 iostreams::copy(output, iostreams::back_inserter(out));
217 return test_output_filter(filter, in, out);
218}
219
220template<typename Filter, typename Source1, typename Source2>
221bool test_output_filter( Filter filter,
222 const Source1& input,
223 const Source2& output )
224{
225 // Use tag dispatch to compensate for bad overload resolution.
226 return test_output_filter( filter, input, output,
227 is_string<Source1>() );
228}
229
230//--------------Definition of test_filter_pair--------------------------------//
231
232template<typename OutputFilter, typename InputFilter>
233bool test_filter_pair( OutputFilter out,
234 InputFilter in,
235 const std::string& data,
236 mpl::true_ )
237{
238 for ( int inc = default_increment;
239 inc <= default_increment * 40;
240 inc += default_increment )
241 {
242 {
243 array_source src(data.data(), data.data() + data.size());
244 std::string temp;
245 std::string dest;
246 iostreams::copy(src, compose(out, non_blocking_sink(temp, inc)));
247 iostreams::copy(
248 compose(in, non_blocking_source(temp, inc)),
249 iostreams::back_inserter(dest)
250 );
251 if (dest != data)
252 return false;
253 }
254 {
255 array_source src(data.data(), data.data() + data.size());
256 std::string temp;
257 std::string dest;
258 iostreams::copy(src, compose(out, non_blocking_sink(temp, inc)));
259 // truncate the file, this should not loop, it may throw
260 // std::ios_base::failure, which we swallow.
261 try {
262 temp.resize(temp.size() / 2);
263 iostreams::copy(
264 compose(in, non_blocking_source(temp, inc)),
265 iostreams::back_inserter(dest)
266 );
267 } catch(std::ios_base::failure&) {}
268 }
269 {
270 array_source src(data.data(), data.data() + data.size());
271 std::string temp;
272 std::string dest;
273 iostreams::copy(compose(out, src), non_blocking_sink(temp, inc));
274 iostreams::copy(
275 non_blocking_source(temp, inc),
276 compose(in, iostreams::back_inserter(dest))
277 );
278 if (dest != data)
279 return false;
280 }
281 {
282 array_source src(data.data(), data.data() + data.size());
283 std::string temp;
284 std::string dest;
285 iostreams::copy(compose(out, src), non_blocking_sink(temp, inc));
286 // truncate the file, this should not loop, it may throw
287 // std::ios_base::failure, which we swallow.
288 try {
289 temp.resize(temp.size() / 2);
290 iostreams::copy(
291 non_blocking_source(temp, inc),
292 compose(in, iostreams::back_inserter(dest))
293 );
294 } catch(std::ios_base::failure&) {}
295 }
296 }
297 return true;
298}
299
300template<typename OutputFilter, typename InputFilter, typename Source>
301bool test_filter_pair( OutputFilter out,
302 InputFilter in,
303 const Source& data,
304 mpl::false_ )
305{
306 std::string str;
307 iostreams::copy(data, iostreams::back_inserter(str));
308 return test_filter_pair(out, in, str);
309}
310
311template<typename OutputFilter, typename InputFilter, typename Source>
312bool test_filter_pair( OutputFilter out,
313 InputFilter in,
314 const Source& data )
315{
316 // Use tag dispatch to compensate for bad overload resolution.
317 return test_filter_pair(out, in, data, is_string<Source>());
318}
319
320} } // End namespaces iostreams, boost.
321
322#endif // #ifndef NDNBOOST_IOSTREAMS_FILTER_TEST_HPP_INCLUDED