Skip to content

Commit

Permalink
ghOSt: Add serialization for absl::StatusOr<std::string>
Browse files Browse the repository at this point in the history
This extends the TrivialStatus library to include a variant to work for
absl::StatusOr<std::string>. It currently supports strings up to 15000
characters in length.

PiperOrigin-RevId: 501929797
  • Loading branch information
joshdon authored and dohyunkim-dev committed Jan 20, 2023
1 parent e7127ec commit e25f625
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 4 deletions.
1 change: 1 addition & 0 deletions BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ cc_library(
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings:str_format",
],
)

Expand Down
14 changes: 14 additions & 0 deletions lib/agent.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,10 @@ struct AgentRpcBuffer {
return Serialize<TrivialStatusOr<T>>(TrivialStatusOr<T>(status_or));
}

absl::Status SerializeStatusOrString(const absl::StatusOr<std::string>& s) {
return Serialize<TrivialStatusOrString>(TrivialStatusOrString(s));
}

// See comment above for `Serialize<T>(const T& t, size_t size)`. This
// function does the same thing but assumes that the size of `T` is
// `sizeof(T)`. In some cases, such as array pointers, this is not true. In
Expand Down Expand Up @@ -338,6 +342,16 @@ struct AgentRpcBuffer {
return deserialize_status.value().ToStatusOr();
}

absl::StatusOr<absl::StatusOr<std::string>> DeserializeStatusOrString()
const {
absl::StatusOr<TrivialStatusOrString> deserialize_status =
Deserialize<TrivialStatusOrString>();
if (!deserialize_status.ok()) {
return deserialize_status.status();
}
return deserialize_status.value().ToStatusOr();
}

// This is a region where arbitrary bytes of data can be written (ie. when the
// RPC mechanism needs to return more than just a response code). Intended to
// be used with the Serialize/Deserialize methods. We use a byte array instead
Expand Down
31 changes: 28 additions & 3 deletions lib/trivial_status.cc
Original file line number Diff line number Diff line change
@@ -1,23 +1,48 @@
#include "lib/trivial_status.h"
#include "absl/strings/str_format.h"

namespace ghost {

namespace {

template <size_t ArraySize>
void CopyString(absl::string_view s, std::array<char, ArraySize>& dest) {
void CopyString(std::array<char, ArraySize>& dest, absl::string_view s) {
static_assert(ArraySize > 0);
const size_t chars_to_copy = std::min(ArraySize - 1, s.size());
if (chars_to_copy < s.size()) {
absl::FPrintF(
stderr,
"Source string too large to fix in TrivialStatus: %zu, vs max_size %zu",
s.size(), chars_to_copy);
}
std::copy_n(s.begin(), chars_to_copy, dest.begin());
dest[std::min(chars_to_copy, ArraySize - 1)] = '\0';
dest[chars_to_copy] = '\0';
}

} // namespace

TrivialStatus::TrivialStatus(const absl::Status& s) {
code_ = s.code();

CopyString<1000>(s.message(), error_message_);
CopyString<kMaxErrorMessageSize>(error_message_, s.message());
}

TrivialStatusOrString::TrivialStatusOrString(
const absl::StatusOr<std::string>& s)
: status_(TrivialStatus(s.status())) {
if (s.ok()) {
string_length_ = s.value().size();
CopyString<kMaxStringSize>(str_, s.value());
}
}

absl::StatusOr<std::string> TrivialStatusOrString::ToStatusOr() const
{
absl::Status s = status_.ToStatus();
if (s.ok()) {
return std::string(str_.data(), string_length_);
}
return s;
}

} // namespace ghost
32 changes: 31 additions & 1 deletion lib/trivial_status.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ class TrivialStatus {
bool ok() const { return code_ == absl::StatusCode::kOk; }

private:
static constexpr size_t kMaxErrorMessageSize = 1000;

absl::StatusCode code_;

// Sized large enough to handle most error messages. Must fit in
// AgentRpcBuffer BufferBytes.
std::array<char, 1000> error_message_;
std::array<char, kMaxErrorMessageSize> error_message_;
};

// This is a trivially copyable version of absl::StatusOr. This is useful
Expand Down Expand Up @@ -71,6 +73,34 @@ class TrivialStatusOr {
T value_;
};

// This is a trivially copyable version of absl::StatusOr<std::string>. This is
// useful because it can be serialized across the shared memory AgentRpcBuffer.
class TrivialStatusOrString {
public:
explicit TrivialStatusOrString()
: status_(TrivialStatus(absl::OkStatus())) {}

explicit TrivialStatusOrString(const absl::StatusOr<std::string>& s);

// Returns the absl::StatusOr<std::string> version of this object.
absl::StatusOr<std::string> ToStatusOr() const;

bool ok() const { return status_.ok(); }

private:
static constexpr size_t kMaxStringSize = 15000;

TrivialStatus status_;

// If the status is OK, this stores the contained string.
// Must fit in AgentRpcBuffer BufferBytes.
std::array<char, kMaxStringSize> str_;

// Not all strings will use null terminators, so we must track the original
// size of the std::string.
size_t string_length_ = 0;
};

} // namespace ghost

#endif // GHOST_LIB_TRIVIAL_STATUS_H_
16 changes: 16 additions & 0 deletions tests/agent_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,22 @@ TEST(AgentTest, RpcStatusOrSerialization) {
EXPECT_EQ(status_or.value().value(), 42);
}

// Test serialization of absl::StatusOr<std::string>.
TEST(AgentTest, RpcStatusOrStringSerialization) {
std::vector<std::string> strings = {"Hello World", "World", "", "Hello"};
AgentRpcResponse response;
for (const std::string& s : strings) {
absl::StatusOr<std::string> status_or = s;
ASSERT_EQ(response.buffer.SerializeStatusOrString(status_or),
absl::OkStatus());
absl::StatusOr<absl::StatusOr<std::string>> deserialized =
response.buffer.DeserializeStatusOrString();
ASSERT_EQ(deserialized.status(), absl::OkStatus());
ASSERT_EQ(deserialized.value().status(), absl::OkStatus());
EXPECT_EQ(deserialized.value().value(), s);
}
}

TEST(AgentTest, ExitHandler) {
bool ran = false;

Expand Down

0 comments on commit e25f625

Please sign in to comment.