refactor: remove tr_sys file_map_for_reading() (#3723)
This commit is contained in:
parent
6e44adabf1
commit
db3467b553
|
@ -7,6 +7,7 @@
|
|||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <cstdlib> // bsearch()
|
||||
#include <fstream>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
|
@ -27,84 +28,39 @@
|
|||
|
||||
void BlocklistFile::close()
|
||||
{
|
||||
if (rules_ != nullptr)
|
||||
{
|
||||
tr_sys_file_unmap(rules_, byte_count_);
|
||||
tr_sys_file_close(fd_);
|
||||
rules_ = nullptr;
|
||||
rule_count_ = 0;
|
||||
byte_count_ = 0;
|
||||
fd_ = TR_BAD_SYS_FILE;
|
||||
}
|
||||
rules_.clear();
|
||||
}
|
||||
|
||||
void BlocklistFile::load()
|
||||
void BlocklistFile::ensureLoaded() const
|
||||
{
|
||||
close();
|
||||
|
||||
auto const info = tr_sys_path_get_info(getFilename());
|
||||
if (!info)
|
||||
if (!std::empty(rules_))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto const byte_count = info->size;
|
||||
if (byte_count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
tr_error* error = nullptr;
|
||||
auto const fd = tr_sys_file_open(getFilename(), TR_SYS_FILE_READ, 0, &error);
|
||||
if (fd == TR_BAD_SYS_FILE)
|
||||
auto in = std::ifstream{ filename_, std::ios_base::in | std::ios_base::binary };
|
||||
if (!in)
|
||||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't read '{path}': {error} ({error_code})"),
|
||||
fmt::arg("path", getFilename()),
|
||||
fmt::arg("error", error->message),
|
||||
fmt::arg("error_code", error->code)));
|
||||
tr_error_free(error);
|
||||
fmt::arg("path", filename_),
|
||||
fmt::arg("error", tr_strerror(errno)),
|
||||
fmt::arg("error_code", errno)));
|
||||
return;
|
||||
}
|
||||
|
||||
rules_ = static_cast<struct IPv4Range*>(tr_sys_file_map_for_reading(fd, 0, byte_count, &error));
|
||||
if (rules_ == nullptr)
|
||||
auto range = IPv4Range{};
|
||||
while (in.read(reinterpret_cast<char*>(&range), sizeof(range)))
|
||||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't read '{path}': {error} ({error_code})"),
|
||||
fmt::arg("path", getFilename()),
|
||||
fmt::arg("error", error->message),
|
||||
fmt::arg("error_code", error->code)));
|
||||
tr_sys_file_close(fd);
|
||||
tr_error_free(error);
|
||||
return;
|
||||
rules_.emplace_back(range);
|
||||
}
|
||||
|
||||
fd_ = fd;
|
||||
byte_count_ = byte_count;
|
||||
rule_count_ = byte_count / sizeof(IPv4Range);
|
||||
|
||||
tr_logAddInfo(fmt::format(
|
||||
ngettext("Blocklist '{path}' has {count} entry", "Blocklist '{path}' has {count} entries", rule_count_),
|
||||
fmt::arg("path", tr_sys_path_basename(getFilename())),
|
||||
fmt::arg("count", rule_count_)));
|
||||
ngettext("Blocklist '{path}' has {count} entry", "Blocklist '{path}' has {count} entries", std::size(rules_)),
|
||||
fmt::arg("path", tr_sys_path_basename(filename_)),
|
||||
fmt::arg("count", std::size(rules_))));
|
||||
}
|
||||
|
||||
void BlocklistFile::ensureLoaded()
|
||||
{
|
||||
if (rules_ == nullptr)
|
||||
{
|
||||
load();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: unused
|
||||
//static void blocklistDelete(tr_blocklistFile* b)
|
||||
//{
|
||||
// blocklistClose(b);
|
||||
// tr_sys_path_remove(b->filename, nullptr);
|
||||
//}
|
||||
|
||||
/***
|
||||
**** PACKAGE-VISIBLE
|
||||
***/
|
||||
|
@ -120,7 +76,7 @@ bool BlocklistFile::hasAddress(tr_address const& addr)
|
|||
|
||||
ensureLoaded();
|
||||
|
||||
if (rules_ == nullptr || rule_count_ == 0)
|
||||
if (std::empty(rules_))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -130,7 +86,7 @@ bool BlocklistFile::hasAddress(tr_address const& addr)
|
|||
// std::binary_search works differently and requires a less-than comparison
|
||||
// and two arguments of the same type. std::bsearch is the right choice.
|
||||
auto const* range = static_cast<IPv4Range const*>(
|
||||
std::bsearch(&needle, rules_, rule_count_, sizeof(IPv4Range), IPv4Range::compareAddressToRange));
|
||||
std::bsearch(&needle, std::data(rules_), std::size(rules_), sizeof(IPv4Range), IPv4Range::compareAddressToRange));
|
||||
|
||||
return range != nullptr;
|
||||
}
|
||||
|
@ -267,114 +223,101 @@ bool BlocklistFile::compareAddressRangesByFirstAddress(IPv4Range const& a, IPv4R
|
|||
|
||||
size_t BlocklistFile::setContent(char const* filename)
|
||||
{
|
||||
int inCount = 0;
|
||||
auto line = std::array<char, 2048>{};
|
||||
tr_error* error = nullptr;
|
||||
|
||||
if (filename == nullptr)
|
||||
{
|
||||
return 0;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto const in = tr_sys_file_open(filename, TR_SYS_FILE_READ, 0, &error);
|
||||
if (in == TR_BAD_SYS_FILE)
|
||||
auto in = std::ifstream{ filename };
|
||||
if (!in.is_open())
|
||||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't read '{path}': {error} ({error_code})"),
|
||||
fmt::arg("path", filename),
|
||||
fmt::arg("error", error->message),
|
||||
fmt::arg("error_code", error->code)));
|
||||
tr_error_free(error);
|
||||
return 0;
|
||||
fmt::arg("error", tr_strerror(errno)),
|
||||
fmt::arg("error_code", errno)));
|
||||
return {};
|
||||
}
|
||||
|
||||
close();
|
||||
|
||||
auto const out = tr_sys_file_open(
|
||||
getFilename(),
|
||||
TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE | TR_SYS_FILE_TRUNCATE,
|
||||
0666,
|
||||
&error);
|
||||
if (out == TR_BAD_SYS_FILE)
|
||||
auto line = std::string{};
|
||||
auto line_number = size_t{ 0U };
|
||||
auto ranges = std::vector<IPv4Range>{};
|
||||
while (std::getline(in, line))
|
||||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't read '{path}': {error} ({error_code})"),
|
||||
fmt::arg("path", getFilename()),
|
||||
fmt::arg("error", error->message),
|
||||
fmt::arg("error_code", error->code)));
|
||||
tr_error_free(error);
|
||||
tr_sys_file_close(in);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* load the rules into memory */
|
||||
std::vector<IPv4Range> ranges;
|
||||
while (tr_sys_file_read_line(in, std::data(line), std::size(line)))
|
||||
{
|
||||
IPv4Range range = {};
|
||||
|
||||
++inCount;
|
||||
|
||||
if (!parseLine(std::data(line), &range))
|
||||
++line_number;
|
||||
auto range = IPv4Range{};
|
||||
if (!parseLine(line.c_str(), &range))
|
||||
{
|
||||
/* don't try to display the actual lines - it causes issues */
|
||||
tr_logAddWarn(fmt::format(_("Couldn't parse line: '{line}'"), fmt::arg("line", inCount)));
|
||||
tr_logAddWarn(fmt::format(_("Couldn't parse line: '{line}'"), fmt::arg("line", line_number)));
|
||||
continue;
|
||||
}
|
||||
|
||||
ranges.push_back(range);
|
||||
}
|
||||
in.close();
|
||||
|
||||
if (!std::empty(ranges)) // sort and merge
|
||||
if (std::empty(ranges))
|
||||
{
|
||||
size_t keep = 0; // index in ranges
|
||||
|
||||
std::sort(std::begin(ranges), std::end(ranges), BlocklistFile::compareAddressRangesByFirstAddress);
|
||||
|
||||
// merge
|
||||
for (auto const& r : ranges)
|
||||
{
|
||||
if (ranges[keep].end_ < r.begin_)
|
||||
{
|
||||
ranges[++keep] = r;
|
||||
}
|
||||
else if (ranges[keep].end_ < r.end_)
|
||||
{
|
||||
ranges[keep].end_ = r.end_;
|
||||
}
|
||||
}
|
||||
|
||||
TR_ASSERT_MSG(keep + 1 <= std::size(ranges), "Can shrink `ranges` or leave intact, but not grow");
|
||||
ranges.resize(keep + 1);
|
||||
|
||||
#ifdef TR_ENABLE_ASSERTS
|
||||
assertValidRules(ranges);
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!tr_sys_file_write(out, ranges.data(), sizeof(IPv4Range) * std::size(ranges), nullptr, &error))
|
||||
size_t keep = 0; // index in ranges
|
||||
|
||||
std::sort(std::begin(ranges), std::end(ranges), BlocklistFile::compareAddressRangesByFirstAddress);
|
||||
|
||||
// merge
|
||||
for (auto const& range : ranges)
|
||||
{
|
||||
if (ranges[keep].end_ < range.begin_)
|
||||
{
|
||||
ranges[++keep] = range;
|
||||
}
|
||||
else if (ranges[keep].end_ < range.end_)
|
||||
{
|
||||
ranges[keep].end_ = range.end_;
|
||||
}
|
||||
}
|
||||
|
||||
TR_ASSERT_MSG(keep + 1 <= std::size(ranges), "Can shrink `ranges` or leave intact, but not grow");
|
||||
ranges.resize(keep + 1);
|
||||
|
||||
#ifdef TR_ENABLE_ASSERTS
|
||||
assertValidRules(ranges);
|
||||
#endif
|
||||
|
||||
auto out = std::ofstream{ filename_, std::ios_base::out | std::ios_base::trunc | std::ios_base::binary };
|
||||
if (!out.is_open())
|
||||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't read '{path}': {error} ({error_code})"),
|
||||
fmt::arg("path", filename_),
|
||||
fmt::arg("error", tr_strerror(errno)),
|
||||
fmt::arg("error_code", errno)));
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!out.write(reinterpret_cast<char const*>(ranges.data()), std::size(ranges) * sizeof(IPv4Range)))
|
||||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't save '{path}': {error} ({error_code})"),
|
||||
fmt::arg("path", getFilename()),
|
||||
fmt::arg("error", error->message),
|
||||
fmt::arg("error_code", error->code)));
|
||||
tr_error_free(error);
|
||||
fmt::arg("path", filename_),
|
||||
fmt::arg("error", tr_strerror(errno)),
|
||||
fmt::arg("error_code", errno)));
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_logAddInfo(fmt::format(
|
||||
ngettext("Blocklist '{path}' has {count} entry", "Blocklist '{path}' has {count} entries", rule_count_),
|
||||
fmt::arg("path", tr_sys_path_basename(getFilename())),
|
||||
fmt::arg("count", rule_count_)));
|
||||
ngettext("Blocklist '{path}' has {count} entry", "Blocklist '{path}' has {count} entries", std::size(rules_)),
|
||||
fmt::arg("path", tr_sys_path_basename(filename_)),
|
||||
fmt::arg("count", std::size(rules_))));
|
||||
}
|
||||
|
||||
tr_sys_file_close(out);
|
||||
tr_sys_file_close(in);
|
||||
out.close();
|
||||
|
||||
load();
|
||||
|
||||
return std::size(ranges);
|
||||
close();
|
||||
ensureLoaded();
|
||||
return std::size(rules_);
|
||||
}
|
||||
|
||||
#ifdef TR_ENABLE_ASSERTS
|
||||
|
|
|
@ -31,8 +31,8 @@ public:
|
|||
BlocklistFile& operator=(BlocklistFile&&) = delete;
|
||||
|
||||
BlocklistFile(char const* filename, bool isEnabled)
|
||||
: is_enabled_(isEnabled)
|
||||
, filename_(filename)
|
||||
: filename_(filename)
|
||||
, is_enabled_(isEnabled)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -41,22 +41,21 @@ public:
|
|||
close();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr auto& filename() const
|
||||
{
|
||||
return filename_;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool exists() const
|
||||
{
|
||||
return tr_sys_path_exists(getFilename(), nullptr);
|
||||
return tr_sys_path_exists(filename_.c_str(), nullptr);
|
||||
}
|
||||
|
||||
[[nodiscard]] char const* getFilename() const
|
||||
{
|
||||
return filename_.c_str();
|
||||
}
|
||||
|
||||
// TODO: This function should be const, but cannot be const due to it calling ensureLoaded()
|
||||
size_t getRuleCount()
|
||||
[[nodiscard]] size_t getRuleCount() const
|
||||
{
|
||||
ensureLoaded();
|
||||
|
||||
return rule_count_;
|
||||
return std::size(rules_);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool isEnabled() const
|
||||
|
@ -100,7 +99,7 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
void ensureLoaded();
|
||||
void ensureLoaded() const;
|
||||
void load();
|
||||
void close();
|
||||
|
||||
|
@ -116,12 +115,8 @@ private:
|
|||
static void assertValidRules(std::vector<IPv4Range> const& ranges);
|
||||
#endif
|
||||
|
||||
bool is_enabled_;
|
||||
tr_sys_file_t fd_{ TR_BAD_SYS_FILE };
|
||||
size_t rule_count_ = 0;
|
||||
uint64_t byte_count_ = 0;
|
||||
std::string const filename_;
|
||||
|
||||
/// @brief Not a container, memory mapped file
|
||||
IPv4Range* rules_ = nullptr;
|
||||
bool is_enabled_ = false;
|
||||
mutable std::vector<IPv4Range> rules_;
|
||||
};
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include <fcntl.h> /* O_LARGEFILE, posix_fadvise(), [posix_]fallocate(), fcntl() */
|
||||
#include <libgen.h> /* basename(), dirname() */
|
||||
#include <sys/file.h> /* flock() */
|
||||
#include <sys/mman.h> /* mmap(), munmap() */
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h> /* lseek(), write(), ftruncate(), pread(), pwrite(), pathconf(), etc */
|
||||
|
@ -1070,37 +1069,6 @@ bool tr_sys_file_preallocate(tr_sys_file_t handle, uint64_t size, int flags, tr_
|
|||
return false;
|
||||
}
|
||||
|
||||
void* tr_sys_file_map_for_reading(tr_sys_file_t handle, uint64_t offset, uint64_t size, tr_error** error)
|
||||
{
|
||||
TR_ASSERT(handle != TR_BAD_SYS_FILE);
|
||||
TR_ASSERT(size > 0);
|
||||
|
||||
void* ret = mmap(nullptr, size, PROT_READ, MAP_SHARED, handle, offset);
|
||||
|
||||
if (ret == MAP_FAILED) // NOLINT(performance-no-int-to-ptr)
|
||||
{
|
||||
set_system_error(error, errno);
|
||||
ret = nullptr;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool tr_sys_file_unmap(void const* address, uint64_t size, tr_error** error)
|
||||
{
|
||||
TR_ASSERT(address != nullptr);
|
||||
TR_ASSERT(size > 0);
|
||||
|
||||
bool const ret = munmap(const_cast<void*>(address), size) != -1;
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
set_system_error(error, errno);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool tr_sys_file_lock([[maybe_unused]] tr_sys_file_t handle, [[maybe_unused]] int operation, tr_error** error)
|
||||
{
|
||||
TR_ASSERT(handle != TR_BAD_SYS_FILE);
|
||||
|
|
|
@ -1198,54 +1198,6 @@ bool tr_sys_file_preallocate(tr_sys_file_t handle, uint64_t size, int flags, tr_
|
|||
return tr_sys_file_truncate(handle, size, error);
|
||||
}
|
||||
|
||||
void* tr_sys_file_map_for_reading(tr_sys_file_t handle, uint64_t offset, uint64_t size, tr_error** error)
|
||||
{
|
||||
TR_ASSERT(handle != TR_BAD_SYS_FILE);
|
||||
TR_ASSERT(size > 0);
|
||||
|
||||
if (size > MAXSIZE_T)
|
||||
{
|
||||
set_system_error(error, ERROR_INVALID_PARAMETER);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void* ret = nullptr;
|
||||
HANDLE mappingHandle = CreateFileMappingW(handle, nullptr, PAGE_READONLY, 0, 0, nullptr);
|
||||
|
||||
if (mappingHandle != nullptr)
|
||||
{
|
||||
ULARGE_INTEGER native_offset;
|
||||
|
||||
native_offset.QuadPart = offset;
|
||||
|
||||
ret = MapViewOfFile(mappingHandle, FILE_MAP_READ, native_offset.u.HighPart, native_offset.u.LowPart, (SIZE_T)size);
|
||||
}
|
||||
|
||||
if (ret == nullptr)
|
||||
{
|
||||
set_system_error(error, GetLastError());
|
||||
}
|
||||
|
||||
CloseHandle(mappingHandle);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool tr_sys_file_unmap(void const* address, [[maybe_unused]] uint64_t size, tr_error** error)
|
||||
{
|
||||
TR_ASSERT(address != nullptr);
|
||||
TR_ASSERT(size > 0);
|
||||
|
||||
bool ret = UnmapViewOfFile(address);
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
set_system_error(error, GetLastError());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool tr_sys_file_lock(tr_sys_file_t handle, int operation, tr_error** error)
|
||||
{
|
||||
TR_ASSERT(handle != TR_BAD_SYS_FILE);
|
||||
|
|
|
@ -13,72 +13,6 @@
|
|||
|
||||
using namespace std::literals;
|
||||
|
||||
bool tr_sys_file_read_line(tr_sys_file_t handle, char* buffer, size_t buffer_size, tr_error** error)
|
||||
{
|
||||
TR_ASSERT(handle != TR_BAD_SYS_FILE);
|
||||
TR_ASSERT(buffer != nullptr);
|
||||
TR_ASSERT(buffer_size > 0);
|
||||
|
||||
auto ret = bool{};
|
||||
auto offset = size_t{};
|
||||
|
||||
while (buffer_size > 0)
|
||||
{
|
||||
size_t const bytes_needed = std::min(buffer_size, size_t{ 1024 });
|
||||
auto bytes_read = uint64_t{};
|
||||
ret = tr_sys_file_read(handle, buffer + offset, bytes_needed, &bytes_read, error);
|
||||
|
||||
if (!ret || (offset == 0 && bytes_read == 0))
|
||||
{
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
|
||||
TR_ASSERT(bytes_read <= bytes_needed);
|
||||
TR_ASSERT(bytes_read <= buffer_size);
|
||||
|
||||
int64_t delta = 0;
|
||||
|
||||
for (size_t i = 0; i < bytes_read; ++i)
|
||||
{
|
||||
if (buffer[offset] == '\n')
|
||||
{
|
||||
delta = i - (int64_t)bytes_read + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
++offset;
|
||||
--buffer_size;
|
||||
}
|
||||
|
||||
if (delta != 0 || buffer_size == 0 || bytes_read == 0)
|
||||
{
|
||||
if (delta != 0)
|
||||
{
|
||||
ret = tr_sys_file_seek(handle, delta, TR_SEEK_CUR, nullptr, error);
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (offset > 0 && buffer[offset - 1] == '\r')
|
||||
{
|
||||
buffer[offset - 1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer[offset] = '\0';
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool tr_sys_file_write_line(tr_sys_file_t handle, std::string_view buffer, tr_error** error)
|
||||
{
|
||||
TR_ASSERT(handle != TR_BAD_SYS_FILE);
|
||||
|
|
|
@ -541,32 +541,6 @@ bool tr_sys_file_advise(
|
|||
*/
|
||||
bool tr_sys_file_preallocate(tr_sys_file_t handle, uint64_t size, int flags, struct tr_error** error = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `mmap()` for files.
|
||||
*
|
||||
* @param[in] handle Valid file descriptor.
|
||||
* @param[in] offset Offset in file to map from.
|
||||
* @param[in] size Number of bytes to map.
|
||||
* @param[out] error Pointer to error object. Optional, pass `nullptr` if you
|
||||
* are not interested in error details.
|
||||
*
|
||||
* @return Pointer to mapped file data on success, `nullptr` otherwise (with
|
||||
* `error` set accordingly).
|
||||
*/
|
||||
void* tr_sys_file_map_for_reading(tr_sys_file_t handle, uint64_t offset, uint64_t size, struct tr_error** error = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `munmap()` for files.
|
||||
*
|
||||
* @param[in] address Pointer to mapped file data.
|
||||
* @param[in] size Size of mapped data in bytes.
|
||||
* @param[out] error Pointer to error object. Optional, pass `nullptr` if you
|
||||
* are not interested in error details.
|
||||
*
|
||||
* @return `True` on success, `false` otherwise (with `error` set accordingly).
|
||||
*/
|
||||
bool tr_sys_file_unmap(void const* address, uint64_t size, struct tr_error** error = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `flock()`.
|
||||
*
|
||||
|
@ -584,29 +558,6 @@ bool tr_sys_file_lock(tr_sys_file_t handle, int operation, struct tr_error** err
|
|||
|
||||
/* File-related wrappers (utility) */
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `fgets()`, removing EOL internally.
|
||||
*
|
||||
* Special care should be taken when reading from one of standard input streams
|
||||
* (@ref tr_std_sys_file_t) since no UTF-8 conversion is currently being made.
|
||||
*
|
||||
* Reading from other streams (files, pipes) also leaves data untouched, so it
|
||||
* should already be in UTF-8 encoding, or whichever else you expect.
|
||||
*
|
||||
* @param[in] handle Valid file descriptor.
|
||||
* @param[out] buffer Buffer to store read zero-terminated string to.
|
||||
* @param[in] buffer_size Buffer size in bytes, taking '\0' character into
|
||||
* account.
|
||||
* @param[out] error Pointer to error object. Optional, pass `nullptr` if
|
||||
* you are not interested in error details.
|
||||
*
|
||||
* @return `True` on success, `false` otherwise (with `error` set accordingly).
|
||||
* Note that `false` will also be returned in case of end of file; if
|
||||
* you need to distinguish the two, check if `error` is `nullptr`
|
||||
* afterwards.
|
||||
*/
|
||||
bool tr_sys_file_read_line(tr_sys_file_t handle, char* buffer, size_t buffer_size, struct tr_error** error = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Portability wrapper for `fputs()`, appending EOL internally.
|
||||
*
|
||||
|
|
|
@ -22,7 +22,7 @@ class tr_natpmp
|
|||
public:
|
||||
tr_natpmp()
|
||||
{
|
||||
natpmp_.s = TR_BAD_SOCKET; /* socket */
|
||||
natpmp_.s = static_cast<decltype(natpmp_.s)>(TR_BAD_SOCKET);
|
||||
}
|
||||
|
||||
~tr_natpmp()
|
||||
|
|
|
@ -2396,7 +2396,7 @@ size_t tr_blocklistSetContent(tr_session* session, char const* content_filename)
|
|||
auto const it = std::find_if(
|
||||
std::begin(src),
|
||||
std::end(src),
|
||||
[&name](auto const& blocklist) { return tr_strvEndsWith(blocklist->getFilename(), name); });
|
||||
[&name](auto const& blocklist) { return tr_strvEndsWith(blocklist->filename(), name); });
|
||||
|
||||
BlocklistFile* b = nullptr;
|
||||
if (it == std::end(src))
|
||||
|
|
|
@ -1295,134 +1295,6 @@ TEST_F(FileTest, filePreallocate)
|
|||
tr_sys_path_remove(path1);
|
||||
}
|
||||
|
||||
TEST_F(FileTest, map)
|
||||
{
|
||||
auto const test_dir = createTestDir(currentTestName());
|
||||
|
||||
auto const path1 = tr_pathbuf{ test_dir, "/a"sv };
|
||||
auto const contents = std::string{ "test" };
|
||||
createFileWithContents(path1, contents.data());
|
||||
|
||||
auto fd = tr_sys_file_open(path1, TR_SYS_FILE_READ | TR_SYS_FILE_WRITE, 0600);
|
||||
|
||||
tr_error* err = nullptr;
|
||||
auto map_len = contents.size();
|
||||
auto* view = static_cast<char*>(tr_sys_file_map_for_reading(fd, 0, map_len, &err));
|
||||
EXPECT_NE(nullptr, view);
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
EXPECT_EQ(contents, std::string(view, map_len));
|
||||
|
||||
#ifdef HAVE_UNIFIED_BUFFER_CACHE
|
||||
|
||||
auto const contents_2 = std::string{ "more" };
|
||||
auto n_written = uint64_t{};
|
||||
tr_sys_file_write_at(fd, contents_2.data(), contents_2.size(), 0, &n_written, &err);
|
||||
EXPECT_EQ(map_len, contents_2.size());
|
||||
EXPECT_EQ(map_len, n_written);
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
EXPECT_EQ(contents_2, std::string(view, map_len));
|
||||
|
||||
#endif
|
||||
|
||||
EXPECT_TRUE(tr_sys_file_unmap(view, map_len, &err));
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
|
||||
tr_sys_file_close(fd);
|
||||
|
||||
tr_sys_path_remove(path1);
|
||||
}
|
||||
|
||||
TEST_F(FileTest, fileUtilities)
|
||||
{
|
||||
auto const test_dir = createTestDir(currentTestName());
|
||||
|
||||
auto const path1 = tr_pathbuf{ test_dir, "/a"sv };
|
||||
auto const contents = std::string{ "a\nbc\r\ndef\nghij\r\n\n\nklmno\r" };
|
||||
createFileWithContents(path1, contents.data());
|
||||
|
||||
auto fd = tr_sys_file_open(path1, TR_SYS_FILE_READ, 0);
|
||||
|
||||
tr_error* err = nullptr;
|
||||
auto buffer = std::array<char, 16>{};
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size(), &err));
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
EXPECT_STREQ("a", buffer.data());
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size(), &err));
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
EXPECT_STREQ("bc", buffer.data());
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size(), &err));
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
EXPECT_STREQ("def", buffer.data());
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size(), &err));
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
EXPECT_STREQ("ghij", buffer.data());
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size(), &err));
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
EXPECT_STREQ("", buffer.data());
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size(), &err));
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
EXPECT_STREQ("", buffer.data());
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), 4, &err));
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
EXPECT_STREQ("klmn", buffer.data());
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size(), &err));
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
EXPECT_STREQ("o", buffer.data());
|
||||
EXPECT_FALSE(tr_sys_file_read_line(fd, buffer.data(), buffer.size(), &err));
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
EXPECT_STREQ("o", buffer.data()); // on EOF, buffer stays unchanged
|
||||
|
||||
tr_sys_file_close(fd);
|
||||
|
||||
fd = tr_sys_file_open(path1, TR_SYS_FILE_READ | TR_SYS_FILE_WRITE | TR_SYS_FILE_TRUNCATE, 0);
|
||||
|
||||
EXPECT_TRUE(tr_sys_file_write_line(fd, "p", &err));
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
EXPECT_TRUE(tr_sys_file_write_line(fd, "", &err));
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
EXPECT_TRUE(tr_sys_file_write_line(fd, "qr", &err));
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
EXPECT_TRUE(tr_sys_file_write_line(fd, "stu", &err));
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
EXPECT_TRUE(tr_sys_file_write_line(fd, "", &err));
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
EXPECT_TRUE(tr_sys_file_write_line(fd, "", &err));
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
EXPECT_TRUE(tr_sys_file_write_line(fd, "vwxy2", &err));
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
|
||||
tr_sys_file_seek(fd, 0, TR_SEEK_SET, nullptr);
|
||||
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size(), &err));
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
EXPECT_STREQ("p", buffer.data());
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size(), &err));
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
EXPECT_STREQ("", buffer.data());
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size(), &err));
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
EXPECT_STREQ("qr", buffer.data());
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size(), &err));
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
EXPECT_STREQ("stu", buffer.data());
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size(), &err));
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
EXPECT_STREQ("", buffer.data());
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size(), &err));
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
EXPECT_STREQ("", buffer.data());
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size(), &err));
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
EXPECT_STREQ("vwxy2", buffer.data());
|
||||
EXPECT_FALSE(tr_sys_file_read_line(fd, buffer.data(), buffer.size(), &err));
|
||||
EXPECT_EQ(nullptr, err) << *err;
|
||||
EXPECT_STREQ("vwxy2", buffer.data()); // on EOF, buffer stays unchanged
|
||||
|
||||
tr_sys_file_close(fd);
|
||||
|
||||
tr_sys_path_remove(path1);
|
||||
}
|
||||
|
||||
TEST_F(FileTest, dirCreate)
|
||||
{
|
||||
auto const test_dir = createTestDir(currentTestName());
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "test-fixtures.h"
|
||||
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
@ -122,38 +123,26 @@ TEST_P(SubprocessTest, SpawnAsyncArgs)
|
|||
|
||||
waitForFileToExist(result_path);
|
||||
|
||||
auto fd = tr_sys_file_open(result_path.data(), TR_SYS_FILE_READ, 0); // NOLINT
|
||||
EXPECT_NE(TR_BAD_SYS_FILE, fd);
|
||||
auto in = std::ifstream{ result_path, std::ios_base::in };
|
||||
EXPECT_TRUE(in.is_open());
|
||||
|
||||
auto buffer = std::array<char, 1024>{};
|
||||
auto line = std::string{};
|
||||
EXPECT_TRUE(std::getline(in, line));
|
||||
EXPECT_EQ(test_arg1, line);
|
||||
|
||||
buffer[0] = '\0';
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size()));
|
||||
buffer.back() = '\0';
|
||||
EXPECT_EQ(test_arg1, buffer.data());
|
||||
EXPECT_TRUE(std::getline(in, line));
|
||||
EXPECT_EQ(test_arg2, line);
|
||||
|
||||
buffer[0] = '\0';
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size()));
|
||||
buffer.back() = '\0';
|
||||
EXPECT_EQ(test_arg2, buffer.data());
|
||||
|
||||
buffer[0] = '\0';
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size()));
|
||||
buffer.back() = '\0';
|
||||
EXPECT_EQ(test_arg3, buffer.data());
|
||||
EXPECT_TRUE(std::getline(in, line));
|
||||
EXPECT_EQ(test_arg3, line);
|
||||
|
||||
if (allow_batch_metachars)
|
||||
{
|
||||
buffer[0] = '\0';
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size()));
|
||||
buffer.back() = '\0';
|
||||
EXPECT_EQ(test_arg4, buffer.data());
|
||||
EXPECT_TRUE(std::getline(in, line));
|
||||
EXPECT_EQ(test_arg4, line);
|
||||
}
|
||||
|
||||
EXPECT_FALSE(tr_sys_file_read_line(fd, buffer.data(), buffer.size()));
|
||||
buffer.back() = '\0';
|
||||
|
||||
tr_sys_file_close(fd);
|
||||
EXPECT_FALSE(std::getline(in, line));
|
||||
}
|
||||
|
||||
TEST_P(SubprocessTest, SpawnAsyncEnv)
|
||||
|
@ -203,38 +192,29 @@ TEST_P(SubprocessTest, SpawnAsyncEnv)
|
|||
|
||||
waitForFileToExist(result_path);
|
||||
|
||||
auto fd = tr_sys_file_open(result_path.data(), TR_SYS_FILE_READ, 0); // NOLINT
|
||||
EXPECT_NE(TR_BAD_SYS_FILE, fd);
|
||||
auto in = std::ifstream{ result_path, std::ios_base::in };
|
||||
EXPECT_TRUE(in.is_open());
|
||||
|
||||
auto buffer = std::array<char, 1024>{};
|
||||
auto line = std::string{};
|
||||
EXPECT_TRUE(std::getline(in, line));
|
||||
EXPECT_EQ(test_env_value1, line);
|
||||
|
||||
buffer[0] = '\0';
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size()));
|
||||
EXPECT_EQ(test_env_value1, buffer.data());
|
||||
EXPECT_TRUE(std::getline(in, line));
|
||||
EXPECT_EQ(test_env_value2, line);
|
||||
|
||||
buffer[0] = '\0';
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size()));
|
||||
EXPECT_EQ(test_env_value2, buffer.data());
|
||||
EXPECT_TRUE(std::getline(in, line));
|
||||
EXPECT_EQ(test_env_value3, line);
|
||||
|
||||
buffer[0] = '\0';
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size()));
|
||||
EXPECT_EQ(test_env_value3, buffer.data());
|
||||
EXPECT_TRUE(std::getline(in, line));
|
||||
EXPECT_EQ(test_env_value4, line);
|
||||
|
||||
buffer[0] = '\0';
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size()));
|
||||
EXPECT_EQ(test_env_value4, buffer.data());
|
||||
EXPECT_TRUE(std::getline(in, line));
|
||||
EXPECT_EQ(test_env_value5, line);
|
||||
|
||||
buffer[0] = '\0';
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size()));
|
||||
EXPECT_EQ(test_env_value5, buffer.data());
|
||||
EXPECT_TRUE(std::getline(in, line));
|
||||
EXPECT_EQ("<null>"sv, line);
|
||||
|
||||
buffer[0] = '\0';
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size()));
|
||||
EXPECT_STREQ("<null>", buffer.data());
|
||||
|
||||
EXPECT_FALSE(tr_sys_file_read_line(fd, buffer.data(), buffer.size()));
|
||||
|
||||
tr_sys_file_close(fd);
|
||||
EXPECT_FALSE(std::getline(in, line));
|
||||
}
|
||||
|
||||
TEST_P(SubprocessTest, SpawnAsyncCwdExplicit)
|
||||
|
@ -251,20 +231,18 @@ TEST_P(SubprocessTest, SpawnAsyncCwdExplicit)
|
|||
|
||||
waitForFileToExist(result_path);
|
||||
|
||||
auto fd = tr_sys_file_open(result_path.data(), TR_SYS_FILE_READ, 0); // NOLINT
|
||||
EXPECT_NE(TR_BAD_SYS_FILE, fd);
|
||||
auto in = std::ifstream{ result_path, std::ios_base::in };
|
||||
EXPECT_TRUE(in.is_open());
|
||||
|
||||
auto buffer = std::array<char, 1024>{};
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size()));
|
||||
auto line = std::string{};
|
||||
EXPECT_TRUE(std::getline(in, line));
|
||||
auto expected = std::string{ test_dir };
|
||||
tr_sys_path_native_separators(std::data(expected));
|
||||
auto actual = std::string{ std::data(buffer) };
|
||||
auto actual = line;
|
||||
tr_sys_path_native_separators(std::data(actual));
|
||||
EXPECT_EQ(expected, actual);
|
||||
|
||||
EXPECT_FALSE(tr_sys_file_read_line(fd, buffer.data(), buffer.size()));
|
||||
|
||||
tr_sys_file_close(fd);
|
||||
EXPECT_FALSE(std::getline(in, line));
|
||||
}
|
||||
|
||||
TEST_P(SubprocessTest, SpawnAsyncCwdInherit)
|
||||
|
@ -280,15 +258,17 @@ TEST_P(SubprocessTest, SpawnAsyncCwdInherit)
|
|||
EXPECT_EQ(nullptr, error) << *error;
|
||||
|
||||
waitForFileToExist(result_path);
|
||||
auto fd = tr_sys_file_open(result_path.data(), TR_SYS_FILE_READ, 0); // NOLINT
|
||||
EXPECT_NE(TR_BAD_SYS_FILE, fd);
|
||||
|
||||
auto buffer = std::array<char, 1024>{};
|
||||
EXPECT_TRUE(tr_sys_file_read_line(fd, buffer.data(), buffer.size()));
|
||||
EXPECT_EQ(expected_cwd, tr_sys_path_native_separators(&buffer.front()));
|
||||
EXPECT_FALSE(tr_sys_file_read_line(fd, buffer.data(), buffer.size()));
|
||||
auto in = std::ifstream{ result_path, std::ios_base::in };
|
||||
EXPECT_TRUE(in.is_open());
|
||||
|
||||
tr_sys_file_close(fd);
|
||||
auto line = std::string{};
|
||||
EXPECT_TRUE(std::getline(in, line));
|
||||
auto actual = line;
|
||||
tr_sys_path_native_separators(std::data(actual));
|
||||
EXPECT_EQ(expected_cwd, actual);
|
||||
|
||||
EXPECT_FALSE(std::getline(in, line));
|
||||
}
|
||||
|
||||
TEST_P(SubprocessTest, SpawnAsyncCwdMissing)
|
||||
|
|
Loading…
Reference in New Issue