jaulib v1.4.0-2-g788cf73
Jau Support Library (C++, Java, ..)
Loading...
Searching...
No Matches
test_bytestream01.cpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2022 Gothel Software e.K.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25#include <cassert>
26#include <cstdint>
27#include <cstring>
28#include <vector>
29
30#include <jau/test/catch2_ext.hpp>
31
32#include <jau/debug.hpp>
33#include <jau/io/file_util.hpp>
35#include <jau/io/io_util.hpp>
36
37#include "test_httpd.hpp"
38
39extern "C" {
40 #include <unistd.h>
41}
42
43using namespace jau::fractions_i64_literals;
44
46 private:
47 const size_t IDX_11kiB = 0;
48 const size_t IDX_65MiB = 1;
49 // const size_t IDX_SKIPFILE1 = 2;
50 // const size_t IDX_SKIPFILE2 = 3;
51 static std::vector<std::string> fname_payload_lst;
52 static std::vector<std::string> fname_payload_copy_lst;
53 static std::vector<uint64_t> fname_payload_size_lst;
54 const std::string url_input_root = "http://localhost:8080/";
55
56 class data {
57 private:
58 static bool add_test_file(const std::string& name, const size_t size_limit) {
61 jau::io::fs::remove(name+".enc.dec");
63 size_t size;
64 {
65 static const std::string one_line = "Hello World, this is a test and I like it. Exactly 100 characters long. 0123456780 abcdefghjklmnop..";
67
68 REQUIRE( ofs.good() == true );
69 REQUIRE( ofs.isOpen() == true );
70
71 for(size=0; size < size_limit; size+=one_line.size()) {
72 if( one_line.size() != ofs.write(one_line.data(), one_line.size()) ) {
73 ERR_PRINT("Write %zu bytes to test file failed: %s", one_line.size(), ofs.toString().c_str());
74 return false;
75 }
76 }
77 if( 1 != ofs.write("X", 1) ) { // make it odd
78 ERR_PRINT("Write %zu bytes to test file failed: %s", 1, ofs.toString().c_str());
79 return false;
80 }
81 size += 1;
82 }
83 fname_payload_lst.push_back(name);
84 fname_payload_copy_lst.push_back(name+".copy");
85 fname_payload_size_lst.push_back( size );
86 return true;
87 }
88 static bool add_test_skipfile(const std::string& name_in, const std::string& name_out, const size_t take, const size_t skip) {
89 jau::io::fs::remove(name_out);
90
93 REQUIRE( true == out.good() );
94 REQUIRE( true == out.isOpen() );
95
96 REQUIRE( true == in.isOpen() );
97 REQUIRE( false == in.canWrite() );
98 REQUIRE( true == in.hasContentSize() );
99
100 char buffer[1024];
101 while( !in.eof() ) {
102 uint64_t in_left = in.contentSize() - in.position();
103 uint64_t l0 = std::min(in_left, std::min(sizeof(buffer), take));
104 REQUIRE( l0 == in.read(buffer, l0) );
105 REQUIRE( l0 == out.write(buffer, l0) );
106
107 in_left = in.contentSize() - in.position();
108 l0 = std::min(in_left, std::min(sizeof(buffer), skip));
109 const uint64_t p0 = in.position() + l0;
110 // std::cout << "XXX: " << in << ": l0 " << l0 << ", p0 " << p0 << "\n";
111 REQUIRE(p0 == in.seek(p0) );
112 }
113
114 fname_payload_lst.push_back(name_out);
115 return true;
116 }
117 data() {
118 REQUIRE( true == add_test_file("testfile_blob_01_11kiB.bin", (size_t)(1024*11)) );
119 REQUIRE( true == add_test_file("testfile_blob_02_65MiB.bin", (size_t)(1024*1024*65)) );
120 REQUIRE( true == add_test_skipfile("testfile_blob_01_11kiB.bin", "testfile_blob_03_skip1.bin", 10, 9) );
121 REQUIRE( true == add_test_skipfile("testfile_blob_01_11kiB.bin", "testfile_blob_04_skip2.bin", 9, 10) );
122 }
123 public:
124 static const data& get() {
125 static data instance;
126 return instance;
127 }
128 };
129
130 public:
132 // produce fresh demo data once per whole test class
133 const data& d = data::get();
134 (void)d;
135 }
136
139 int res = std::system("killall mini_httpd");
140 (void)res;
141 }
142 }
143
144 static void httpd_start() {
146 int res = std::system("killall mini_httpd");
147 (void)res;
148 const std::string cwd = jau::io::fs::get_cwd();
149 const std::string cmd = std::string(mini_httpd_exe)+" -p 8080 -l "+cwd+"/mini_httpd.log";
150 jau::PLAIN_PRINT(true, "%s", cmd.c_str());
151 res = std::system(cmd.c_str());
152 (void)res;
153 }
154 }
155
156 static bool transfer(jau::io::ByteStream& input, const std::string& output_fname, const size_t buffer_size=4096) {
158 jau::PLAIN_PRINT(true, "Transfer Start: %s", input.toString().c_str());
159 {
160 const jau::io::fs::file_stats output_stats(output_fname);
161 if( output_stats.exists() ) {
162 if( output_stats.is_file() ) {
163 if( !jau::io::fs::remove(output_fname) ) {
164 ERR_PRINT2("ByteStream copy failed: Failed deletion of existing output file %s", output_fname.c_str());
165 return false;
166 }
167 } else {
168 ERR_PRINT2("ByteStream copy failed: Not overwriting existing output file %s", output_fname.c_str());
169 return false;
170 }
171 }
172 }
174 if ( !outfile.good() || !outfile.isOpen() ) {
175 ERR_PRINT2("ByteStream copy failed: Couldn't open output file %s", output_fname.c_str());
176 return false;
177 }
178
179 uint64_t out_bytes_payload = 0;
180 jau::io::StreamConsumerFunc consume_data = [&](jau::io::secure_vector<uint8_t>& data, bool is_final) -> bool {
181 if( !is_final && ( !input.hasContentSize() || out_bytes_payload + data.size() < input.contentSize() ) ) {
182 const size_t written = outfile.write(data.data(), data.size());
183 out_bytes_payload += written;
184 return data.size() == written; // continue ..
185 } else {
186 const size_t written = outfile.write(data.data(), data.size());
187 out_bytes_payload += written;
188 return false; // EOS
189 }
190 };
192 io_buffer.reserve(buffer_size);
193 const uint64_t in_bytes_total = jau::io::read_stream(input, io_buffer, consume_data);
194 input.close();
195
196 if ( 0==in_bytes_total || input.fail() ) {
197 IRQ_PRINT("ByteStream copy failed: Input file read failed in %s, out %s", input.toString().c_str(), outfile.toString().c_str());
198 return false;
199 }
200 if ( outfile.fail() ) {
201 IRQ_PRINT("ByteStream copy failed: Output file write failed in %s, out %s", input.toString().c_str(), outfile.toString().c_str());
202 return false;
203 }
204
206 jau::io::print_stats("Transfer "+output_fname, out_bytes_payload, _td);
207 jau::PLAIN_PRINT(true, "Transfer End: %s", input.toString().c_str());
208
209 return true;
210 }
211
213 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
214 const bool http_support_expected = jau::io::uri_tk::protocol_supported("http:");
215 const bool file_support_expected = jau::io::uri_tk::protocol_supported("file:");
216 httpd_start();
217 {
218 std::vector<std::string_view> protos = jau::io::uri_tk::supported_protocols();
219 jau::PLAIN_PRINT(true, "test00_protocols: Supported protocols: %zu: %s", protos.size(), jau::to_string(protos, ",").c_str());
220 if( http_support_expected ) { // assume no http -> no curl
221 REQUIRE( 0 < protos.size() );
222 } else {
223 REQUIRE( 0 == protos.size() );
224 }
225 }
226 const size_t file_idx = IDX_11kiB;
227 {
228 const std::string url = "not_exiting_file.txt";
229 REQUIRE( false == jau::io::uri_tk::is_local_file_protocol(url) );
230 REQUIRE( false == jau::io::uri_tk::protocol_supported(url) );
231
232 std::unique_ptr<jau::io::ByteStream> in = jau::io::to_ByteInStream(url);
233 if( nullptr != in ) {
234 jau::PLAIN_PRINT(true, "test00_protocols: not_exiting_file: %s", in->toString().c_str());
235 }
236 REQUIRE( nullptr == in );
237 }
238 {
239 const std::string url = "file://not_exiting_file_uri.txt";
240 REQUIRE( true == jau::io::uri_tk::is_local_file_protocol(url) );
241 REQUIRE( file_support_expected == jau::io::uri_tk::protocol_supported(url) );
242
243 std::unique_ptr<jau::io::ByteStream> in = jau::io::to_ByteInStream(url);
244 if( nullptr != in ) {
245 jau::PLAIN_PRINT(true, "test00_protocols: not_exiting_file_uri: %s", in->toString().c_str());
246 }
247 REQUIRE( nullptr == in );
248 }
249 {
250 const std::string url = "lala://localhost:8080/" + fname_payload_lst[file_idx];
251 REQUIRE( false == jau::io::uri_tk::is_local_file_protocol(url) );
252 REQUIRE( false == jau::io::uri_tk::protocol_supported(url) );
253
254 std::unique_ptr<jau::io::ByteStream> in = jau::io::to_ByteInStream(url);
255 if( nullptr != in ) {
256 jau::PLAIN_PRINT(true, "test00_protocols: not_exiting_protocol_uri: %s", in->toString().c_str());
257 }
258 REQUIRE( nullptr == in );
259 }
260 {
261 const std::string url = url_input_root + "not_exiting_http_uri.txt";
262 REQUIRE( false == jau::io::uri_tk::is_local_file_protocol(url) );
263 REQUIRE( http_support_expected == jau::io::uri_tk::protocol_supported(url) );
264
265 std::unique_ptr<jau::io::ByteStream> in = jau::io::to_ByteInStream(url);
266 if( http_support_expected ) {
267 REQUIRE( nullptr != in );
268 jau::sleep_for( 100_ms ); // time to read 404 response
269 jau::PLAIN_PRINT(true, "test00_protocols: not_exiting_http_uri: %s", in->toString().c_str());
270 REQUIRE( false == in->good() );
271 REQUIRE( true == in->fail() );
272 REQUIRE( 0 == in->contentSize() );
273 } else {
274 REQUIRE( nullptr == in );
275 }
276 }
277 }
278
280 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
281 const bool http_support_expected = jau::io::uri_tk::protocol_supported("http:");
282 const bool file_support_expected = jau::io::uri_tk::protocol_supported("file:");
283 httpd_start();
284 const size_t file_idx = IDX_11kiB;
285 {
286 const std::string& url = fname_payload_lst[file_idx];
287 REQUIRE( false == jau::io::uri_tk::is_local_file_protocol(url) );
288 REQUIRE( false == jau::io::uri_tk::protocol_supported(url) );
289
290 std::unique_ptr<jau::io::ByteStream> in = jau::io::to_ByteInStream(url);
291 if( nullptr != in ) {
292 jau::PLAIN_PRINT(true, "test00_protocols: local-file-0: %s", in->toString().c_str());
293 }
294 REQUIRE( nullptr != in );
295 REQUIRE( false == in->fail() );
296
297 bool res = transfer(*in, fname_payload_copy_lst[file_idx]);
298 REQUIRE( true == res );
299
300 jau::io::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
301 REQUIRE( true == out_stats.exists() );
302 REQUIRE( true == out_stats.is_file() );
303 REQUIRE( in->contentSize() == out_stats.size() );
304 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
305 REQUIRE( true == jau::io::fs::compare(in->id(), out_stats.path(), true /* verbose */) );
306 }
307 {
308 const std::string url = "file://" + fname_payload_lst[file_idx];
309 REQUIRE( true == jau::io::uri_tk::is_local_file_protocol(url) );
310 REQUIRE( file_support_expected == jau::io::uri_tk::protocol_supported(url) );
311
312 std::unique_ptr<jau::io::ByteStream> in = jau::io::to_ByteInStream(url);
313 if( nullptr != in ) {
314 jau::PLAIN_PRINT(true, "test00_protocols: local-file-1: %s", in->toString().c_str());
315 } else {
316 jau::PLAIN_PRINT(true, "test00_protocols: local-file-1: NULL from url '%s'", url.c_str());
317 }
318 REQUIRE( nullptr != in );
319 REQUIRE( false == in->fail() );
320
321 bool res = transfer(*in, fname_payload_copy_lst[file_idx]);
322 REQUIRE( true == res );
323
324 jau::io::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
325 REQUIRE( true == out_stats.exists() );
326 REQUIRE( true == out_stats.is_file() );
327 REQUIRE( in->contentSize() == out_stats.size() );
328 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
329 REQUIRE( true == jau::io::fs::compare(fname_payload_lst[file_idx], out_stats.path(), true /* verbose */) );
330 }
331 {
332 const std::string url = url_input_root + fname_payload_lst[file_idx];
333 REQUIRE( false == jau::io::uri_tk::is_local_file_protocol(url) );
334 REQUIRE( http_support_expected == jau::io::uri_tk::protocol_supported(url) );
335
336 std::unique_ptr<jau::io::ByteStream> in = jau::io::to_ByteInStream(url);
337 if( nullptr != in ) {
338 jau::PLAIN_PRINT(true, "test00_protocols: http: %s", in->toString().c_str());
339 }
340 if( http_support_expected ) {
341 REQUIRE( nullptr != in );
342 REQUIRE( false == in->fail() );
343
344 bool res = transfer(*in, fname_payload_copy_lst[file_idx]);
345 REQUIRE( true == res );
346
347 jau::io::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
348 REQUIRE( true == out_stats.exists() );
349 REQUIRE( true == out_stats.is_file() );
350 REQUIRE( in->contentSize() == out_stats.size() );
351 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
352 REQUIRE( true == jau::io::fs::compare(fname_payload_lst[file_idx], out_stats.path(), true /* verbose */) );
353 } else {
354 REQUIRE( nullptr == in );
355 }
356 }
357 }
358
360 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
361 const size_t file_idx = IDX_11kiB;
362 jau::io::ByteStream_File data_stream(fname_payload_lst[file_idx], jau::io::iomode_t::read);
363
364 bool res = transfer(data_stream, fname_payload_copy_lst[file_idx], 4096);
365 REQUIRE( true == res );
366
367 jau::io::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
368 REQUIRE( true == out_stats.exists() );
369 REQUIRE( true == out_stats.is_file() );
370 REQUIRE( data_stream.contentSize() == out_stats.size() );
371 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
372 REQUIRE( true == jau::io::fs::compare(data_stream.id(), out_stats.path(), true /* verbose */) );
373 }
374
376 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
377 const size_t file_idx = IDX_65MiB;
378 jau::io::ByteStream_File data_stream(fname_payload_lst[file_idx], jau::io::iomode_t::read);
379
380 bool res = transfer(data_stream, fname_payload_copy_lst[file_idx], 4096);
381 REQUIRE( true == res );
382
383 jau::io::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
384 REQUIRE( true == out_stats.exists() );
385 REQUIRE( true == out_stats.is_file() );
386 REQUIRE( data_stream.contentSize() == out_stats.size() );
387 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
388 REQUIRE( true == jau::io::fs::compare(data_stream.id(), out_stats.path(), true /* verbose */) );
389 }
390
392 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
393 const size_t file_idx = IDX_65MiB;
394 jau::io::ByteStream_File data_stream(fname_payload_lst[file_idx], jau::io::iomode_t::read);
395
396 bool res = transfer(data_stream, fname_payload_copy_lst[file_idx], 32768);
397 REQUIRE( true == res );
398
399 jau::io::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
400 REQUIRE( true == out_stats.exists() );
401 REQUIRE( true == out_stats.is_file() );
402 REQUIRE( data_stream.contentSize() == out_stats.size() );
403 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
404 REQUIRE( true == jau::io::fs::compare(data_stream.id(), out_stats.path(), true /* verbose */) );
405 }
406
408 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
410 jau::PLAIN_PRINT(true, "http not supported, abort\n");
411 return;
412 }
413 httpd_start();
414 {
415 const size_t file_idx = IDX_11kiB;
416
417 const std::string uri_original = url_input_root + fname_payload_lst[file_idx];
418
419 jau::io::ByteInStream_URL data_stream(uri_original, 500_ms);
420
421 bool res = transfer(data_stream, fname_payload_copy_lst[file_idx], 32768);
422 REQUIRE( true == res );
423
424 jau::io::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
425 REQUIRE( true == out_stats.exists() );
426 REQUIRE( true == out_stats.is_file() );
427 REQUIRE( data_stream.contentSize() == out_stats.size() );
428 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
429 REQUIRE( true == jau::io::fs::compare(fname_payload_lst[file_idx], out_stats.path(), true /* verbose */) );
430 }
431 {
432 const size_t file_idx = IDX_65MiB;
433
434 const std::string uri_original = url_input_root + fname_payload_lst[file_idx];
435
436 jau::io::ByteInStream_URL data_stream(uri_original, 500_ms);
437
438 bool res = transfer(data_stream, fname_payload_copy_lst[file_idx], 32768);
439 REQUIRE( true == res );
440
441 jau::io::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
442 REQUIRE( true == out_stats.exists() );
443 REQUIRE( true == out_stats.is_file() );
444 REQUIRE( data_stream.contentSize() == out_stats.size() );
445 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
446 REQUIRE( true == jau::io::fs::compare(fname_payload_lst[file_idx], out_stats.path(), true /* verbose */) );
447 }
448 }
449
451 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
453 jau::PLAIN_PRINT(true, "http not supported, abort\n");
454 return;
455 }
456 httpd_start();
457 {
458 const size_t file_idx = IDX_11kiB;
459
460 const std::string uri_original = url_input_root + "doesnt_exists.txt";
461
462 jau::io::ByteInStream_URL data_stream(uri_original, 500_ms);
463
464 bool res = transfer(data_stream, fname_payload_copy_lst[file_idx]);
465 REQUIRE( false == res );
466
467 jau::io::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
468 REQUIRE( true == out_stats.exists() );
469 REQUIRE( true == out_stats.is_file() );
470 REQUIRE( data_stream.fail() == true );
471 REQUIRE( data_stream.hasContentSize() == false );
472 REQUIRE( data_stream.contentSize() == 0 );
473 REQUIRE( 0 == out_stats.size() );
474 }
475 }
476
477 // throttled, no content size, interruptReader() via set_eof() will avoid timeout
478 static void feed_source_00(jau::io::ByteInStream_Feed * data_feed, const size_t feed_size=1024) {
479 uint64_t xfer_total = 0;
480 jau::io::ByteStream_File data_stream(data_feed->id(), jau::io::iomode_t::read);
481 std::vector<uint8_t> buffer;
482 buffer.resize(feed_size);
483 while( data_stream.good() ) {
484 size_t count = data_stream.read(buffer.data(), buffer.size());
485 if( 0 < count ) {
486 xfer_total += count;
487 if( data_feed->write(buffer.data(), count) ) {
488 jau::sleep_for( 16_ms );
489 } else {
490 break;
491 }
492 }
493 }
494 // probably set after transfering due to above sleep, which also ends when total size has been reached.
496 (void)xfer_total; // not used yet ..
497 }
498
499 // throttled, with content size
500 static void feed_source_01(jau::io::ByteInStream_Feed * data_feed, const size_t feed_size=1024) {
501 uint64_t xfer_total = 0;
502 jau::io::ByteStream_File data_stream(data_feed->id(), jau::io::iomode_t::read);
503 const uint64_t file_size = data_stream.contentSize();
504 data_feed->setContentSize( file_size );
505 std::vector<uint8_t> buffer;
506 buffer.resize(feed_size);
507 while( data_stream.good() && xfer_total < file_size ) {
508 size_t count = data_stream.read(buffer.data(), buffer.size());
509 if( 0 < count ) {
510 xfer_total += count;
511 if( data_feed->write(buffer.data(), count) ) {
512 jau::sleep_for( 16_ms );
513 } else {
514 break;
515 }
516 }
517 }
518 // probably set after transfering due to above sleep, which also ends when total size has been reached.
519 data_feed->setEOF( !data_feed->fail() && xfer_total == file_size ? jau::io::io_result_t::SUCCESS : jau::io::io_result_t::FAILED );
520 }
521
522 // full speed, with content size
523 static void feed_source_10(jau::io::ByteInStream_Feed * data_feed, const size_t feed_size=1024) {
524 uint64_t xfer_total = 0;
525 jau::io::ByteStream_File data_stream(data_feed->id(), jau::io::iomode_t::read);
526 const uint64_t file_size = data_stream.contentSize();
527 data_feed->setContentSize( data_stream.contentSize() );
528 std::vector<uint8_t> buffer;
529 buffer.resize(feed_size);
530 while( data_stream.good() && xfer_total < file_size ) {
531 size_t count = data_stream.read(buffer.data(), buffer.size());
532 if( 0 < count ) {
533 xfer_total += count;
534 if( !data_feed->write(buffer.data(), count) ) {
535 break;
536 }
537 }
538 }
539 data_feed->setEOF( !data_feed->fail() && xfer_total == file_size ? jau::io::io_result_t::SUCCESS : jau::io::io_result_t::FAILED );
540 }
541
542 // full speed, no content size, interrupting @ 1024 bytes within our header
543 static void feed_source_20(jau::io::ByteInStream_Feed * data_feed, const size_t feed_size=1024) {
544 uint64_t xfer_total = 0;
545 jau::io::ByteStream_File data_stream(data_feed->id(), jau::io::iomode_t::read);
546 std::vector<uint8_t> buffer;
547 buffer.resize(feed_size);
548 while( data_stream.good() ) {
549 size_t count = data_stream.read(buffer.data(), buffer.size());
550 if( 0 < count ) {
551 xfer_total += count;
552 if( data_feed->write(buffer.data(), count) ) {
553 if( xfer_total >= 1024 ) {
554 data_feed->setEOF( jau::io::io_result_t::FAILED ); // calls data_feed->interruptReader();
555 return;
556 }
557 } else {
558 break;
559 }
560 }
561 }
562 // probably set after transfering due to above sleep, which also ends when total size has been reached.
563 // data_feed->set_eof( jau::io::async_io_result_t::SUCCESS );
564 }
565
566 // full speed, with content size, interrupting 1/4 way
567 static void feed_source_21(jau::io::ByteInStream_Feed * data_feed, const size_t feed_size=1024) {
568 uint64_t xfer_total = 0;
569 jau::io::ByteStream_File data_stream(data_feed->id(), jau::io::iomode_t::read);
570 const uint64_t file_size = data_stream.contentSize();
571 data_feed->setContentSize( data_stream.contentSize() );
572 std::vector<uint8_t> buffer;
573 buffer.resize(feed_size);
574 while( data_stream.good() ) {
575 size_t count = data_stream.read(buffer.data(), buffer.size());
576 if( 0 < count ) {
577 xfer_total += count;
578 if( data_feed->write(buffer.data(), count) ) {
579 if( xfer_total >= file_size/4 ) {
580 data_feed->setEOF( jau::io::io_result_t::FAILED ); // calls data_feed->interruptReader();
581 return;
582 }
583 } else {
584 break;
585 }
586 }
587 }
588 // probably set after transfering due to above sleep, which also ends when total size has been reached.
589 // data_feed->set_eof( jau::io::async_io_result_t::SUCCESS );
590 }
591
593 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
594 const size_t buffer_size = 4096;
595 const size_t feed_size = 1024;
596 {
597 const size_t file_idx = IDX_11kiB;
598 {
599 // full speed, with content size
600 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
601 std::thread feeder_thread= std::thread(&feed_source_10, &data_feed, feed_size);
602
603 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
604 if( feeder_thread.joinable() ) {
605 feeder_thread.join();
606 }
607 REQUIRE( true == res );
608
609 jau::io::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
610 REQUIRE( true == out_stats.exists() );
611 REQUIRE( true == out_stats.is_file() );
612 REQUIRE( data_feed.contentSize() == out_stats.size() );
613 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
614 REQUIRE( true == jau::io::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
615 }
616 {
617 // throttled, with content size
618 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
619 std::thread feeder_thread= std::thread(&feed_source_01, &data_feed, feed_size);
620
621 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
622 if( feeder_thread.joinable() ) {
623 feeder_thread.join();
624 }
625 REQUIRE( true == res );
626
627 jau::io::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
628 REQUIRE( true == out_stats.exists() );
629 REQUIRE( true == out_stats.is_file() );
630 REQUIRE( data_feed.contentSize() == out_stats.size() );
631 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
632 REQUIRE( true == jau::io::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
633 }
634 {
635 // throttled, no content size, interruptReader() via set_eof() will avoid timeout
636 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
637 std::thread feeder_thread= std::thread(&feed_source_00, &data_feed, feed_size);
638
639 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
640 if( feeder_thread.joinable() ) {
641 feeder_thread.join();
642 }
643 REQUIRE( true == res );
644
645 jau::io::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
646 REQUIRE( true == out_stats.exists() );
647 REQUIRE( true == out_stats.is_file() );
648 REQUIRE( data_feed.contentSize() == 0 );
649 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
650 REQUIRE( true == jau::io::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
651 }
652 }
653 {
654 const size_t file_idx = IDX_65MiB;
655 {
656 // full speed, with content size
657 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
658 std::thread feeder_thread= std::thread(&feed_source_10, &data_feed, feed_size);
659
660 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
661 if( feeder_thread.joinable() ) {
662 feeder_thread.join();
663 }
664 REQUIRE( true == res );
665
666 jau::io::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
667 REQUIRE( true == out_stats.exists() );
668 REQUIRE( true == out_stats.is_file() );
669 REQUIRE( data_feed.contentSize() == out_stats.size() );
670 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
671 REQUIRE( true == jau::io::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
672 }
673 }
674 }
675
677 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
678 const size_t buffer_size = 32768;
679 const size_t feed_size = 32768;
680 {
681 const size_t file_idx = IDX_11kiB;
682 {
683 // full speed, with content size
684 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
685 std::thread feeder_thread= std::thread(&feed_source_10, &data_feed, feed_size);
686
687 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
688 if( feeder_thread.joinable() ) {
689 feeder_thread.join();
690 }
691 REQUIRE( true == res );
692
693 jau::io::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
694 REQUIRE( true == out_stats.exists() );
695 REQUIRE( true == out_stats.is_file() );
696 REQUIRE( data_feed.contentSize() == out_stats.size() );
697 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
698 REQUIRE( true == jau::io::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
699 }
700 {
701 // throttled, with content size
702 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
703 std::thread feeder_thread= std::thread(&feed_source_01, &data_feed, feed_size);
704
705 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
706 if( feeder_thread.joinable() ) {
707 feeder_thread.join();
708 }
709 REQUIRE( true == res );
710
711 jau::io::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
712 REQUIRE( true == out_stats.exists() );
713 REQUIRE( true == out_stats.is_file() );
714 REQUIRE( data_feed.contentSize() == out_stats.size() );
715 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
716 REQUIRE( true == jau::io::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
717 }
718 {
719 // throttled, no content size, interruptReader() via set_eof() will avoid timeout
720 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
721 std::thread feeder_thread= std::thread(&feed_source_00, &data_feed, feed_size);
722
723 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
724 if( feeder_thread.joinable() ) {
725 feeder_thread.join();
726 }
727 REQUIRE( true == res );
728
729 jau::io::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
730 REQUIRE( true == out_stats.exists() );
731 REQUIRE( true == out_stats.is_file() );
732 REQUIRE( data_feed.contentSize() == 0 );
733 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
734 REQUIRE( true == jau::io::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
735 }
736 }
737 }
738
740 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
741 const size_t buffer_size = 32768;
742 const size_t feed_size = 32768;
743 {
744 const size_t file_idx = IDX_65MiB;
745 {
746 // full speed, with content size
747 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
748 std::thread feeder_thread= std::thread(&feed_source_10, &data_feed, feed_size);
749
750 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
751 if( feeder_thread.joinable() ) {
752 feeder_thread.join();
753 }
754 REQUIRE( true == res );
755
756 jau::io::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
757 REQUIRE( true == out_stats.exists() );
758 REQUIRE( true == out_stats.is_file() );
759 REQUIRE( data_feed.contentSize() == out_stats.size() );
760 REQUIRE( fname_payload_size_lst[file_idx] == out_stats.size() );
761 REQUIRE( true == jau::io::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
762 }
763 }
764 }
765
767 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
768 const size_t buffer_size = 4096;
769 const size_t feed_size = 1024;
770 {
771 const size_t file_idx = IDX_65MiB;
772 {
773 // full speed, no content size, interrupting @ 1024 bytes within our header
774 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
775 std::thread feeder_thread= std::thread(&feed_source_20, &data_feed, feed_size);
776
777 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
778 if( feeder_thread.joinable() ) {
779 feeder_thread.join();
780 }
781 REQUIRE( false == res );
782
783 jau::io::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
784 REQUIRE( true == out_stats.exists() );
785 REQUIRE( true == out_stats.is_file() );
786 REQUIRE( false == data_feed.hasContentSize() );
787 REQUIRE( 0 == data_feed.contentSize() );
788 REQUIRE( fname_payload_size_lst[file_idx] > out_stats.size() ); // interrupted...
789 REQUIRE( false == jau::io::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
790 }
791 {
792 // full speed, with content size, interrupting 1/4 way
793 jau::io::ByteInStream_Feed data_feed(fname_payload_lst[file_idx], 500_ms);
794 std::thread feeder_thread= std::thread(&feed_source_21, &data_feed, feed_size);
795
796 bool res = transfer(data_feed, fname_payload_copy_lst[file_idx], buffer_size);
797 if( feeder_thread.joinable() ) {
798 feeder_thread.join();
799 }
800 REQUIRE( false == res );
801
802 jau::io::fs::file_stats out_stats(fname_payload_copy_lst[file_idx]);
803 REQUIRE( true == out_stats.exists() );
804 REQUIRE( true == out_stats.is_file() );
805 REQUIRE( true == data_feed.hasContentSize() );
806 REQUIRE( fname_payload_size_lst[file_idx] == data_feed.contentSize() );
807 REQUIRE( data_feed.contentSize() > out_stats.size() ); // interrupted...
808 REQUIRE( false == jau::io::fs::compare(data_feed.id(), out_stats.path(), true /* verbose */) );
809 }
810 }
811 }
812
814 REQUIRE( true == in.isOpen() );
815 REQUIRE( false == in.canWrite() );
816 REQUIRE( true == in.hasContentSize() );
817 uint64_t p0 = 0;
818 REQUIRE( p0 == in.position() );
819 const uint64_t len = in.contentSize();
820 REQUIRE( true == in.available(len) );
821 p0 = len/4;
822 REQUIRE( 0 < p0 );
823 REQUIRE( p0 == in.seek(p0) );
824 REQUIRE( p0 == in.position() );
825 REQUIRE( true == in.available(len-p0) );
826 REQUIRE( p0 == in.discard(p0) );
827 p0 = len/2;
828 REQUIRE( p0 == in.position() );
829 REQUIRE( true == in.available(len-p0) );
830 }
832 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
833 // 4*40
834 jau::io::ByteStream_SecMemory data_stream("0123456789abcdefghjklmnopqrstuvwxyz_()#$0123456789abcdefghjklmnopqrstuvwxyz_()#$0123456789abcdefghjklmnopqrstuvwxyz_()#$0123456789abcdefghjklmnopqrstuvwxyz_()#$");
835 test_stream_seek(data_stream);
836 }
838 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
839 const size_t file_idx = IDX_65MiB;
840 jau::io::ByteStream_File data_stream(fname_payload_lst[file_idx], jau::io::iomode_t::read);
841 test_stream_seek(data_stream);
842 }
843
844 constexpr static const char* test40_src_name = "test40_seek.src.in";
845 constexpr static const char* test40_cmp1_name = "test40_seek.cmp1.in";
846 constexpr static const char* test40_cmp2_name = "test40_seek.cmp2.in";
847 constexpr static const char* test40_cmp3_name = "test40_seek.cmp3.in";
848
850
854 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
855 // 4*40
856 jau::io::ByteStream_SecMemory in("0123456789abcdefghjklmnopqrstuvwxyz_()#$0123456789abcdefghjklmnopqrstuvwxyz_()#$0123456789abcdefghjklmnopqrstuvwxyz_()#$0123456789abcdefghjklmnopqrstuvwxyz_()#$");
857 REQUIRE( true == transfer(in, test40_src_name, 1024) );
858 // s(41) + 40 + s(40) + 39
859 jau::io::ByteStream_SecMemory cmp1("123456789abcdefghjklmnopqrstuvwxyz_()#$0123456789abcdefghjklmnopqrstuvwxyz_()#$");
860 REQUIRE( true == transfer(cmp1, test40_cmp1_name, 1024) );
861 // s(28) + 31 + s(41) + 60
862 jau::io::ByteStream_SecMemory cmp2("tuvwxyz_()#$0123456789abcdefghjlmnopqrstuvwxyz_()#$0123456789abcdefghjklmnopqrstuvwxyz_()#$");
863 REQUIRE( true == transfer(cmp2, test40_cmp2_name, 1024) );
864
865 jau::io::ByteStream_SecMemory cmp3("0123456789abcde||0123456789abcde||#$||#$||#$||abcdefghjklmnopqrstuvwxy||abcdefghjklmnopqrstuvwxy||abcdefghjklmnopqrstuvwxyz||abcdefghjklmnopqrstuvwxyz||abcdefghjklmnopqrstuvwxyz||");
866 REQUIRE( true == transfer(cmp3, test40_cmp3_name, 1024) );
867 }
868
869 void test_stream_seek(jau::io::ByteStream& in, const std::string& out_name, const std::string& cmp_name,
870 size_t skip1, size_t take1, size_t skip2, size_t take2) {
871 REQUIRE( true == in.isOpen() );
872 REQUIRE( false == in.canWrite() );
873 REQUIRE( true == in.hasContentSize() );
874
876 REQUIRE( true == out.good() );
877 REQUIRE( true == out.isOpen() );
878 {
879 char buffer[100];
880
881 uint64_t p0 = 0;
882 REQUIRE( p0 == in.position() );
883 const uint64_t len = in.contentSize();
884 REQUIRE( true == in.available(len) );
885 {
886 p0 = skip1;
887 REQUIRE( p0 == in.seek(p0) );
888 REQUIRE( p0 == in.position() );
889 REQUIRE( true == in.available(len-p0) );
890 }
891 REQUIRE( take1 == in.read(buffer, take1) );
892 p0 += take1;
893 REQUIRE( p0 == in.position() );
894 REQUIRE(take1 == out.write(buffer, take1) );
895 REQUIRE(take1 == out.position() );
896 {
897 REQUIRE( skip2 == in.discard(skip2) );
898 p0 += skip2;
899 REQUIRE( p0 == in.position() );
900 REQUIRE( true == in.available(len-p0) );
901 }
902 REQUIRE( true == in.available(take2) );
903 REQUIRE( take2 == in.contentSize() - in.position() );
904 REQUIRE( take2 == in.read(buffer, take2) );
905 p0 += take2;
906 REQUIRE( p0 == in.position() );
907 REQUIRE(take2 == out.write(buffer, take2) );
908 REQUIRE(take1+take2 == out.position() );
909 REQUIRE( in.contentSize() == in.position() );
910 REQUIRE( true == in.eof() );
911 }
912 REQUIRE( true == jau::io::fs::compare(out_name, cmp_name, true /* verbose */) );
913 }
914
916 const std::string outfile_name01 = "test41_seek.01.out";
917 const std::string outfile_name02 = "test41_seek.02.out";
918 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
919
921 REQUIRE( true == in.isOpen() );
922 REQUIRE( false == in.canWrite() );
923 REQUIRE( true == in.hasContentSize() );
924 test_stream_seek(in, outfile_name01, test40_cmp1_name, 41, 40, 40, 39);
925 REQUIRE(0 == in.seek(0) );
926 test_stream_seek(in, outfile_name02, test40_cmp2_name, 28, 31, 41, 60);
927 }
928
930 const std::string uri_original = url_input_root + test40_src_name;
931 const std::string outfile_name01 = "test42_seek.01.out";
932 const std::string outfile_name02 = "test42_seek.02.out";
933
934 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
936 jau::PLAIN_PRINT(true, "http not supported, abort\n");
937 return;
938 }
939 httpd_start();
940 {
941
942 jau::io::ByteInStream_URL in(uri_original, 500_ms);
943 REQUIRE( true == in.isOpen() );
944 REQUIRE( false == in.canWrite() );
945 REQUIRE( true == in.hasContentSize() );
946 test_stream_seek(in, outfile_name01, test40_cmp1_name, 41, 40, 40, 39);
947 REQUIRE(jau::io::ByteStream::npos == in.seek(0) );
948 }
949 {
950
951 jau::io::ByteInStream_URL in(uri_original, 500_ms);
952 REQUIRE( true == in.isOpen() );
953 REQUIRE( false == in.canWrite() );
954 REQUIRE( true == in.hasContentSize() );
955 test_stream_seek(in, outfile_name02, test40_cmp2_name, 28, 31, 41, 60);
956 REQUIRE(jau::io::ByteStream::npos == in.seek(0) );
957 }
958 }
959
960 void test_stream_rewind(jau::io::ByteStream& in, const std::string& out_name, const std::string& cmp_name) {
961 REQUIRE( true == in.isOpen() );
962 REQUIRE( false == in.canWrite() );
963 REQUIRE( true == in.hasContentSize() );
964
966 REQUIRE( true == out.good() );
967 REQUIRE( true == out.isOpen() );
968
969 // "0123456789abcdefghjklmnopqrstuvwxyz_()#$0123456789abcdefghjklmnopqrstuvwxyz_()#$0123456789abcdefghjklmnopqrstuvwxyz_()#$0123456789abcdefghjklmnopqrstuvwxyz_()#$"
970 // 2x [ 40- 55): 0123456789abcde (15 = 30)
971 // 3x [ 38- 40): #$ (2 = 6)
972 // 2x [ 50- 74): abcdefghjklmnopqrstuvwxy (24 = 48)
973 // 3x [130-155): abcdefghjklmnopqrstuvwxyz (25 = 75)
974 // "0123456789abcde||0123456789abcde||#$||#$||#$||abcdefghjklmnopqrstuvwxy||abcdefghjklmnopqrstuvwxy||abcdefghjklmnopqrstuvwxyz||abcdefghjklmnopqrstuvwxyz||abcdefghjklmnopqrstuvwxyz"
975 // 30 + 6 + 48 + 75 = 159 -> 177
976 {
977 char buffer[100];
978
979 REQUIRE( 0 == in.position() );
980 const uint64_t len = in.contentSize();
981 REQUIRE( true == in.available(len) );
982
983 {
984 REQUIRE( jau::io::ByteStream::npos == in.mark() );
985 REQUIRE( true == in.setMark(5) );
986 REQUIRE( 0 == in.mark() );
987 std::memset(buffer, 0, sizeof(buffer));
988 REQUIRE( 15 == in.read(buffer, 15) ); // if not unlimited: invalidates mark (readLimit exceeded)
989 if( 15 >= in.markReadLimit() ) {
990 REQUIRE( jau::io::ByteStream::npos == in.mark() );
991 }
992 REQUIRE( 15 == in.position() );
993 }
994
995 // read to 38
996 std::memset(buffer, 0, sizeof(buffer));
997 REQUIRE( 38-15 == in.read(buffer, 38-15) );
998 REQUIRE( 38 == in.position() );
999
1000 REQUIRE( true == in.setMark(17) ); // covering [38-55), also allowing seek-back
1001 REQUIRE( 38 == in.mark() );
1002 REQUIRE( 38 == in.position() );
1003
1004 // read to 40
1005 std::memset(buffer, 0, sizeof(buffer));
1006 REQUIRE( 40-38 == in.read(buffer, 40-38) );
1007 REQUIRE( 40 == in.position() );
1008
1009 // x1: 15
1010 REQUIRE( 38 == in.mark() );
1011 REQUIRE( 40 == in.position() );
1012 std::memset(buffer, 0, sizeof(buffer));
1013 REQUIRE( 15 == in.read(buffer, 15) );
1014 REQUIRE( 55 == in.position() );
1015 REQUIRE( 38 == in.mark() );
1016 REQUIRE( 15 == out.write(buffer, 15) );
1017 REQUIRE( 2 == out.write("||", 2) );
1018 REQUIRE( 40 == in.seek(40) ); // seek back (allowed on all stream types due to mark readLimit)
1019 REQUIRE( 40 == in.position() );
1020 // x2: 15
1021 REQUIRE( 40 == in.position() );
1022 std::memset(buffer, 0, sizeof(buffer));
1023 REQUIRE( 15 == in.read(buffer, 15) );
1024 REQUIRE( 55 == in.position() );
1025 REQUIRE( 38 == in.mark() );
1026 REQUIRE( 15 == out.write(buffer, 15) );
1027 REQUIRE( 2 == out.write("||", 2) );
1028
1029 REQUIRE( true == in.seekMark() );
1030 REQUIRE( 38 == in.position() );
1031
1032 // x1: 2
1033 REQUIRE( 38 == in.mark() );
1034 REQUIRE( 38 == in.position() );
1035 std::memset(buffer, 0, sizeof(buffer));
1036 REQUIRE( 2 == in.read(buffer, 2) );
1037 REQUIRE( 40 == in.position() );
1038 REQUIRE( 38 == in.mark() );
1039 REQUIRE( 2 == out.write(buffer, 2) );
1040 REQUIRE( 2 == out.write("||", 2) );
1041 REQUIRE( true == in.seekMark() );
1042 // x2: 2
1043 REQUIRE( 38 == in.position() );
1044 std::memset(buffer, 0, sizeof(buffer));
1045 REQUIRE( 2 == in.read(buffer, 2) );
1046 REQUIRE( 40 == in.position() );
1047 REQUIRE( 38 == in.mark() );
1048 REQUIRE( 2 == out.write(buffer, 2) );
1049 REQUIRE( 2 == out.write("||", 2) );
1050 REQUIRE( true == in.seekMark() );
1051 // x3: 2
1052 REQUIRE( 38 == in.position() );
1053 std::memset(buffer, 0, sizeof(buffer));
1054 REQUIRE( 2 == in.read(buffer, 2) );
1055 REQUIRE( 40 == in.position() );
1056 REQUIRE( 38 == in.mark() );
1057 REQUIRE( 2 == out.write(buffer, 2) );
1058 REQUIRE( 2 == out.write("||", 2) );
1059 REQUIRE( true == in.seekMark() );
1060
1061 // seek to 50
1062 std::memset(buffer, 0, sizeof(buffer));
1063 REQUIRE( 50 == in.seek(50) );
1064 REQUIRE( 50 == in.position() );
1065
1066 // x1: 24
1067 REQUIRE( true == in.setMark(25) );
1068 REQUIRE( 50 == in.mark() );
1069 REQUIRE( 50 == in.position() );
1070 std::memset(buffer, 0, sizeof(buffer));
1071 REQUIRE( 24 == in.read(buffer, 24) );
1072 REQUIRE( 74 == in.position() );
1073 REQUIRE( 50 == in.mark() );
1074 REQUIRE( 24 == out.write(buffer, 24) );
1075 REQUIRE( 2 == out.write("||", 2) );
1076 REQUIRE( 50 == in.seek(50) ); // alternative to seekMark!
1077 // x2: 24
1078 REQUIRE( 50 == in.position() );
1079 std::memset(buffer, 0, sizeof(buffer));
1080 REQUIRE( 24 == in.read(buffer, 24) );
1081 REQUIRE( 74 == in.position() );
1082 REQUIRE( 50 == in.mark() );
1083 REQUIRE( 24 == out.write(buffer, 24) );
1084 REQUIRE( 2 == out.write("||", 2) );
1085
1086 // read to 130
1087 std::memset(buffer, 0, sizeof(buffer));
1088 REQUIRE( 130-74 == in.read(buffer, 130-74) );
1089 REQUIRE( 130 == in.position() );
1090
1091 // x1: 25
1092 REQUIRE( true == in.setMark(in.markReadLimit()) );
1093 REQUIRE( 130 == in.mark() );
1094 REQUIRE( 130 == in.position() );
1095 std::memset(buffer, 0, sizeof(buffer));
1096 REQUIRE( 25 == in.read(buffer, 25) );
1097 REQUIRE( 155 == in.position() );
1098 REQUIRE( 130 == in.mark() );
1099 REQUIRE( 25 == out.write(buffer, 25) );
1100 REQUIRE( 2 == out.write("||", 2) );
1101 REQUIRE( 130 == in.seek(130) ); // alternative to seekMark!
1102 // x2: 25
1103 REQUIRE( 130 == in.position() );
1104 std::memset(buffer, 0, sizeof(buffer));
1105 REQUIRE( 25 == in.read(buffer, 25) );
1106 REQUIRE( 155 == in.position() );
1107 REQUIRE( 130 == in.mark() );
1108 REQUIRE( 25 == out.write(buffer, 25) );
1109 REQUIRE( 2 == out.write("||", 2) );
1110 REQUIRE( true == in.seekMark() );
1111 // x3: 25
1112 REQUIRE( 130 == in.position() );
1113 std::memset(buffer, 0, sizeof(buffer));
1114 REQUIRE( 25 == in.read(buffer, 25) );
1115 REQUIRE( 155 == in.position() );
1116 REQUIRE( 130 == in.mark() );
1117 REQUIRE( 25 == out.write(buffer, 25) );
1118 REQUIRE( 2 == out.write("||", 2) );
1119
1120 ///
1121
1122 REQUIRE( 179 == out.position() );
1123 REQUIRE( false == in.eof() );
1124 }
1125 REQUIRE( true == jau::io::fs::compare(out_name, cmp_name, true /* verbose */) );
1126 }
1127
1129 const std::string outfile_name01 = "test4a_rewind.01.out";
1130 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
1131
1133 REQUIRE( true == in.isOpen() );
1134 REQUIRE( false == in.canWrite() );
1135 REQUIRE( true == in.hasContentSize() );
1136 test_stream_rewind(in, outfile_name01, test40_cmp3_name);
1137 }
1138
1140 const std::string uri_original = url_input_root + test40_src_name;
1141 const std::string outfile_name01 = "test4b_rewind.01.out";
1142
1143 jau::fprintf_td(stderr, "\n"); jau::fprintf_td(stderr, "%s\n", __func__);
1144 if( !jau::io::uri_tk::protocol_supported("http:") ) {
1145 jau::PLAIN_PRINT(true, "http not supported, abort\n");
1146 return;
1147 }
1148 httpd_start();
1149 {
1150 jau::io::ByteInStream_URL in(uri_original, 500_ms);
1151 REQUIRE( true == in.isOpen() );
1152 REQUIRE( false == in.canWrite() );
1153 REQUIRE( true == in.hasContentSize() );
1154 test_stream_rewind(in, outfile_name01, test40_cmp3_name);
1155 REQUIRE(jau::io::ByteStream::npos == in.seek(0) );
1156 }
1157 }
1158};
1159
1160std::vector<std::string> TestByteStream01::fname_payload_lst;
1161std::vector<std::string> TestByteStream01::fname_payload_copy_lst;
1162std::vector<uint64_t> TestByteStream01::fname_payload_size_lst;
1163
1166
1170
1173
1178
static void feed_source_00(jau::io::ByteInStream_Feed *data_feed, const size_t feed_size=1024)
static void feed_source_20(jau::io::ByteInStream_Feed *data_feed, const size_t feed_size=1024)
void test_stream_seek(jau::io::ByteStream &in, const std::string &out_name, const std::string &cmp_name, size_t skip1, size_t take1, size_t skip2, size_t take2)
static constexpr const char * test40_cmp1_name
static bool transfer(jau::io::ByteStream &input, const std::string &output_fname, const size_t buffer_size=4096)
void test04_copy_file_ok_65MiB_buff32k()
static constexpr const char * test40_cmp2_name
void test_stream_seek(jau::io::ByteStream &in)
static void httpd_start()
static void feed_source_01(jau::io::ByteInStream_Feed *data_feed, const size_t feed_size=1024)
void test20_copy_fed_ok_buff4k_feed1k()
void test01_copy_file_ok_11kiB_buff4k()
static constexpr const char * test40_src_name
static constexpr const char * test40_cmp3_name
void test02_copy_file_ok_65MiB_buff4k()
void test_stream_rewind(jau::io::ByteStream &in, const std::string &out_name, const std::string &cmp_name)
static void feed_source_10(jau::io::ByteInStream_Feed *data_feed, const size_t feed_size=1024)
static void feed_source_21(jau::io::ByteInStream_Feed *data_feed, const size_t feed_size=1024)
Ringbuffer-Based byte input stream with an externally provisioned data feed.
void setEOF(const io_result_t result) noexcept
Set end-of-data (EOS), i.e.
void setContentSize(const size_type size) noexcept
Set known content size, informal only.
size_type contentSize() const noexcept override
Returns the content_size if known.
size_t write(const void *in, size_t length, const jau::fraction_i64 &timeout) noexcept
Write given bytes to the async ringbuffer using explicit given timeout.
bool hasContentSize() const noexcept override
Returns true if implementation is aware of content_size(), otherwise false.
std::string id() const noexcept override
return the id of this data source
Ringbuffer-Based byte input stream with a URL connection provisioned data feed.
bool isOpen() const noexcept override
Checks if the stream has an associated file.
bool hasContentSize() const noexcept override
Returns true if implementation is aware of content_size(), otherwise false.
size_type seek(size_type newPos) noexcept override
newPos < position() limited to markpos, see setMark().
size_type contentSize() const noexcept override
Returns the content_size if known.
File based byte input stream, including named file descriptor.
size_type seek(size_type newPos) noexcept override
Sets position indicator for output-streams or input-streams with known length, similar to e....
size_type position() const noexcept override
Returns the position indicator, similar to e.g.
bool hasContentSize() const noexcept override
Returns true if implementation is aware of content_size(), otherwise false.
size_type contentSize() const noexcept override
Returns the content_size if known.
bool isOpen() const noexcept override
Checks if the stream has an associated file.
size_t write(const void *, size_t) noexcept override
Write to the data sink.
std::string id() const noexcept override
return the id of this data source
std::string toString() const noexcept override
size_t read(void *, size_t) noexcept override
Read from the source.
Secure Memory-Based byte input stream.
Byte stream interface.
virtual size_t read(void *out, size_t length) noexcept=0
Read from the source.
virtual bool isOpen() const noexcept=0
Checks if the stream has an associated file.
constexpr bool canWrite() const noexcept
Returns true in case stream has iomode::write capabilities.
virtual size_type mark() const noexcept=0
Returns the markpos set via setMark() or ByteStream::npos if unset.
virtual bool seekMark() noexcept=0
Seeks stream position to markpos as set via setMark().
virtual size_type markReadLimit() const noexcept=0
Returns the readLimit set via setMark().
virtual size_t discard(size_t N) noexcept=0
Discard the next N bytes of the data.
virtual bool setMark(size_type readLimit) noexcept=0
Set markpos to current position, allowing the stream to be seekMark().
virtual std::string toString() const noexcept=0
virtual bool hasContentSize() const noexcept=0
Returns true if implementation is aware of content_size(), otherwise false.
virtual void close() noexcept=0
Close the stream if supported by the underlying mechanism.
virtual size_type contentSize() const noexcept=0
Returns the content_size if known.
virtual bool available(size_t n) noexcept=0
Return whether n bytes are available in the input stream, if has_content_size() or using an asynchron...
virtual size_type position() const noexcept=0
Returns the position indicator, similar to e.g.
virtual size_type seek(size_type newPos) noexcept=0
Sets position indicator for output-streams or input-streams with known length, similar to e....
static constexpr size_type npos
Invalid position constant, denoting unset mark() or invalid position.
bool fail() const noexcept
Checks if an error has occurred.
bool good() const noexcept
Checks if no error nor eof() has occurred i.e.
bool eof() const noexcept
Checks if end-of-file has been reached.
Platform agnostic representation of POSIX ::lstat() and ::stat() for a given pathname.
constexpr bool is_file() const noexcept
Returns true if entity is a file, might be in combination with is_link().
uint64_t size() const noexcept
Returns the size in bytes of this element if is_file(), otherwise zero.
std::string path() const noexcept
Returns the unix path representation.
constexpr bool exists() const noexcept
Returns true if entity does not exist, exclusive bit.
#define ERR_PRINT(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
Definition debug.hpp:122
#define ERR_PRINT2(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
Definition debug.hpp:125
#define IRQ_PRINT(...)
Use for unconditional interruption messages, prefix '[elapsed_time] Interrupted @ FILE:LINE FUNC: '.
Definition debug.hpp:128
constexpr std::string_view name(const Bool v) noexcept
std::string get_cwd() noexcept
Return the current working directory or empty on failure.
Definition file_util.cpp:85
bool compare(const file_stats &source1, const file_stats &source2, const bool verbose=false) noexcept
Compare the bytes of both files, denoted by source1 and source2.
bool remove(const std::string &path, const traverse_options topts=traverse_options::none) noexcept
Remove the given path.
fraction_timespec getMonotonicTime() noexcept
Returns current monotonic time since Unix Epoch 00:00:00 UTC on 1970-01-01.
FracI64SizeBoolTuple to_fraction_i64(const std::string &value, const fraction_i64 &min_allowed, const fraction_i64 &max_allowed) noexcept
Returns the fraction_i64 of the given string in format <num>/<denom>, which may contain whitespace.
fraction< int64_t > fraction_i64
fraction using int64_t as integral type
bool is_local_file_protocol(const std::string_view &uri) noexcept
Returns true if the uri-scheme of given uri matches the local file protocol, i.e.
Definition io_util.cpp:205
jau::function< bool(secure_vector< uint8_t > &, bool)> StreamConsumerFunc
Stream consumer function.
Definition io_util.hpp:82
uint64_t read_stream(ByteStream &in, secure_vector< uint8_t > &buffer, const StreamConsumerFunc &consumer_fn) noexcept
Synchronous byte input stream reader using the given StreamConsumerFunc consumer_fn.
Definition io_util.cpp:60
std::vector< std::string_view > supported_protocols() noexcept
Returns a list of supported protocol supported by libcurl network protocols, queried at runtime.
Definition io_util.cpp:160
std::unique_ptr< ByteStream > to_ByteInStream(const std::string &path_or_uri, jau::fraction_i64 timeout=20_s) noexcept
Parses the given path_or_uri, if it matches a supported protocol, see jau::io::uri::protocol_supporte...
std::vector< T, jau::callocator_sec< T > > secure_vector
Definition io_util.hpp:33
void print_stats(const std::string &prefix, const uint64_t &out_bytes_total, const jau::fraction_i64 &td) noexcept
Definition io_util.cpp:1149
bool protocol_supported(const std::string_view &uri) noexcept
Returns true if the uri-scheme of given uri matches a supported by libcurl network protocols otherwis...
Definition io_util.cpp:195
@ writetrunc
Write capabilities and truncate existing (file) stream, i.e.
@ read
Read capabilities.
@ write
Write capabilities.
@ FAILED
Operation failed.
Definition io_util.hpp:57
@ SUCCESS
Operation succeeded.
Definition io_util.hpp:63
void PLAIN_PRINT(const bool printPrefix, const char *format,...) noexcept
Use for unconditional plain messages, prefix '[elapsed_time] ' if printPrefix == true.
Definition debug.cpp:264
int fprintf_td(const uint64_t elapsed_ms, FILE *stream, const char *format,...) noexcept
Convenient fprintf() invocation, prepending the given elapsed_ms timestamp.
Definition debug.cpp:276
std::string to_string(const bit_order_t v) noexcept
Return std::string representation of the given bit_order_t.
bool sleep_for(const fraction_timespec &relative_time, const bool monotonic=true, const bool ignore_irq=true) noexcept
sleep_for causes the current thread to block until a specific amount of time has passed.
Timespec structure using int64_t for its components in analogy to struct timespec_t on 64-bit platfor...
METHOD_AS_TEST_CASE(TestByteStream01::test00a_protocols_error, "test00a_protocols_error")
constexpr std::string_view mini_httpd_exe