merged RC_1_1 into master

This commit is contained in:
arvidn 2017-10-04 11:43:01 +02:00
commit 69dc73914a
14 changed files with 178 additions and 432 deletions

View File

@ -79,6 +79,8 @@
1.1.5 release
* fix leak of peer_class objects (when setting per-torrent rate limits)
* expose peer_class API to python binding
* fix integer overflow in whole_pieces_threshold logic
* fix uTP path MTU discovery issue on windows (DF bit was not set correctly)
* fix python binding for torrent_handle, to be hashable

View File

@ -393,9 +393,8 @@ rule tag ( name : type ? : property-set )
feature ipv6 : on off : composite propagated link-incompatible ;
feature.compose <ipv6>off : <define>TORRENT_USE_IPV6=0 ;
feature sanitize : off address memory bounds undefined thread rtc : composite propagated link-incompatible ;
feature sanitize : off address memory undefined thread rtc : composite propagated link-incompatible ;
# sanitize is a clang and GCC feature
feature.compose <sanitize>bounds : <cflags>-fsanitize=bounds <cflags>-fsanitize-undefined-trap-on-error <linkflags>-fsanitize-undefined-trap-on-error ;
feature.compose <sanitize>undefined : <cflags>-fsanitize=undefined <cflags>-fsanitize-undefined-trap-on-error <linkflags>-fsanitize=undefined <linkflags>-fsanitize-undefined-trap-on-error ;
feature.compose <sanitize>thread : <cflags>-fsanitize=thread <linkflags>-fsanitize=thread ;
feature.compose <sanitize>address : <cflags>-fsanitize=address <linkflags>-fsanitize=address ;

View File

@ -20,6 +20,7 @@
#include "libtorrent/alert.hpp"
#include "libtorrent/create_torrent.hpp" // for create_flags_t
#include "libtorrent/portmap.hpp" // for port_mapping_t
#include "libtorrent/peer_class.hpp"
#include <vector>
using namespace boost::python;
@ -301,6 +302,7 @@ void bind_converters()
to_python_converter<lt::piece_index_t, from_strong_typedef<lt::piece_index_t>>();
to_python_converter<lt::file_index_t, from_strong_typedef<lt::file_index_t>>();
to_python_converter<lt::port_mapping_t, from_strong_typedef<lt::port_mapping_t>>();
to_python_converter<lt::peer_class_t, from_strong_typedef<lt::peer_class_t>>();
to_python_converter<lt::torrent_flags_t, from_bitfield_flag<lt::torrent_flags_t>>();
to_python_converter<lt::peer_flags_t, from_bitfield_flag<lt::peer_flags_t>>();
to_python_converter<lt::peer_source_flags_t, from_bitfield_flag<lt::peer_source_flags_t>>();
@ -361,6 +363,7 @@ void bind_converters()
to_strong_typedef<lt::piece_index_t>();
to_strong_typedef<lt::file_index_t>();
to_strong_typedef<lt::port_mapping_t>();
to_strong_typedef<lt::peer_class_t>();
to_bitfield_flag<lt::torrent_flags_t>();
to_bitfield_flag<lt::peer_flags_t>();
to_bitfield_flag<lt::peer_source_flags_t>();

View File

@ -21,6 +21,7 @@
#include <libtorrent/time.hpp>
#include <libtorrent/session_stats.hpp>
#include <libtorrent/session_status.hpp>
#include <libtorrent/peer_class_type_filter.hpp>
#include <libtorrent/extensions/smart_ban.hpp>
#include <libtorrent/extensions/ut_metadata.hpp>
@ -512,6 +513,72 @@ namespace
ses.load_state(e, save_state_flags_t(flags));
}
dict get_peer_class(lt::session& ses, lt::peer_class_t const pc)
{
lt::peer_class_info pci;
{
allow_threading_guard guard;
pci = ses.get_peer_class(pc);
}
dict ret;
ret["ignore_unchoke_slots"] = pci.ignore_unchoke_slots;
ret["connection_limit_factor"] = pci.connection_limit_factor;
ret["label"] = pci.label;
ret["upload_limit"] = pci.upload_limit;
ret["download_limit"] = pci.download_limit;
ret["upload_priority"] = pci.upload_priority;
ret["download_priority"] = pci.download_priority;
return ret;
}
void set_peer_class(lt::session& ses, peer_class_t const pc, dict info)
{
lt::peer_class_info pci;
stl_input_iterator<std::string> i(info.keys()), end;
for (; i != end; ++i)
{
std::string const key = *i;
object const value = info[key];
if (key == "ignore_unchoke_slots")
{
pci.ignore_unchoke_slots = extract<bool>(value);
}
else if (key == "connection_limit_factor")
{
pci.connection_limit_factor = extract<int>(value);
}
else if (key == "label")
{
pci.label = extract<std::string>(value);
}
else if (key == "upload_limit")
{
pci.upload_limit = extract<int>(value);
}
else if (key == "download_limit")
{
pci.download_limit = extract<int>(value);
}
else if (key == "upload_priority")
{
pci.upload_priority = extract<int>(value);
}
else if (key == "download_priority")
{
pci.download_priority = extract<int>(value);
}
else
{
PyErr_SetString(PyExc_KeyError, ("unknown name in peer_class_info: " + key).c_str());
throw_error_already_set();
}
}
allow_threading_guard guard;
ses.set_peer_class(pc, pci);
}
#ifndef TORRENT_DISABLE_DHT
void dht_get_mutable_item(lt::session& ses, std::string key, std::string salt)
{
@ -567,7 +634,7 @@ namespace
return lt::find_metric_idx(name);
}
} // namespace unnamed
} // anonymous namespace
struct dummy1 {};
#ifndef TORRENT_NO_DEPRECATE
@ -813,6 +880,23 @@ void bind_session()
.value("upnp", lt::portmap_transport::upnp)
;
class_<lt::peer_class_type_filter>("peer_class_type_filter")
.def(init<>())
.def("add", &lt::peer_class_type_filter::add)
.def("remove", &lt::peer_class_type_filter::remove)
.def("disallow", &lt::peer_class_type_filter::disallow)
.def("allow", &lt::peer_class_type_filter::allow)
.def("apply", &lt::peer_class_type_filter::apply)
;
enum_<lt::peer_class_type_filter::socket_type_t>("socket_type_t")
.value("tcp_socket", peer_class_type_filter::tcp_socket)
.value("utp_socket", peer_class_type_filter::utp_socket)
.value("ssl_tcp_socket", peer_class_type_filter::ssl_tcp_socket)
.value("ssl_utp_socket", peer_class_type_filter::ssl_utp_socket)
.value("i2p_socket", peer_class_type_filter::i2p_socket)
;
{
scope s = class_<lt::session, boost::noncopyable>("session", no_init)
.def("__init__", boost::python::make_constructor(&make_session
@ -902,6 +986,12 @@ void bind_session()
.def("get_cache_info", &get_cache_info1, (arg("handle") = torrent_handle(), arg("flags") = 0))
.def("add_port_mapping", allow_threads(&lt::session::add_port_mapping))
.def("delete_port_mapping", allow_threads(&lt::session::delete_port_mapping))
.def("set_peer_class_filter", &lt::session::set_peer_class_filter)
.def("set_peer_class_type_filter", &lt::session::set_peer_class_type_filter)
.def("create_peer_class", &lt::session::create_peer_class)
.def("delete_peer_class", &lt::session::delete_peer_class)
.def("get_peer_class", &get_peer_class)
.def("set_peer_class", &set_peer_class)
#ifndef TORRENT_NO_DEPRECATE
.def(
@ -954,6 +1044,10 @@ void bind_session()
s.attr("tcp") = lt::portmap_protocol::tcp;
s.attr("udp") = lt::portmap_protocol::udp;
s.attr("global_peer_class_id") = session::global_peer_class_id;
s.attr("tcp_peer_class_id") = session::tcp_peer_class_id;
s.attr("local_peer_class_id") = session::local_peer_class_id;
}
#ifndef TORRENT_NO_DEPRECATE

View File

@ -417,6 +417,59 @@ class test_magnet_link(unittest.TestCase):
h = ses.add_torrent(p)
self.assertEqual(str(h.info_hash()), '178882f042c0c33426a6d81e0333ece346e68a68')
class test_peer_class(unittest.TestCase):
def test_peer_class_ids(self):
s = lt.session({'enable_dht': False})
print('global_peer_class_id:', lt.session.global_peer_class_id)
print('tcp_peer_class_id:', lt.session.tcp_peer_class_id)
print('local_peer_class_id:', lt.session.local_peer_class_id)
print('global: ', s.get_peer_class(s.global_peer_class_id))
print('tcp: ', s.get_peer_class(s.local_peer_class_id))
print('local: ', s.get_peer_class(s.local_peer_class_id))
def test_peer_class(self):
s = lt.session({'enable_dht': False})
c = s.create_peer_class('test class')
print('new class: ', s.get_peer_class(c))
nfo = s.get_peer_class(c)
self.assertEqual(nfo['download_limit'], 0)
self.assertEqual(nfo['upload_limit'], 0)
self.assertEqual(nfo['ignore_unchoke_slots'], False)
self.assertEqual(nfo['connection_limit_factor'], 100)
self.assertEqual(nfo['download_priority'], 1)
self.assertEqual(nfo['upload_priority'], 1)
self.assertEqual(nfo['label'], 'test class')
nfo['download_limit'] = 1337
nfo['upload_limit'] = 1338
nfo['ignore_unchoke_slots'] = True
nfo['connection_limit_factor'] = 42
nfo['download_priority'] = 2
nfo['upload_priority'] = 3
s.set_peer_class(c, nfo)
nfo2 = s.get_peer_class(c)
self.assertEqual(nfo, nfo2)
def test_peer_class_filter(self):
filt = lt.peer_class_type_filter()
filt.add(lt.socket_type_t.tcp_socket, lt.session.global_peer_class_id);
filt.remove(lt.socket_type_t.utp_socket, lt.session.local_peer_class_id);
filt.disallow(lt.socket_type_t.tcp_socket, lt.session.global_peer_class_id);
filt.allow(lt.socket_type_t.utp_socket, lt.session.local_peer_class_id);
def test_peer_class_ip_filter(self):
s = lt.session({'enable_dht': False})
s.set_peer_class_type_filter(lt.peer_class_type_filter())
s.set_peer_class_filter(lt.ip_filter())
class test_session(unittest.TestCase):
def test_add_torrent(self):

View File

@ -61,7 +61,6 @@ namespace libtorrent {
num_socket_types
};
// ``add()`` and ``remove()`` adds and removes a peer class to be added
// to new peers based on socket type.
void add(socket_type_t st, peer_class_t const peer_class)
@ -125,9 +124,9 @@ namespace libtorrent {
private:
// maps socket type to a bitmask that's used to filter out
// (mask) bits from the m_peer_class_filter.
std::uint32_t m_peer_class_type_mask[5];
std::uint32_t m_peer_class_type_mask[num_socket_types];
// peer class bitfield added based on socket type
std::uint32_t m_peer_class_type[5];
std::uint32_t m_peer_class_type[num_socket_types];
};
}

View File

@ -877,7 +877,7 @@ namespace libtorrent {
// the most recent pieces that are in the read cache.
suggest_mode,
// ``max_queued_disk_bytes`` is the number maximum number of bytes, to
// ``max_queued_disk_bytes`` is the maximum number of bytes, to
// be written to disk, that can wait in the disk I/O thread queue.
// This queue is only for waiting for the disk I/O thread to receive
// the job and either write it to disk or insert it in the write

View File

@ -59,7 +59,7 @@ namespace libtorrent {
void peer_class_set::remove_class(peer_class_pool& pool, peer_class_t const c)
{
auto const i = std::find(m_class.begin(), m_class.begin() + m_size, c);
int idx = int(i - m_class.begin());
int const idx = int(i - m_class.begin());
if (idx == m_size) return; // not found
if (idx < m_size - 1)
{

View File

@ -1086,7 +1086,7 @@ namespace {
{
TORRENT_ASSERT(is_single_thread());
// if you hit this assert, you're deleting a non-existent peer class
TORRENT_ASSERT(m_classes.at(cid));
TORRENT_ASSERT_PRECOND(m_classes.at(cid));
if (m_classes.at(cid) == nullptr) return;
m_classes.decref(cid);
}
@ -1163,7 +1163,7 @@ namespace {
{
peer_class* pc = m_classes.at(cid);
// if you hit this assert, you're passing in an invalid cid
TORRENT_ASSERT(pc);
TORRENT_ASSERT_PRECOND(pc);
if (pc == nullptr) return;
pc->set_info(&pci);

View File

@ -4369,6 +4369,7 @@ namespace libtorrent {
if (m_peer_class > peer_class_t{0})
{
remove_class(m_ses.peer_classes(), m_peer_class);
m_ses.peer_classes().decref(m_peer_class);
m_peer_class = peer_class_t{0};
}

View File

@ -1010,14 +1010,28 @@ namespace {
m_files.set_piece_length(0);
return false;
}
TORRENT_ASSERT(!files.name().empty());
if (files.num_files() == 0)
{
ec = errors::no_files_in_torrent;
// mark the torrent as invalid
m_files.set_piece_length(0);
return false;
}
if (files.name().empty())
{
ec = errors::torrent_missing_name;
// mark the torrent as invalid
m_files.set_piece_length(0);
return false;
}
// extract SHA-1 hashes for all pieces
// we want this division to round upwards, that's why we have the
// extra addition
if (files.total_size() >= static_cast<std::int64_t>(std::numeric_limits<int>::max() - files.piece_length())
* files.piece_length())
if (files.total_size() >=
static_cast<boost::int64_t>(std::numeric_limits<int>::max()
- files.piece_length()) * files.piece_length())
{
ec = errors::too_many_pieces_in_torrent;
// mark the torrent as invalid

View File

@ -35,7 +35,6 @@ project tools
<link>static
;
exe fuzz_torrent : fuzz_torrent.cpp ;
exe parse_access_log : parse_access_log.cpp ;
exe dht : dht_put.cpp : <include>../ed25519/src ;
exe session_log_alerts : session_log_alerts.cpp ;

View File

@ -1,5 +1,4 @@
tool_programs = \
fuzz_torrent \
session_log_alerts
if ENABLE_EXAMPLES
@ -20,7 +19,6 @@ EXTRA_DIST = Jamfile \
parse_session_stats.py \
parse_utp_log.py
fuzz_torrent_SOURCES = fuzz_torrent.cpp
session_log_alerts_SOURCES = session_log_alerts.cpp
LDADD = $(top_builddir)/src/libtorrent-rasterbar.la

View File

@ -1,416 +0,0 @@
/*
Copyright (c) 2015, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include <string>
#include <cstdint>
#include <random>
#include <cinttypes> // for PRId64 et.al.
#include "libtorrent/bdecode.hpp"
#include "libtorrent/torrent_info.hpp"
#include "libtorrent/error_code.hpp"
using lt::bdecode_node;
using std::mt19937;
using std::uniform_int_distribution;
char const* invalid_utf8_sequences[] =
{
"\x80",
"\xbf",
"\xff",
"\xfe",
"\xff\xff\xfe\xfe",
"\xc0\xaf",
"\xe0\x80\xaf",
"\xf0\x80\x80\xaf",
"\xf8\x80\x80\x80\xaf ",
"\xfc\x80\x80\x80\x80\xaf",
"\xc1\xbf",
"\xe0\x9f\xbf",
"\xf0\x8f\xbf\xbf",
"\xf8\x87\xbf\xbf\xbf",
"\xfc\x83\xbf\xbf\xbf\xbf",
"\xc0\x80",
"\xe0\x80\x80",
"\xf0\x80\x80\x80",
"\xf8\x80\x80\x80\x80",
"\xfc\x80\x80\x80\x80\x80",
"\xed\xa0\x80",
"\xed\xad\xbf",
"\xed\xae\x80",
"\xed\xaf\xbf",
"\xed\xb0\x80",
"\xed\xbe\x80",
"\xed\xbf\xbf",
"\xed\xa0\x80\xed\xb0\x80",
"\xed\xa0\x80\xed\xbf\xbf",
"\xed\xad\xbf\xed\xb0\x80",
"\xed\xad\xbf\xed\xbf\xbf",
"\xed\xae\x80\xed\xb0\x80",
"\xed\xae\x80\xed\xbf\xbf",
"\xed\xaf\xbf\xed\xb0\x80",
"\xed\xaf\xbf\xed\xbf\xbf",
};
std::int64_t g_seed;
void print_ascii_number(std::string& output, std::int64_t val)
{
const bool overflow = g_seed == 1;
const bool underflow = g_seed == 2;
const bool negative = g_seed == 3;
const bool double_negative = g_seed == 4;
const bool zero = g_seed == 5;
g_seed -= 5;
char const* numbers = "0123456789";
if (zero)
{
output += '0';
}
else if (underflow)
{
output += '-';
for (int i = 0; i < 100; ++i) output += numbers[rand() % 10];
return;
}
else if (overflow)
{
for (int i = 0; i < 100; ++i) output += numbers[rand() % 10];
return;
}
else
{
if (negative) output += '-';
else if (double_negative) output += "--";
char buf[50];
std::snprintf(buf, sizeof(buf), "%" PRId64 "", val);
output += buf;
}
}
void print_string(std::string& output, std::string str)
{
const bool empty_string = g_seed == 1;
g_seed -= 1;
if (empty_string)
{
print_ascii_number(output, 0);
output += ':';
return;
}
const bool random_string = g_seed > 0 && g_seed <= 1000;
const int str_seed = int(g_seed) - 1;
g_seed -= 1000;
if (random_string)
{
static mt19937 random_engine(str_seed);
uniform_int_distribution<> d(0, 255);
for (int i = 0; i < int(str.size()); ++i)
str[i] = std::uint8_t(d(random_engine));
print_ascii_number(output, str.size());
output += ':';
output += str;
return;
}
const int num_sequences = (sizeof(invalid_utf8_sequences)/sizeof(char const*));
const bool invalid_utf8 = g_seed <= num_sequences && g_seed > 0;
if (invalid_utf8)
str += invalid_utf8_sequences[g_seed-1];
g_seed -= num_sequences;
print_ascii_number(output, str.size());
output += ':';
output += str;
}
void print_terminate(std::string& output)
{
const bool unterminated = g_seed == 1;
g_seed -= 1;
if (!unterminated) output += 'e';
}
void print_int(std::string& output, std::int64_t value)
{
const bool double_int = g_seed == 1;
g_seed -= 1;
if (double_int) output += 'i';
output += 'i';
print_ascii_number(output, value);
print_terminate(output);
}
void print_dict(std::string& output)
{
const bool double_dict = g_seed == 1;
g_seed -= 1;
if (double_dict) output += 'd';
output += 'd';
}
void print_list(std::string& output)
{
const bool double_list = g_seed == 1;
g_seed -= 1;
if (double_list) output += 'l';
output += 'l';
}
void render_arbitrary_item(std::string& out)
{
if (g_seed <= 0) return;
std::string option;
print_int(option, 1337);
if (g_seed <= 0)
{
out += option;
return;
}
option.clear();
print_string(option, "abcdefgh");
if (g_seed <= 0)
{
out += option;
return;
}
option.clear();
print_dict(option);
print_string(option, "abcdefgh");
print_int(option, 1337);
print_terminate(option);
if (g_seed <= 0)
{
out += option;
return;
}
option.clear();
print_list(option);
print_string(option, "abcdefgh");
print_terminate(option);
if (g_seed <= 0)
{
out += option;
return;
}
}
void render_variant(std::string& out, bdecode_node const& e)
{
switch (e.type())
{
case bdecode_node::dict_t:
print_dict(out);
for (int i = 0; i < e.dict_size(); ++i)
{
std::pair<lt::string_view, bdecode_node> item = e.dict_at(i);
const bool duplicate = g_seed == 1;
const bool skipped = g_seed == 2;
g_seed -= 2;
if (duplicate)
{
print_string(out, item.first.to_string());
render_variant(out, item.second);
}
if (!skipped)
{
print_string(out, item.first.to_string());
render_variant(out, item.second);
}
render_arbitrary_item(out);
}
print_terminate(out);
break;
case bdecode_node::list_t:
print_list(out);
for (int i = 0; i < e.list_size(); ++i)
{
const bool duplicate = g_seed == 1;
const bool skipped = g_seed == 2;
g_seed -= 2;
if (duplicate) render_variant(out, e.list_at(i));
render_arbitrary_item(out);
if (!skipped) render_variant(out, e.list_at(i));
}
print_terminate(out);
break;
case bdecode_node::int_t:
print_int(out, e.int_value());
break;
case bdecode_node::string_t:
print_string(out, e.string_value().to_string());
break;
default:
abort();
}
}
int load_file(std::string const& filename, std::vector<char>& v
, lt::error_code& ec, int limit = 8000000)
{
ec.clear();
FILE* f = fopen(filename.c_str(), "rb");
if (f == nullptr)
{
ec.assign(errno, boost::system::system_category());
return -1;
}
int r = fseek(f, 0, SEEK_END);
if (r != 0)
{
ec.assign(errno, boost::system::system_category());
fclose(f);
return -1;
}
long s = ftell(f);
if (s < 0)
{
ec.assign(errno, boost::system::system_category());
fclose(f);
return -1;
}
if (s > limit)
{
fclose(f);
return -2;
}
r = fseek(f, 0, SEEK_SET);
if (r != 0)
{
ec.assign(errno, boost::system::system_category());
fclose(f);
return -1;
}
v.resize(s);
if (s == 0)
{
fclose(f);
return 0;
}
r = int(fread(&v[0], 1, v.size(), f));
if (r < 0)
{
ec.assign(errno, boost::system::system_category());
fclose(f);
return -1;
}
fclose(f);
if (r != s) return -3;
return 0;
}
int main(int argc, char const* argv[])
{
std::vector<char> buf;
lt::error_code ec;
if (argc < 2)
{
std::fprintf(stderr, "usage: fuzz_torrent torrent-file [torrent-file ...]\n");
return 1;
}
--argc;
++argv;
for (;argc > 0; --argc, ++argv)
{
int ret = load_file(*argv, buf, ec);
if (ret < 0)
{
std::fprintf(stderr, "ERROR loading file: %s\n%s\n"
, *argv, ec.message().c_str());
continue;
}
bdecode_node e;
if (buf.empty() || bdecode(&buf[0], &buf[0] + buf.size(), e, ec) != 0)
{
std::fprintf(stderr, "ERROR parsing file: %s\n%s\n"
, *argv, ec.message().c_str());
continue;
}
std::string test_buffer;
int i = 0;
for (i = 0; i < 10000000; ++i)
{
g_seed = i;
test_buffer.clear();
render_variant(test_buffer, e);
lt::error_code ec;
lt::torrent_info t(test_buffer, ec, lt::from_span);
// TODO: add option to save to file unconditionally (to test other clients)
/*
{
std::fprintf(stderr, "saving %d\n", i);
char filename[100];
std::snprintf(filename, sizeof(filename), "torrents/fuzz-%d.torrent", i);
FILE* f = fopen(filename, "wb+");
if (f == 0)
{
std::fprintf(stderr, "ERROR saving file: (%d) %s\n", errno, strerror(errno));
return 1;
}
fwrite(test_buffer.c_str(), test_buffer.size(), 1, f);
fclose(f);
}
*/
if (g_seed > 0) break;
}
std::fprintf(stderr, "tested %d variants of %s\n", i, *argv);
}
return 0;
}