blob: 16f284743b9fa9b90a28b609f68dce893f6b1977 [file] [log] [blame]
// Copyright 2022 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#pragma once
#include "pw_assert/assert.h"
#include "pw_bytes/span.h"
#include "pw_protobuf/encoder.h"
#include "pw_protobuf/internal/codegen.h"
#include "pw_protobuf/stream_decoder.h"
#include "pw_rpc/internal/client_call.h"
#include "pw_rpc/internal/server_call.h"
#include "pw_span/span.h"
#include "pw_status/status.h"
#include "pw_status/status_with_size.h"
namespace pw::rpc::internal {
using PwpbMessageDescriptor =
const span<const protobuf::internal::MessageField>*;
// Serializer/deserializer for a pw_protobuf message.
class PwpbSerde {
public:
explicit constexpr PwpbSerde(PwpbMessageDescriptor table) : table_(table) {}
PwpbSerde(const PwpbSerde&) = default;
PwpbSerde& operator=(const PwpbSerde&) = default;
// Encodes a pw_protobuf struct to the serialized wire format.
template <typename Message>
StatusWithSize Encode(const Message& message, ByteSpan buffer) const {
return Encoder(buffer).Write(as_bytes(span(&message, 1)), table_);
}
// Decodes a serialized protobuf into a pw_protobuf message struct.
template <typename Message>
Status Decode(ConstByteSpan buffer, Message& message) const {
return Decoder(buffer).Read(as_writable_bytes(span(&message, 1)), table_);
}
private:
class Encoder : public protobuf::MemoryEncoder {
public:
constexpr Encoder(ByteSpan buffer) : protobuf::MemoryEncoder(buffer) {}
StatusWithSize Write(ConstByteSpan message, PwpbMessageDescriptor table) {
const auto status = protobuf::MemoryEncoder::Write(message, *table);
return StatusWithSize(status, size());
}
};
class Decoder : public protobuf::StreamDecoder {
public:
constexpr Decoder(ConstByteSpan buffer)
: protobuf::StreamDecoder(reader_), reader_(buffer) {}
Status Read(ByteSpan message, PwpbMessageDescriptor table) {
return protobuf::StreamDecoder::Read(message, *table);
}
private:
stream::MemoryReader reader_;
};
PwpbMessageDescriptor table_;
};
// Serializer/deserializer for pw_protobuf request and response message structs
// within an RPC method.
class PwpbMethodSerde {
public:
constexpr PwpbMethodSerde(PwpbMessageDescriptor request_table,
PwpbMessageDescriptor response_table)
: request_serde_(request_table), response_serde_(response_table) {}
PwpbMethodSerde(const PwpbMethodSerde&) = delete;
PwpbMethodSerde& operator=(const PwpbMethodSerde&) = delete;
// Encodes the pw_protobuf request struct to the serialized wire format.
template <typename Request>
StatusWithSize EncodeRequest(const Request& request, ByteSpan buffer) const {
return request_serde_.Encode(request, buffer);
}
// Encodes the pw_protobuf response struct to the serialized wire format.
template <typename Response>
StatusWithSize EncodeResponse(const Response& response,
ByteSpan buffer) const {
return response_serde_.Encode(response, buffer);
}
// Decodes a serialized protobuf into the pw_protobuf request struct.
template <typename Request>
Status DecodeRequest(ConstByteSpan buffer, Request& request) const {
return request_serde_.Decode(buffer, request);
}
// Decodes a serialized protobuf into the pw_protobuf response struct.
template <typename Response>
Status DecodeResponse(ConstByteSpan buffer, Response& response) const {
return response_serde_.Decode(buffer, response);
}
const PwpbSerde& request() const { return request_serde_; }
const PwpbSerde& response() const { return response_serde_; }
private:
PwpbSerde request_serde_;
PwpbSerde response_serde_;
};
// Defines per-message struct type instance of the serializer/deserializer.
template <PwpbMessageDescriptor kRequest, PwpbMessageDescriptor kResponse>
constexpr PwpbMethodSerde kPwpbMethodSerde(kRequest, kResponse);
// Encodes a message struct into a payload buffer.
template <typename Payload>
Result<ByteSpan> PwpbEncodeToPayloadBuffer(const Payload& payload,
PwpbSerde serde)
PW_EXCLUSIVE_LOCKS_REQUIRED(rpc_lock()) {
ByteSpan buffer = GetPayloadBuffer();
const StatusWithSize sws = serde.Encode(payload, buffer);
if (!sws.ok()) {
return sws.status();
}
return buffer.first(sws.size());
}
// [Client] Encodes and sends the initial request message for the call.
// active() must be true.
template <typename Request>
void PwpbSendInitialRequest(ClientCall& call,
PwpbSerde serde,
const Request& request)
PW_UNLOCK_FUNCTION(rpc_lock()) {
PW_ASSERT(call.active_locked());
Result<ByteSpan> buffer = PwpbEncodeToPayloadBuffer(request, serde);
if (buffer.ok()) {
call.SendInitialClientRequest(*buffer);
} else {
call.HandleError(buffer.status());
}
}
// [Client/Server] Encodes and sends a client or server stream message.
// active() must be true.
template <typename Payload>
Status PwpbSendStream(Call& call, const Payload& payload, PwpbSerde serde)
PW_EXCLUSIVE_LOCKS_REQUIRED(rpc_lock()) {
Result<ByteSpan> buffer = PwpbEncodeToPayloadBuffer(payload, serde);
PW_TRY(buffer);
return call.WriteLocked(*buffer);
}
// [Server] Encodes and sends the final response message from an untyped
// ConstByteSpan.
// active() must be true.
template <typename Response>
Status PwpbSendFinalResponse(internal::ServerCall& call,
const Response& response,
Status status,
PwpbSerde serde)
PW_EXCLUSIVE_LOCKS_REQUIRED(rpc_lock()) {
PW_ASSERT(call.active_locked());
Result<ByteSpan> buffer = PwpbEncodeToPayloadBuffer(response, serde);
if (!buffer.ok()) {
return call.CloseAndSendServerErrorLocked(Status::Internal());
}
return call.CloseAndSendResponseLocked(*buffer, status);
}
} // namespace pw::rpc::internal