blob: 06a351a7f8e2ea12febdebac4556b07a2bb5d418 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include <sstream>
#include "garnet/lib/overnet/protocol/varint.h"
#include "garnet/lib/overnet/vocabulary/optional.h"
#include "garnet/lib/overnet/vocabulary/slice.h"
#include "garnet/lib/overnet/vocabulary/status.h"
namespace overnet {
// An Introduction records a request to create a new stream in Overnet.
// It's a key-value store, with a small fixed set of keys (up to 256 are
// allowed).
class Introduction {
public:
// Assigned key names
enum class Key : uint8_t {
ServiceName = 1,
};
Introduction() = default;
Introduction(std::initializer_list<std::pair<Key, Slice>> aggregate) {
for (auto &p : aggregate) {
(*this)[p.first] = p.second;
}
}
Optional<Slice> &operator[](Key key) {
return values_[static_cast<int>(key)];
}
const Optional<Slice> &operator[](Key key) const {
return values_[static_cast<int>(key)];
}
Slice Write(Border desired_border) const {
uint8_t key_len[256];
size_t value_len[256];
size_t frame_len = 0;
for (int i = 0; i < 256; i++) {
if (values_[i].has_value()) {
value_len[i] = values_[i]->length();
key_len[i] = varint::WireSizeFor(value_len[i]);
frame_len += 1;
frame_len += key_len[i];
frame_len += value_len[i];
} else {
key_len[i] = 0;
}
}
return Slice::WithInitializerAndBorders(
frame_len, desired_border, [&](uint8_t *p) {
for (int i = 0; i < 256; i++) {
if (key_len[i] != 0) {
*p++ = static_cast<uint8_t>(i);
p = varint::Write(value_len[i], key_len[i], p);
memcpy(p, values_[i]->begin(), value_len[i]);
p += value_len[i];
}
}
});
}
static StatusOr<Introduction> Parse(Slice slice) {
int max_seen_id = -1;
Introduction out;
const uint8_t *p = slice.begin();
const uint8_t *end = slice.end();
while (p != end) {
int id = *p++;
if (id <= max_seen_id) {
return StatusOr<Introduction>(
StatusCode::FAILED_PRECONDITION,
"Introduction chunks must be sent in ascending order");
}
uint64_t len;
if (!varint::Read(&p, end, &len)) {
return StatusOr<Introduction>(
StatusCode::FAILED_PRECONDITION,
"Failed to read value length from introduction slice");
}
const uint64_t remaining_bytes = uint64_t(end - p);
if (remaining_bytes < len) {
std::ostringstream out;
out << "Introduction value length runs past end of introduction slice: "
"had "
<< remaining_bytes
<< " left in stream, but encoded stream requested " << len
<< " bytes: " << slice;
return StatusOr<Introduction>(StatusCode::FAILED_PRECONDITION,
out.str());
}
max_seen_id = id;
out.values_[id] = slice.FromPointer(p).ToOffset(len);
p += len;
}
return out;
}
bool operator==(const Introduction &other) const {
for (int i = 0; i < 256; i++) {
if (values_[i] != other.values_[i]) {
return false;
}
}
return true;
}
private:
Optional<Slice> values_[256];
};
} // namespace overnet