fd/ops: Add sendto / recvfrom helpers

Change-Id: I19311c43b2b310831d9a57c699e4461552911393
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 c2def78..7dc47ef 100644
--- a/include-fd/stdplus/fd/ops.hpp
+++ b/include-fd/stdplus/fd/ops.hpp
@@ -18,6 +18,8 @@
 void recvExact(Fd& fd, std::span<std::byte> data, RecvFlags flags);
 void writeExact(Fd& fd, std::span<const std::byte> data);
 void sendExact(Fd& fd, std::span<const std::byte> data, SendFlags flags);
+void sendtoExact(Fd& fd, std::span<const std::byte> data, SendFlags flags,
+                 std::span<const std::byte> addr);
 
 std::span<std::byte> readAligned(Fd& fd, size_t align,
                                  std::span<std::byte> buf);
@@ -55,6 +57,18 @@
 }
 
 template <typename Container>
+auto recvfrom(Fd& fd, Container&& c, SockAddrBuf& addr, RecvFlags flags = {})
+{
+    using Data = raw::detail::dataType<Container>;
+    auto ret = fd.recvfrom(
+        raw::asSpan<std::byte>(c), flags,
+        std::span(reinterpret_cast<std::byte*>(&addr), addr.maxLen));
+    addr.len = std::get<1>(ret).size();
+    return std::span<Data>(std::begin(c),
+                           std::get<0>(ret).size() / sizeof(Data));
+}
+
+template <typename Container>
 inline auto write(Fd& fd, Container&& c)
 {
     return detail::alignedOp(detail::writeAligned, fd,
@@ -68,6 +82,17 @@
                              std::forward<Container>(c), flags);
 }
 
+template <typename Container>
+auto sendto(Fd& fd, Container&& c, const SockAddrBuf& addr,
+            SendFlags flags = {})
+{
+    using Data = raw::detail::dataType<Container>;
+    auto ret = fd.sendto(
+        raw::asSpan<std::byte>(c), flags,
+        std::span(reinterpret_cast<const std::byte*>(&addr), addr.len));
+    return std::span<Data>(std::begin(c), ret.size() / sizeof(Data));
+}
+
 template <typename T>
 inline void readExact(Fd& fd, T&& t)
 {
@@ -92,6 +117,15 @@
     detail::sendExact(fd, raw::asSpan<std::byte>(t), flags);
 }
 
+template <typename Container>
+inline void sendtoExact(Fd& fd, Container&& c, const SockAddrBuf& addr,
+                        SendFlags flags = {})
+{
+    detail::sendtoExact(
+        fd, raw::asSpan<std::byte>(c), flags,
+        std::span(reinterpret_cast<const std::byte*>(&addr), addr.len));
+}
+
 inline size_t lseek(Fd& fd, off_t offset, Whence whence)
 {
     return fd.lseek(offset, whence);
diff --git a/src/fd/ops.cpp b/src/fd/ops.cpp
index d18e855..c299ed0 100644
--- a/src/fd/ops.cpp
+++ b/src/fd/ops.cpp
@@ -61,6 +61,20 @@
     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,