blob: 5d15bc51c0b6e67e32cf9ead905708880bf12c2a [file] [log] [blame]
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001/*
2 * Copyright 2014 Google Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef FLATBUFFERS_GRPC_H_
18#define FLATBUFFERS_GRPC_H_
19
20// Helper functionality to glue FlatBuffers and GRPC.
21
22#include "flatbuffers/flatbuffers.h"
Austin Schuhe89fa2d2019-08-14 20:24:23 -070023#include "grpc/byte_buffer_reader.h"
James Kuszmaul8e62b022022-03-22 09:33:25 -070024#include "grpcpp/support/byte_buffer.h"
25#include "grpcpp/support/slice.h"
Austin Schuhe89fa2d2019-08-14 20:24:23 -070026
27namespace flatbuffers {
28namespace grpc {
29
30// Message is a typed wrapper around a buffer that manages the underlying
31// `grpc_slice` and also provides flatbuffers-specific helpers such as `Verify`
32// and `GetRoot`. Since it is backed by a `grpc_slice`, the underlying buffer
33// is refcounted and ownership is be managed automatically.
34template<class T> class Message {
35 public:
James Kuszmaul8e62b022022-03-22 09:33:25 -070036 Message() {}
Austin Schuhe89fa2d2019-08-14 20:24:23 -070037
James Kuszmaul8e62b022022-03-22 09:33:25 -070038 Message(::grpc::Slice slice) : slice_(slice) {}
Austin Schuhe89fa2d2019-08-14 20:24:23 -070039
40 Message &operator=(const Message &other) = delete;
41
James Kuszmaul8e62b022022-03-22 09:33:25 -070042 Message(Message &&other) = default;
Austin Schuhe89fa2d2019-08-14 20:24:23 -070043
44 Message(const Message &other) = delete;
45
James Kuszmaul8e62b022022-03-22 09:33:25 -070046 Message &operator=(Message &&other) = default;
Austin Schuhe89fa2d2019-08-14 20:24:23 -070047
James Kuszmaul8e62b022022-03-22 09:33:25 -070048 const uint8_t *mutable_data() const { return slice_.begin(); }
Austin Schuhe89fa2d2019-08-14 20:24:23 -070049
James Kuszmaul8e62b022022-03-22 09:33:25 -070050 const uint8_t *data() const { return slice_.begin(); }
Austin Schuhe89fa2d2019-08-14 20:24:23 -070051
James Kuszmaul8e62b022022-03-22 09:33:25 -070052 size_t size() const { return slice_.size(); }
Austin Schuhe89fa2d2019-08-14 20:24:23 -070053
54 bool Verify() const {
55 Verifier verifier(data(), size());
56 return verifier.VerifyBuffer<T>(nullptr);
57 }
58
59 T *GetMutableRoot() { return flatbuffers::GetMutableRoot<T>(mutable_data()); }
60
61 const T *GetRoot() const { return flatbuffers::GetRoot<T>(data()); }
62
63 // This is only intended for serializer use, or if you know what you're doing
James Kuszmaul8e62b022022-03-22 09:33:25 -070064 const ::grpc::Slice &BorrowSlice() const { return slice_; }
Austin Schuhe89fa2d2019-08-14 20:24:23 -070065
66 private:
James Kuszmaul8e62b022022-03-22 09:33:25 -070067 ::grpc::Slice slice_;
Austin Schuhe89fa2d2019-08-14 20:24:23 -070068};
69
70class MessageBuilder;
71
72// SliceAllocator is a gRPC-specific allocator that uses the `grpc_slice`
73// refcounted slices to manage memory ownership. This makes it easy and
74// efficient to transfer buffers to gRPC.
75class SliceAllocator : public Allocator {
76 public:
James Kuszmaul8e62b022022-03-22 09:33:25 -070077 SliceAllocator() {}
Austin Schuhe89fa2d2019-08-14 20:24:23 -070078
79 SliceAllocator(const SliceAllocator &other) = delete;
80 SliceAllocator &operator=(const SliceAllocator &other) = delete;
81
James Kuszmaul8e62b022022-03-22 09:33:25 -070082 SliceAllocator(SliceAllocator &&other) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -070083 // default-construct and swap idiom
84 swap(other);
85 }
86
87 SliceAllocator &operator=(SliceAllocator &&other) {
88 // move-construct and swap idiom
89 SliceAllocator temp(std::move(other));
90 swap(temp);
91 return *this;
92 }
93
94 void swap(SliceAllocator &other) {
95 using std::swap;
96 swap(slice_, other.slice_);
97 }
98
James Kuszmaul8e62b022022-03-22 09:33:25 -070099 virtual ~SliceAllocator() {}
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700100
101 virtual uint8_t *allocate(size_t size) override {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700102 FLATBUFFERS_ASSERT(slice_.size() == 0);
103 slice_ = ::grpc::Slice(size);
104 return const_cast<uint8_t *>(slice_.begin());
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700105 }
106
107 virtual void deallocate(uint8_t *p, size_t size) override {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700108 FLATBUFFERS_ASSERT(p == slice_.begin());
109 FLATBUFFERS_ASSERT(size == slice_.size());
110 slice_ = ::grpc::Slice();
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700111 }
112
113 virtual uint8_t *reallocate_downward(uint8_t *old_p, size_t old_size,
114 size_t new_size, size_t in_use_back,
115 size_t in_use_front) override {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700116 FLATBUFFERS_ASSERT(old_p == slice_.begin());
117 FLATBUFFERS_ASSERT(old_size == slice_.size());
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700118 FLATBUFFERS_ASSERT(new_size > old_size);
James Kuszmaul8e62b022022-03-22 09:33:25 -0700119 ::grpc::Slice old_slice = slice_;
120 ::grpc::Slice new_slice = ::grpc::Slice(new_size);
121 uint8_t *new_p = const_cast<uint8_t *>(new_slice.begin());
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700122 memcpy_downward(old_p, old_size, new_p, new_size, in_use_back,
123 in_use_front);
124 slice_ = new_slice;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700125 return new_p;
126 }
127
128 private:
James Kuszmaul8e62b022022-03-22 09:33:25 -0700129 ::grpc::Slice &get_slice(uint8_t *p, size_t size) {
130 FLATBUFFERS_ASSERT(p == slice_.begin());
131 FLATBUFFERS_ASSERT(size == slice_.size());
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700132 return slice_;
133 }
134
James Kuszmaul8e62b022022-03-22 09:33:25 -0700135 ::grpc::Slice slice_;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700136
137 friend class MessageBuilder;
138};
139
140// SliceAllocatorMember is a hack to ensure that the MessageBuilder's
141// slice_allocator_ member is constructed before the FlatBufferBuilder, since
142// the allocator is used in the FlatBufferBuilder ctor.
143namespace detail {
144struct SliceAllocatorMember {
145 SliceAllocator slice_allocator_;
146};
147} // namespace detail
148
149// MessageBuilder is a gRPC-specific FlatBufferBuilder that uses SliceAllocator
150// to allocate gRPC buffers.
151class MessageBuilder : private detail::SliceAllocatorMember,
152 public FlatBufferBuilder {
153 public:
154 explicit MessageBuilder(uoffset_t initial_size = 1024)
Austin Schuh272c6132020-11-14 16:37:52 -0800155 : FlatBufferBuilder(initial_size, &slice_allocator_, false) {}
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700156
157 MessageBuilder(const MessageBuilder &other) = delete;
158 MessageBuilder &operator=(const MessageBuilder &other) = delete;
159
160 MessageBuilder(MessageBuilder &&other)
Austin Schuh272c6132020-11-14 16:37:52 -0800161 : FlatBufferBuilder(1024, &slice_allocator_, false) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700162 // Default construct and swap idiom.
163 Swap(other);
164 }
165
166 /// Create a MessageBuilder from a FlatBufferBuilder.
Austin Schuh272c6132020-11-14 16:37:52 -0800167 explicit MessageBuilder(FlatBufferBuilder &&src,
168 void (*dealloc)(void *,
169 size_t) = &DefaultAllocator::dealloc)
170 : FlatBufferBuilder(1024, &slice_allocator_, false) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700171 src.Swap(*this);
172 src.SwapBufAllocator(*this);
173 if (buf_.capacity()) {
Austin Schuh272c6132020-11-14 16:37:52 -0800174 uint8_t *buf = buf_.scratch_data(); // pointer to memory
175 size_t capacity = buf_.capacity(); // size of memory
James Kuszmaul8e62b022022-03-22 09:33:25 -0700176 slice_allocator_.slice_ = ::grpc::Slice(buf, capacity, dealloc);
Austin Schuh272c6132020-11-14 16:37:52 -0800177 } else {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700178 slice_allocator_.slice_ = ::grpc::Slice();
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700179 }
180 }
181
182 /// Move-assign a FlatBufferBuilder to a MessageBuilder.
Austin Schuh272c6132020-11-14 16:37:52 -0800183 /// Only FlatBufferBuilder with default allocator (basically, nullptr) is
184 /// supported.
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700185 MessageBuilder &operator=(FlatBufferBuilder &&src) {
186 // Move construct a temporary and swap
187 MessageBuilder temp(std::move(src));
188 Swap(temp);
189 return *this;
190 }
191
192 MessageBuilder &operator=(MessageBuilder &&other) {
193 // Move construct a temporary and swap
194 MessageBuilder temp(std::move(other));
195 Swap(temp);
196 return *this;
197 }
198
199 void Swap(MessageBuilder &other) {
200 slice_allocator_.swap(other.slice_allocator_);
201 FlatBufferBuilder::Swap(other);
Austin Schuh272c6132020-11-14 16:37:52 -0800202 // After swapping the FlatBufferBuilder, we swap back the allocator, which
203 // restores the original allocator back in place. This is necessary because
204 // MessageBuilder's allocator is its own member (SliceAllocatorMember). The
205 // allocator passed to FlatBufferBuilder::vector_downward must point to this
206 // member.
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700207 buf_.swap_allocator(other.buf_);
208 }
209
210 // Releases the ownership of the buffer pointer.
211 // Returns the size, offset, and the original grpc_slice that
212 // allocated the buffer. Also see grpc_slice_unref().
James Kuszmaul8e62b022022-03-22 09:33:25 -0700213 uint8_t *ReleaseRaw(size_t &size, size_t &offset, ::grpc::Slice &slice) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700214 uint8_t *buf = FlatBufferBuilder::ReleaseRaw(size, offset);
215 slice = slice_allocator_.slice_;
James Kuszmaul8e62b022022-03-22 09:33:25 -0700216 slice_allocator_.slice_ = ::grpc::Slice();
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700217 return buf;
218 }
219
220 ~MessageBuilder() {}
221
222 // GetMessage extracts the subslice of the buffer corresponding to the
223 // flatbuffers-encoded region and wraps it in a `Message<T>` to handle buffer
224 // ownership.
225 template<class T> Message<T> GetMessage() {
Austin Schuh272c6132020-11-14 16:37:52 -0800226 auto buf_data = buf_.scratch_data(); // pointer to memory
227 auto buf_size = buf_.capacity(); // size of memory
228 auto msg_data = buf_.data(); // pointer to msg
229 auto msg_size = buf_.size(); // size of msg
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700230 // Do some sanity checks on data/size
231 FLATBUFFERS_ASSERT(msg_data);
232 FLATBUFFERS_ASSERT(msg_size);
233 FLATBUFFERS_ASSERT(msg_data >= buf_data);
234 FLATBUFFERS_ASSERT(msg_data + msg_size <= buf_data + buf_size);
235 // Calculate offsets from the buffer start
236 auto begin = msg_data - buf_data;
237 auto end = begin + msg_size;
238 // Get the slice we are working with (no refcount change)
James Kuszmaul8e62b022022-03-22 09:33:25 -0700239 ::grpc::Slice slice = slice_allocator_.get_slice(buf_data, buf_size);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700240 // Extract a subslice of the existing slice (increment refcount)
James Kuszmaul8e62b022022-03-22 09:33:25 -0700241 ::grpc::Slice subslice = slice.sub(begin, end);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700242 // Wrap the subslice in a `Message<T>`, but don't increment refcount
James Kuszmaul8e62b022022-03-22 09:33:25 -0700243 Message<T> msg(subslice);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700244 return msg;
245 }
246
247 template<class T> Message<T> ReleaseMessage() {
248 Message<T> msg = GetMessage<T>();
249 Reset();
250 return msg;
251 }
252
253 private:
254 // SliceAllocator slice_allocator_; // part of SliceAllocatorMember
255};
256
257} // namespace grpc
258} // namespace flatbuffers
259
260namespace grpc {
261
262template<class T> class SerializationTraits<flatbuffers::grpc::Message<T>> {
263 public:
264 static grpc::Status Serialize(const flatbuffers::grpc::Message<T> &msg,
James Kuszmaul8e62b022022-03-22 09:33:25 -0700265 ByteBuffer *buffer, bool *own_buffer) {
266 // Package the single slice into a `ByteBuffer`,
267 // incrementing the refcount in the process.
268 *buffer = ByteBuffer(&msg.BorrowSlice(), 1);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700269 *own_buffer = true;
270 return grpc::Status::OK;
271 }
272
273 // Deserialize by pulling the
James Kuszmaul8e62b022022-03-22 09:33:25 -0700274 static grpc::Status Deserialize(ByteBuffer *buf,
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700275 flatbuffers::grpc::Message<T> *msg) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700276 Slice slice;
277 if (!buf->TrySingleSlice(&slice).ok()) {
278 if (!buf->DumpToSingleSlice(&slice).ok()) {
279 buf->Clear();
280 return ::grpc::Status(::grpc::StatusCode::INTERNAL, "No payload");
281 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700282 }
James Kuszmaul8e62b022022-03-22 09:33:25 -0700283 *msg = flatbuffers::grpc::Message<T>(slice);
284 buf->Clear();
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700285#if FLATBUFFERS_GRPC_DISABLE_AUTO_VERIFICATION
286 return ::grpc::Status::OK;
287#else
288 if (msg->Verify()) {
289 return ::grpc::Status::OK;
290 } else {
291 return ::grpc::Status(::grpc::StatusCode::INTERNAL,
292 "Message verification failed");
293 }
294#endif
295 }
296};
297
298} // namespace grpc
299
300#endif // FLATBUFFERS_GRPC_H_