blob: ca549f793abdf34f5fb1ac81ecb3dd537dd3f48e [file] [log] [blame]
#include <stdplus/exception.hpp>
#include <stdplus/fd/ops.hpp>
#include <format>
#include <utility>
namespace stdplus
{
namespace fd
{
namespace detail
{
template <typename Fun, typename Byte, typename... Args>
static void opExact(const char* name, Fun&& fun, Fd& fd, std::span<Byte> data,
Args&&... args)
{
std::size_t total = 0;
try
{
while (total < data.size())
{
auto r = (fd.*fun)(data.subspan(total),
std::forward<Args>(args)...);
if (r.size() == 0)
{
throw exception::WouldBlock(
std::format("{} missing", name, data.size()));
}
total += r.size();
}
}
catch (const std::system_error&)
{
if (total != 0)
{
throw exception::Incomplete(
std::format("{} is {}B/{}B", name, total, data.size()));
}
throw;
}
}
void readExact(Fd& fd, std::span<std::byte> data)
{
opExact("readExact", &Fd::read, fd, data);
}
void recvExact(Fd& fd, std::span<std::byte> data, RecvFlags flags)
{
opExact("recvExact", &Fd::recv, fd, data, flags);
}
void writeExact(Fd& fd, std::span<const std::byte> data)
{
opExact("writeExact", &Fd::write, fd, data);
}
void sendExact(Fd& fd, std::span<const std::byte> data, SendFlags flags)
{
opExact("sendExact", &Fd::send, fd, data, flags);
}
void sendtoExact(Fd& fd, std::span<const std::byte> data, SendFlags flags,
std::span<const std::byte> addr)
{
auto r = fd.sendto(data, flags, addr);
if (r.size() == 0)
{
throw exception::WouldBlock("sendto");
}
if (r.size() < data.size())
{
throw exception::Incomplete("sendto");
}
}
template <typename Fun, typename Byte, typename... Args>
static std::span<Byte> opAligned(const char* name, Fun&& fun, Fd& fd,
size_t align, std::span<Byte> data,
Args&&... args)
{
std::size_t total = 0;
try
{
do
{
auto r = (fd.*fun)(data.subspan(total),
std::forward<Args>(args)...);
if (total != 0 && r.size() == 0)
{
throw exception::Incomplete(
std::format("{} is {}B/{}B", name, total % align, align));
}
total += r.size();
} while (total % align != 0);
}
catch (const std::system_error&)
{
if (total % align)
{
throw exception::Incomplete(
std::format("{} is {}B/{}B", name, total % align, align));
}
throw;
}
return std::span<Byte>(data.data(), total);
}
std::span<std::byte> readAligned(Fd& fd, size_t align, std::span<std::byte> buf)
{
return opAligned("readAligned", &Fd::read, fd, align, buf);
}
std::span<std::byte> recvAligned(Fd& fd, size_t align, std::span<std::byte> buf,
RecvFlags flags)
{
return opAligned("recvAligned", &Fd::recv, fd, align, buf, flags);
}
std::span<const std::byte> writeAligned(Fd& fd, size_t align,
std::span<const std::byte> data)
{
return opAligned("writeAligned", &Fd::write, fd, align, data);
}
std::span<const std::byte> sendAligned(Fd& fd, size_t align,
std::span<const std::byte> data,
SendFlags flags)
{
return opAligned("sendAligned", &Fd::send, fd, align, data, flags);
}
constexpr std::size_t maxStrideB = 65536;
void readAll(Fd& fd, function_view<std::span<std::byte>(size_t req)> resize)
{
std::size_t strideB = 256;
std::size_t totalB = 0;
auto doRead = [&]() {
auto buf = resize(totalB + strideB);
auto r = fd.read(buf.subspan(totalB));
if (r.size() == 0)
{
throw exception::WouldBlock("readAll");
}
totalB += r.size();
};
auto validateSize = [&]() {
auto buf = resize(totalB);
if (totalB != buf.size())
{
throw exception::Incomplete(
std::format("readAll extra {}B", buf.size() - totalB));
}
};
try
{
while (strideB < maxStrideB)
{
doRead();
if (totalB >= strideB)
{
strideB = strideB << 1;
}
}
while (true)
{
doRead();
}
}
catch (const exception::Eof&)
{
validateSize();
return;
}
catch (const std::system_error&)
{
validateSize();
throw;
}
}
std::span<std::byte> readAllFixed(Fd& fd, size_t align,
std::span<std::byte> buf)
{
std::size_t totalB = 0;
auto validateSize = [&]() {
if (totalB % align != 0)
{
throw exception::Incomplete(std::format(
"readAllFixed partial {}B/{}B", totalB % align, align));
}
};
try
{
while (totalB < buf.size())
{
auto r = fd.read(
buf.subspan(totalB, std::min(maxStrideB, buf.size() - totalB)));
if (r.size() == 0)
{
throw exception::WouldBlock("readAllFixed");
}
totalB += r.size();
}
std::byte b;
auto r = fd.read(std::span(&b, 1));
if (r.size() == 0)
{
throw exception::WouldBlock("readAllFixed");
}
throw std::system_error(
std::make_error_code(std::errc::value_too_large),
"readAllFixed overflow");
}
catch (const exception::Eof& e)
{
validateSize();
return buf.subspan(0, totalB);
}
catch (const std::system_error& e)
{
validateSize();
throw;
}
}
void verifyExact(size_t expected, size_t actual)
{
if (expected != actual)
{
throw exception::WouldBlock("verifyExact");
}
}
} // namespace detail
} // namespace fd
} // namespace stdplus