Add support for string buffers to ScopedPipe
Change-Id: Ia116eaf6c9905a10c8b3d0a79a08fc010821c7c3
Signed-off-by: James Kuszmaul <james.kuszmaul@bluerivertech.com>
diff --git a/aos/util/BUILD b/aos/util/BUILD
index a57ec79..0314069 100644
--- a/aos/util/BUILD
+++ b/aos/util/BUILD
@@ -223,16 +223,6 @@
],
)
-cc_library(
- name = "scoped_pipe",
- srcs = ["scoped_pipe.cc"],
- hdrs = ["scoped_pipe.h"],
- target_compatible_with = ["@platforms//os:linux"],
- deps = [
- "@com_github_google_glog//:glog",
- ],
-)
-
cc_test(
name = "file_test",
size = "small",
@@ -246,6 +236,27 @@
],
)
+cc_library(
+ name = "scoped_pipe",
+ srcs = ["scoped_pipe.cc"],
+ hdrs = ["scoped_pipe.h"],
+ target_compatible_with = ["@platforms//os:linux"],
+ deps = [
+ "@com_github_google_glog//:glog",
+ "@com_google_absl//absl/types:span",
+ ],
+)
+
+cc_test(
+ name = "scoped_pipe_test",
+ srcs = ["scoped_pipe_test.cc"],
+ target_compatible_with = ["@platforms//os:linux"],
+ deps = [
+ ":scoped_pipe",
+ "//aos/testing:googletest",
+ ],
+)
+
py_library(
name = "python_init",
srcs = ["__init__.py"],
diff --git a/aos/util/scoped_pipe.cc b/aos/util/scoped_pipe.cc
index 8cf2957..36bc1b3 100644
--- a/aos/util/scoped_pipe.cc
+++ b/aos/util/scoped_pipe.cc
@@ -35,6 +35,33 @@
return {ScopedReadPipe(fds[0]), ScopedWritePipe(fds[1])};
}
+size_t ScopedPipe::ScopedReadPipe::Read(std::string *buffer) {
+ CHECK_NOTNULL(buffer);
+ constexpr ssize_t kBufferSize = 1024;
+ const size_t original_size = buffer->size();
+ size_t read_bytes = 0;
+ while (true) {
+ buffer->resize(buffer->size() + kBufferSize);
+ const ssize_t result =
+ read(fd(), buffer->data() + buffer->size() - kBufferSize, kBufferSize);
+ if (result == -1) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ buffer->resize(original_size);
+ return 0;
+ }
+ PLOG(FATAL) << "Error on reading pipe.";
+ } else if (result < kBufferSize) {
+ read_bytes += result;
+ buffer->resize(original_size + read_bytes);
+ break;
+ } else {
+ CHECK_EQ(result, kBufferSize);
+ read_bytes += result;
+ }
+ }
+ return read_bytes;
+}
+
std::optional<uint32_t> ScopedPipe::ScopedReadPipe::Read() {
uint32_t buf;
ssize_t result = read(fd(), &buf, sizeof(buf));
@@ -48,7 +75,13 @@
void ScopedPipe::ScopedWritePipe::Write(uint32_t data) {
ssize_t result = write(fd(), &data, sizeof(data));
PCHECK(result != -1);
- CHECK(result == sizeof(data));
+ CHECK_EQ(static_cast<size_t>(result), sizeof(data));
+}
+
+void ScopedPipe::ScopedWritePipe::Write(absl::Span<const uint8_t> data) {
+ ssize_t result = write(fd(), data.data(), data.size());
+ PCHECK(result != -1);
+ CHECK_EQ(static_cast<size_t>(result), data.size());
}
} // namespace aos::util
diff --git a/aos/util/scoped_pipe.h b/aos/util/scoped_pipe.h
index 5f86510..6716fb5 100644
--- a/aos/util/scoped_pipe.h
+++ b/aos/util/scoped_pipe.h
@@ -6,6 +6,8 @@
#include <optional>
#include <tuple>
+#include "absl/types/span.h"
+
namespace aos::util {
// RAII Pipe for sending individual ints between reader and writer.
@@ -34,6 +36,10 @@
class ScopedPipe::ScopedReadPipe : public ScopedPipe {
public:
std::optional<uint32_t> Read();
+ // Reads as many bytes as possible out of the pipe, appending them to the end
+ // of the provided buffer. Returns the number of bytes read. Dies on errors
+ // other than EAGAIN or EWOULDBLOCK.
+ size_t Read(std::string *buffer);
private:
using ScopedPipe::ScopedPipe;
@@ -44,6 +50,8 @@
class ScopedPipe::ScopedWritePipe : public ScopedPipe {
public:
void Write(uint32_t data);
+ // Writes the entirety of the specified buffer to the pipe. Dies on failure.
+ void Write(absl::Span<const uint8_t> data);
private:
using ScopedPipe::ScopedPipe;
diff --git a/aos/util/scoped_pipe_test.cc b/aos/util/scoped_pipe_test.cc
new file mode 100644
index 0000000..183688e
--- /dev/null
+++ b/aos/util/scoped_pipe_test.cc
@@ -0,0 +1,49 @@
+#include "aos/util/scoped_pipe.h"
+
+#include <array>
+#include <string>
+
+#include "gtest/gtest.h"
+
+namespace aos {
+namespace util {
+namespace testing {
+
+// Tests using uint32_t read/write methods on the ScopedPipe objects.
+TEST(ScopedPipeTest, IntegerPipe) {
+ std::tuple<ScopedPipe::ScopedReadPipe, ScopedPipe::ScopedWritePipe>
+ pipe = ScopedPipe::MakePipe();
+ ASSERT_FALSE(std::get<0>(pipe).Read().has_value())
+ << "Shouldn't get anything on empty read.";
+ std::get<1>(pipe).Write(971);
+ ASSERT_EQ(971, std::get<0>(pipe).Read().value());
+}
+
+// Tests using string read/write methods on the ScopedPipe objects.
+TEST(ScopedPipeTest, StringPipe) {
+ std::tuple<ScopedPipe::ScopedReadPipe, ScopedPipe::ScopedWritePipe>
+ pipe = ScopedPipe::MakePipe();
+ std::string buffer;
+ ASSERT_EQ(0u, std::get<0>(pipe).Read(&buffer))
+ << "Shouldn't get anything on empty read.";
+ ASSERT_TRUE(buffer.empty());
+
+ const char *const kAbc = "abcdef";
+ std::get<1>(pipe).Write(
+ absl::Span<const uint8_t>(reinterpret_cast<const uint8_t *>(kAbc), 6));
+ ASSERT_EQ(6u, std::get<0>(pipe).Read(&buffer));
+ ASSERT_EQ("abcdef", buffer);
+
+ std::array<uint8_t, 10000> large_buffer;
+ large_buffer.fill(99);
+ std::get<1>(pipe).Write(
+ absl::Span<const uint8_t>(large_buffer.data(), large_buffer.size()));
+ ASSERT_EQ(large_buffer.size(), std::get<0>(pipe).Read(&buffer));
+ for (size_t ii = 0; ii < large_buffer.size(); ++ii) {
+ ASSERT_EQ(large_buffer[ii], buffer[ii + 6]);
+ }
+}
+
+} // namespace testing
+} // namespace util
+} // namespace aos