2015-04-22 18:36:40 +02:00
|
|
|
#ifndef IOUTILITIES_COPY_H
|
|
|
|
#define IOUTILITIES_COPY_H
|
|
|
|
|
2019-08-13 00:32:59 +02:00
|
|
|
#include "./nativefilestream.h"
|
2023-04-29 12:53:18 +02:00
|
|
|
#if defined(CPP_UTILITIES_USE_PLATFORM_SPECIFIC_API_FOR_OPTIMIZING_COPY_HELPER) && defined(CPP_UTILITIES_USE_NATIVE_FILE_BUFFER) \
|
|
|
|
&& defined(PLATFORM_LINUX)
|
2019-08-14 21:57:17 +02:00
|
|
|
#define CPP_UTILITIES_USE_SEND_FILE
|
2023-03-19 20:09:14 +01:00
|
|
|
#include "../conversion/stringbuilder.h"
|
|
|
|
#endif
|
2019-08-14 21:57:17 +02:00
|
|
|
|
2023-03-19 20:09:14 +01:00
|
|
|
#ifdef CPP_UTILITIES_USE_SEND_FILE
|
2019-08-14 21:57:17 +02:00
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/sendfile.h>
|
2023-03-19 20:09:14 +01:00
|
|
|
#endif
|
2019-08-14 21:57:17 +02:00
|
|
|
|
2023-03-19 20:09:14 +01:00
|
|
|
#include <functional>
|
|
|
|
#include <iostream>
|
|
|
|
#ifdef CPP_UTILITIES_USE_SEND_FILE
|
2019-08-14 21:57:17 +02:00
|
|
|
#include <algorithm>
|
|
|
|
#include <cstring>
|
|
|
|
#endif
|
|
|
|
|
2019-06-10 21:56:46 +02:00
|
|
|
namespace CppUtilities {
|
2015-04-22 18:36:40 +02:00
|
|
|
|
|
|
|
/*!
|
2019-09-04 18:45:28 +02:00
|
|
|
* \class CopyHelper
|
2016-09-17 11:44:49 +02:00
|
|
|
* \brief The CopyHelper class helps to copy bytes from one stream to another.
|
2019-08-14 21:57:17 +02:00
|
|
|
* \tparam Specifies the chunk/buffer size.
|
2015-04-22 18:36:40 +02:00
|
|
|
*/
|
2017-05-01 03:13:11 +02:00
|
|
|
template <std::size_t bufferSize> class CPP_UTILITIES_EXPORT CopyHelper {
|
2015-04-22 18:36:40 +02:00
|
|
|
public:
|
|
|
|
CopyHelper();
|
2022-07-14 00:30:40 +02:00
|
|
|
void copy(std::istream &input, std::ostream &output, std::uint64_t count);
|
|
|
|
void callbackCopy(std::istream &input, std::ostream &output, std::uint64_t count, const std::function<bool(void)> &isAborted,
|
2017-05-01 03:13:11 +02:00
|
|
|
const std::function<void(double)> &callback);
|
2022-07-14 00:30:40 +02:00
|
|
|
void copy(NativeFileStream &input, NativeFileStream &output, std::uint64_t count);
|
|
|
|
void callbackCopy(NativeFileStream &input, NativeFileStream &output, std::uint64_t count, const std::function<bool(void)> &isAborted,
|
2019-08-13 00:32:59 +02:00
|
|
|
const std::function<void(double)> &callback);
|
2016-01-18 23:41:30 +01:00
|
|
|
char *buffer();
|
2017-05-01 03:13:11 +02:00
|
|
|
|
2015-04-22 18:36:40 +02:00
|
|
|
private:
|
|
|
|
char m_buffer[bufferSize];
|
|
|
|
};
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Constructs a new copy helper.
|
|
|
|
*/
|
2017-05-01 03:13:11 +02:00
|
|
|
template <std::size_t bufferSize> CopyHelper<bufferSize>::CopyHelper()
|
|
|
|
{
|
|
|
|
}
|
2015-04-22 18:36:40 +02:00
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Copies \a count bytes from \a input to \a output.
|
2017-02-03 00:54:44 +01:00
|
|
|
* \remarks Set an exception mask using std::ios::exceptions() to get a std::ios_base::failure exception
|
|
|
|
* when an IO error occurs.
|
2015-04-22 18:36:40 +02:00
|
|
|
*/
|
2022-07-14 00:30:40 +02:00
|
|
|
template <std::size_t bufferSize> void CopyHelper<bufferSize>::copy(std::istream &input, std::ostream &output, std::uint64_t count)
|
2015-04-22 18:36:40 +02:00
|
|
|
{
|
2017-05-01 03:13:11 +02:00
|
|
|
while (count > bufferSize) {
|
2015-04-22 18:36:40 +02:00
|
|
|
input.read(m_buffer, bufferSize);
|
|
|
|
output.write(m_buffer, bufferSize);
|
|
|
|
count -= bufferSize;
|
|
|
|
}
|
2021-03-19 23:09:01 +01:00
|
|
|
input.read(m_buffer, static_cast<std::streamsize>(count));
|
|
|
|
output.write(m_buffer, static_cast<std::streamsize>(count));
|
2015-04-22 18:36:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
2017-02-03 00:54:44 +01:00
|
|
|
* \brief Copies \a count bytes from \a input to \a output. The procedure might be aborted and
|
|
|
|
* progress updates will be reported.
|
2015-04-22 18:36:40 +02:00
|
|
|
*
|
2019-08-14 21:57:17 +02:00
|
|
|
* Before processing the next chunk \a isAborted is checked and the copying aborted if it returns true. Before processing the next chunk
|
|
|
|
* \a callback is invoked to report the current progress.
|
2015-04-22 18:36:40 +02:00
|
|
|
*
|
2017-02-03 00:54:44 +01:00
|
|
|
* \remarks Set an exception mask using std::ios::exceptions() to get a std::ios_base::failure exception
|
|
|
|
* when an IO error occurs.
|
2015-04-22 18:36:40 +02:00
|
|
|
*/
|
2017-05-01 03:13:11 +02:00
|
|
|
template <std::size_t bufferSize>
|
2022-07-14 00:30:40 +02:00
|
|
|
void CopyHelper<bufferSize>::callbackCopy(std::istream &input, std::ostream &output, std::uint64_t count, const std::function<bool(void)> &isAborted,
|
2017-05-01 03:13:11 +02:00
|
|
|
const std::function<void(double)> &callback)
|
2015-04-22 18:36:40 +02:00
|
|
|
{
|
2021-03-19 23:09:01 +01:00
|
|
|
const auto totalBytes = count;
|
2017-05-01 03:13:11 +02:00
|
|
|
while (count > bufferSize) {
|
2015-04-22 18:36:40 +02:00
|
|
|
input.read(m_buffer, bufferSize);
|
|
|
|
output.write(m_buffer, bufferSize);
|
|
|
|
count -= bufferSize;
|
2017-05-01 03:13:11 +02:00
|
|
|
if (isAborted()) {
|
2015-04-22 18:36:40 +02:00
|
|
|
return;
|
|
|
|
}
|
2021-03-19 23:09:01 +01:00
|
|
|
callback(static_cast<double>(totalBytes - count) / static_cast<double>(totalBytes));
|
2015-04-22 18:36:40 +02:00
|
|
|
}
|
2021-03-19 23:09:01 +01:00
|
|
|
input.read(m_buffer, static_cast<std::streamsize>(count));
|
|
|
|
output.write(m_buffer, static_cast<std::streamsize>(count));
|
2015-04-22 18:36:40 +02:00
|
|
|
callback(1.0);
|
|
|
|
}
|
|
|
|
|
2019-08-13 00:32:59 +02:00
|
|
|
/*!
|
|
|
|
* \brief Copies \a count bytes from \a input to \a output.
|
|
|
|
* \remarks
|
|
|
|
* - Set an exception mask using std::ios::exceptions() to get a std::ios_base::failure exception
|
|
|
|
* when an IO error occurs.
|
2019-08-14 21:57:17 +02:00
|
|
|
* - Possibly uses native APIs such as POSIX sendfile() to improve the speed.
|
2019-08-13 00:32:59 +02:00
|
|
|
*/
|
2022-07-14 00:30:40 +02:00
|
|
|
template <std::size_t bufferSize> void CopyHelper<bufferSize>::copy(NativeFileStream &input, NativeFileStream &output, std::uint64_t count)
|
2019-08-13 00:32:59 +02:00
|
|
|
{
|
2019-08-14 21:57:17 +02:00
|
|
|
#ifdef CPP_UTILITIES_USE_SEND_FILE
|
2023-04-23 21:21:32 +02:00
|
|
|
if (output.fileDescriptor() != -1 && input.fileDescriptor() != -1 && output.fileDescriptor() != input.fileDescriptor()) {
|
2019-08-14 21:57:17 +02:00
|
|
|
const auto inputTellg = output.tellg();
|
|
|
|
const auto inputTellp = output.tellp();
|
|
|
|
const auto outputTellg = output.tellg();
|
|
|
|
const auto outputTellp = output.tellp();
|
|
|
|
input.flush();
|
|
|
|
output.flush();
|
|
|
|
const auto totalBytes = static_cast<std::streamoff>(count);
|
|
|
|
while (count) {
|
|
|
|
const auto bytesCopied = ::sendfile64(output.fileDescriptor(), input.fileDescriptor(), nullptr, count);
|
|
|
|
if (bytesCopied < 0) {
|
2023-04-23 21:04:31 +02:00
|
|
|
if ((errno == EINVAL || errno == ENOSYS) && static_cast<std::uint64_t>(totalBytes) == count) {
|
2023-04-23 19:38:56 +02:00
|
|
|
// try again the unoptimized version, maybe the filesystem doesn't support sendfile
|
|
|
|
goto unoptimized_version;
|
|
|
|
}
|
2019-08-14 21:57:17 +02:00
|
|
|
throw std::ios_base::failure(argsToString("sendfile64() failed: ", std::strerror(errno)));
|
|
|
|
}
|
2023-04-23 19:38:56 +02:00
|
|
|
count -= static_cast<std::uint64_t>(bytesCopied);
|
2019-08-14 21:57:17 +02:00
|
|
|
}
|
|
|
|
input.sync();
|
|
|
|
output.sync();
|
|
|
|
output.seekg(outputTellg + totalBytes);
|
|
|
|
output.seekp(outputTellp + totalBytes);
|
|
|
|
input.seekg(inputTellg + totalBytes);
|
|
|
|
input.seekp(inputTellp + totalBytes);
|
|
|
|
return;
|
|
|
|
}
|
2023-04-23 19:38:56 +02:00
|
|
|
unoptimized_version:
|
2019-08-14 21:57:17 +02:00
|
|
|
#endif
|
2019-08-13 00:32:59 +02:00
|
|
|
copy(static_cast<std::istream &>(input), static_cast<std::ostream &>(output), count);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Copies \a count bytes from \a input to \a output. The procedure might be aborted and
|
|
|
|
* progress updates will be reported.
|
|
|
|
*
|
2019-08-14 21:57:17 +02:00
|
|
|
* Before processing the next chunk \a isAborted is checked and the copying aborted if it returns true. Before processing the next chunk
|
|
|
|
* \a callback is invoked to report the current progress.
|
2019-08-13 00:32:59 +02:00
|
|
|
*
|
|
|
|
* - Set an exception mask using std::ios::exceptions() to get a std::ios_base::failure exception
|
|
|
|
* when an IO error occurs.
|
2019-08-14 21:57:17 +02:00
|
|
|
* - Possibly uses native APIs such as POSIX sendfile() to improve the speed.
|
2019-08-13 00:32:59 +02:00
|
|
|
*/
|
|
|
|
template <std::size_t bufferSize>
|
2022-07-14 00:30:40 +02:00
|
|
|
void CopyHelper<bufferSize>::callbackCopy(NativeFileStream &input, NativeFileStream &output, std::uint64_t count,
|
2019-08-13 00:32:59 +02:00
|
|
|
const std::function<bool(void)> &isAborted, const std::function<void(double)> &callback)
|
|
|
|
{
|
2019-08-14 21:57:17 +02:00
|
|
|
#ifdef CPP_UTILITIES_USE_SEND_FILE
|
2023-04-23 21:21:32 +02:00
|
|
|
if (output.fileDescriptor() != -1 && input.fileDescriptor() != -1 && output.fileDescriptor() != input.fileDescriptor()) {
|
2019-08-14 21:57:17 +02:00
|
|
|
const auto inputTellg = output.tellg();
|
|
|
|
const auto inputTellp = output.tellp();
|
|
|
|
const auto outputTellg = output.tellg();
|
|
|
|
const auto outputTellp = output.tellp();
|
|
|
|
input.flush();
|
|
|
|
output.flush();
|
|
|
|
const auto totalBytes = static_cast<std::streamoff>(count);
|
|
|
|
while (count) {
|
2023-04-06 17:33:41 +02:00
|
|
|
const auto bytesToCopy = static_cast<std::size_t>(std::min(count, static_cast<std::uint64_t>(bufferSize)));
|
|
|
|
const auto bytesCopied = ::sendfile64(output.fileDescriptor(), input.fileDescriptor(), nullptr, bytesToCopy);
|
2019-08-14 21:57:17 +02:00
|
|
|
if (bytesCopied < 0) {
|
2023-04-23 21:04:31 +02:00
|
|
|
if ((errno == EINVAL || errno == ENOSYS) && static_cast<std::uint64_t>(totalBytes) == count) {
|
2023-04-23 19:38:56 +02:00
|
|
|
// try again the unoptimized version, maybe the filesystem doesn't support sendfile
|
|
|
|
goto unoptimized_version;
|
|
|
|
}
|
2019-08-14 21:57:17 +02:00
|
|
|
throw std::ios_base::failure(argsToString("sendfile64() failed: ", std::strerror(errno)));
|
|
|
|
}
|
|
|
|
count -= static_cast<std::uint64_t>(bytesCopied);
|
|
|
|
if (isAborted()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
callback(static_cast<double>(totalBytes - static_cast<std::streamoff>(count)) / static_cast<double>(totalBytes));
|
|
|
|
}
|
|
|
|
input.sync();
|
|
|
|
output.sync();
|
|
|
|
output.seekg(outputTellg + totalBytes);
|
|
|
|
output.seekp(outputTellp + totalBytes);
|
|
|
|
input.seekg(inputTellg + totalBytes);
|
|
|
|
input.seekp(inputTellp + totalBytes);
|
|
|
|
callback(1.0);
|
|
|
|
return;
|
|
|
|
}
|
2023-04-23 19:38:56 +02:00
|
|
|
unoptimized_version:
|
2019-08-14 21:57:17 +02:00
|
|
|
#endif
|
2019-08-13 00:32:59 +02:00
|
|
|
callbackCopy(static_cast<std::istream &>(input), static_cast<std::ostream &>(output), count, isAborted, callback);
|
|
|
|
}
|
|
|
|
|
2016-01-18 23:41:30 +01:00
|
|
|
/*!
|
|
|
|
* \brief Returns the internal buffer.
|
|
|
|
*/
|
2017-05-01 03:13:11 +02:00
|
|
|
template <std::size_t bufferSize> char *CopyHelper<bufferSize>::buffer()
|
2016-01-18 23:41:30 +01:00
|
|
|
{
|
|
|
|
return m_buffer;
|
|
|
|
}
|
2019-06-10 21:56:46 +02:00
|
|
|
} // namespace CppUtilities
|
2015-04-22 18:36:40 +02:00
|
|
|
|
|
|
|
#endif // IOUTILITIES_COPY_H
|