fd/ops: Add readAllExact()
This is a minor shortcut for `readAllFixed` which requires the entire
input to be read exactly.
Change-Id: Ia1208099ef71d2045050eae1483f8ee0b13c7033
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/include-fd/stdplus/fd/ops.hpp b/include-fd/stdplus/fd/ops.hpp
index 53380d3..20395ab 100644
--- a/include-fd/stdplus/fd/ops.hpp
+++ b/include-fd/stdplus/fd/ops.hpp
@@ -36,6 +36,8 @@
std::span<const std::byte> data,
SendFlags flags);
+void verifyExact(size_t expected, size_t actual);
+
template <typename Fun, typename Container, typename... Args>
auto alignedOp(Fun&& fun, Fd& fd, Container&& c, Args&&... args)
{
@@ -126,6 +128,13 @@
}
template <typename T>
+inline auto readAllExact(Fd& fd, T&& t)
+{
+ auto s = raw::asSpan<std::byte>(t);
+ detail::verifyExact(s.size(), detail::readAllFixed(fd, 1, s).size());
+}
+
+template <typename T>
inline void recvExact(Fd& fd, T&& t, RecvFlags flags = {})
{
detail::recvExact(fd, raw::asSpan<std::byte>(t), flags);
diff --git a/src/fd/ops.cpp b/src/fd/ops.cpp
index d05e9b2..ca549f7 100644
--- a/src/fd/ops.cpp
+++ b/src/fd/ops.cpp
@@ -226,6 +226,14 @@
}
}
+void verifyExact(size_t expected, size_t actual)
+{
+ if (expected != actual)
+ {
+ throw exception::WouldBlock("verifyExact");
+ }
+}
+
} // namespace detail
} // namespace fd
} // namespace stdplus
diff --git a/test/fd/ops.cpp b/test/fd/ops.cpp
index 708a0ff..3fc511c 100644
--- a/test/fd/ops.cpp
+++ b/test/fd/ops.cpp
@@ -352,4 +352,61 @@
EXPECT_THROW(readAllFixed(fd, buf), std::system_error);
}
+TEST(ReadAllExact, FailEmpty)
+{
+ testing::StrictMock<FdMock> fd;
+ {
+ testing::InSequence seq;
+ EXPECT_CALL(fd, read(_))
+ .WillRepeatedly(testing::Throw(exception::Eof("test")));
+ }
+ std::array<char, 16> buf;
+ EXPECT_THROW(readAllExact(fd, buf), std::system_error);
+}
+
+TEST(ReadAllExact, FailPartial)
+{
+ testing::StrictMock<FdMock> fd;
+ {
+ testing::InSequence seq;
+ EXPECT_CALL(fd, read(SizeIs(Ge(4)))).WillOnce(readSv("alph"));
+ EXPECT_CALL(fd, read(SizeIs(Ge(2)))).WillOnce(readSv("a "));
+ EXPECT_CALL(fd, read(SizeIs(Ge(3)))).WillOnce(readSv("one"));
+ EXPECT_CALL(fd, read(_))
+ .WillRepeatedly(testing::Throw(exception::Eof("test")));
+ }
+ std::array<char, 16> buf;
+ EXPECT_THROW(readAllExact(fd, buf), std::system_error);
+}
+
+TEST(ReadAllExact, Success)
+{
+ testing::StrictMock<FdMock> fd;
+ {
+ testing::InSequence seq;
+ EXPECT_CALL(fd, read(SizeIs(Ge(4)))).WillOnce(readSv("alph"));
+ EXPECT_CALL(fd, read(SizeIs(Ge(2)))).WillOnce(readSv("a "));
+ EXPECT_CALL(fd, read(SizeIs(Ge(10)))).WillOnce(readSv("one tenner"));
+ EXPECT_CALL(fd, read(_))
+ .WillRepeatedly(testing::Throw(exception::Eof("test")));
+ }
+ std::array<char, 16> buf;
+ readAllExact(fd, buf);
+ EXPECT_EQ(std::string_view(buf.begin(), buf.end()), "alpha one tenner");
+}
+
+TEST(ReadAllExact, TooMuch)
+{
+ testing::StrictMock<FdMock> fd;
+ {
+ testing::InSequence seq;
+ EXPECT_CALL(fd, read(SizeIs(Ge(4)))).WillOnce(readSv("alph"));
+ EXPECT_CALL(fd, read(SizeIs(Ge(2)))).WillOnce(readSv("a "));
+ EXPECT_CALL(fd, read(SizeIs(Ge(10)))).WillOnce(readSv("one tenner"));
+ EXPECT_CALL(fd, read(SizeIs(Ge(1)))).WillOnce(readSv("r"));
+ }
+ std::array<char, 16> buf;
+ EXPECT_THROW(readAllExact(fd, buf), std::system_error);
+}
+
} // namespace stdplus::fd