VLink  2.0.0
A high-performance communication middleware
flatbuffers_registry.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2026 by Thun Lu. All rights reserved.
3  * Author: Thun Lu <thun.lu@zohomail.cn>
4  * Repo: https://github.com/thun-res/vlink
5  * _ __ __ _ __
6  * | | / / / / (_) ____ / /__
7  * | | / / / / / / / __ \ / //_/
8  * | |/ / / /___ / / / / / / / ,<
9  * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10  *
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  * http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  */
23 
24 /**
25  * @file flatbuffers_registry.h
26  * @brief Process-local lookup table for compiled-in FlatBuffers BFBS reflection blobs.
27  *
28  * @details
29  * Protobuf ships with a built-in process-wide registry of generated descriptors
30  * (@c google::protobuf::DescriptorPool::generated_pool()). FlatBuffers offers no such
31  * runtime pool, so VLink keeps a small singleton table that stores BFBS reflection
32  * blobs embedded inside the running binary. The schema plugin reads from this table
33  * when @c BagWriter or the proxy frontends need to attach FlatBuffers schemas to
34  * recorded URLs.
35  *
36  * The registry is intentionally minimal and never touches the filesystem -- it does not
37  * read @c VLINK_FBS_DIR / @c VLINK_PROTO_DIR and does not own the BFBS buffer memory.
38  * Three operations are supported:
39  *
40  * | API | Purpose |
41  * | ---------------------------------------------------------------- | --------------------------------------------- |
42  * | @c register_schema<BinarySchema>(name) | Register a generated @c *BinarySchema helper |
43  * | @c register_schema(name, bfbs_data, bfbs_size) | Register raw BFBS bytes |
44  * | @c search_schema(name) | Look up one entry by root type name |
45  * | @c get_all_schemas() | Enumerate every registered entry |
46  *
47  * Type resolution flow:
48  *
49  * @verbatim
50  * generated *BinarySchema --register_schema--> +-------------------+
51  * | BFBS map |
52  * | (name -> Schema) |
53  * user lookup (root type) --search_schema---> +-------------------+ --> SchemaData
54  * @endverbatim
55  *
56  * @par Example
57  * @code
58  * #include <vlink/extension/flatbuffers_registry.h>
59  * #include "helloworld/fbs/User_generated.h"
60  *
61  * // Auto-register at static initialisation time:
62  * VLINK_REGISTER_FLATBUFFERS("Helloworld.fbs.User", Helloworld::fbs::UserBinarySchema);
63  *
64  * void inspect() {
65  * auto schema = vlink::FlatbuffersRegistry::get().search_schema("Helloworld.fbs.User");
66  * if (!schema.data.empty()) {
67  * // Use schema.data to populate bag metadata or webviz channel info.
68  * }
69  * }
70  * @endcode
71  */
72 
73 #pragma once
74 
75 #include <cstddef>
76 #include <mutex>
77 #include <shared_mutex>
78 #include <string>
79 #include <unordered_map>
80 #include <vector>
81 
82 #include "../impl/types.h"
83 
84 #if __has_include(<flatbuffers/idl.h>)
85 #include <flatbuffers/flatbuffers.h>
86 #include <flatbuffers/idl.h>
87 #define VLINK_HAS_SCHEMA_PLUGIN_FLATBUFFERS
88 #endif
89 
90 #ifdef VLINK_HAS_SCHEMA_PLUGIN_FLATBUFFERS
91 
92 namespace vlink {
93 
94 /**
95  * @class FlatbuffersRegistry
96  * @brief Singleton storing non-owning views of compiled-in FlatBuffers BFBS reflection blobs.
97  *
98  * @details
99  * The registry keeps a @c SchemaData entry per fully-qualified FlatBuffers root type
100  * name. Callers must guarantee that the BFBS bytes outlive every registry reference;
101  * generated @c *BinarySchema helpers point into static link-time storage and satisfy
102  * this requirement automatically. All public operations are thread-safe.
103  */
104 class FlatbuffersRegistry final {
105  public:
106  /**
107  * @brief Registers the BFBS data exposed by a generated @c *BinarySchema helper type.
108  *
109  * @tparam BinarySchema Generated helper exposing @c data() and @c size() static accessors.
110  * @param name Fully-qualified FlatBuffers root type name (table or struct).
111  * @return @c true when the blob is valid FlatBuffers reflection data and is now stored.
112  */
113  template <typename BinarySchema>
114  static bool register_schema(const std::string& name);
115 
116  /**
117  * @brief Registers raw BFBS bytes under the given root type name.
118  *
119  * @param name Fully-qualified FlatBuffers root type name.
120  * @param bfbs_data Pointer to BFBS reflection bytes.
121  * @param bfbs_size Length of @p bfbs_data in bytes.
122  * @return @c true when the blob is valid FlatBuffers reflection data and is now stored.
123  */
124  static bool register_schema(const std::string& name, const uint8_t* bfbs_data, size_t bfbs_size);
125 
126  /**
127  * @brief Looks up a single BFBS entry by root type name.
128  *
129  * @param name Fully-qualified root type name.
130  * @return Copy of the stored @c SchemaData, or an empty schema when no entry matches.
131  */
132  [[nodiscard]] SchemaData search_schema(const std::string& name);
133 
134  /**
135  * @brief Returns every BFBS entry currently held by the registry.
136  *
137  * @return Vector of @c SchemaData copies.
138  */
139  [[nodiscard]] std::vector<SchemaData> get_all_schemas();
140 
141  private:
142  FlatbuffersRegistry() = default;
143 
144  ~FlatbuffersRegistry() = default;
145 
146  static SchemaData build_data(const std::string& name, const uint8_t* bfbs_data, size_t bfbs_size);
147 
148  std::unordered_map<std::string, SchemaData> map_;
149  std::shared_mutex mtx_;
150 
151  VLINK_SINGLETON_DECLARE(FlatbuffersRegistry)
152 };
153 
154 ////////////////////////////////////////////////////////////////
155 // Details
156 ////////////////////////////////////////////////////////////////
157 
158 template <typename BinarySchema>
159 inline bool FlatbuffersRegistry::register_schema(const std::string& name) {
160  return register_schema(name, BinarySchema::data(), BinarySchema::size());
161 }
162 
163 inline bool FlatbuffersRegistry::register_schema(const std::string& name, const uint8_t* bfbs_data, size_t bfbs_size) {
164  auto schema = build_data(name, bfbs_data, bfbs_size);
165 
166  if VUNLIKELY (schema.encoding != "flatbuffers" || schema.data.empty()) {
167  return false;
168  }
169 
170  auto& registry = get();
171 
172  std::lock_guard lock(registry.mtx_);
173  registry.map_[schema.name] = std::move(schema);
174 
175  return true;
176 }
177 
178 inline SchemaData FlatbuffersRegistry::search_schema(const std::string& name) {
179  std::shared_lock lock(mtx_);
180 
181  auto iter = map_.find(name);
182 
183  if (iter == map_.end()) {
184  return {};
185  }
186 
187  return iter->second;
188 }
189 
190 inline std::vector<SchemaData> FlatbuffersRegistry::get_all_schemas() {
191  std::shared_lock lock(mtx_);
192 
193  std::vector<SchemaData> schemas;
194  schemas.reserve(map_.size());
195 
196  for (const auto& [name, schema] : map_) {
197  (void)name;
198  schemas.emplace_back(schema);
199  }
200 
201  return schemas;
202 }
203 
204 inline SchemaData FlatbuffersRegistry::build_data(const std::string& name, const uint8_t* bfbs_data, size_t bfbs_size) {
205  SchemaData schema;
207 
208  if VUNLIKELY (name.empty() || bfbs_data == nullptr || bfbs_size == 0) {
209  return schema;
210  }
211 
212  schema.name = name;
213  schema.encoding = "flatbuffers";
214  schema.schema_type = SchemaType::kFlatbuffers;
215  schema.data = Bytes::shallow_copy(bfbs_data, bfbs_size);
216 
217  flatbuffers::Verifier verifier(schema.data.data(), schema.data.size());
218 
219  if VUNLIKELY (!reflection::VerifySchemaBuffer(verifier)) {
220  schema.encoding.clear();
221  schema.schema_type = SchemaType::kUnknown;
222  schema.data.clear();
223  }
224 
225  return schema;
226 }
227 
228 } // namespace vlink
229 
230 /**
231  * @def VLINK_REGISTER_FLATBUFFERS_NOW(schema_name, binary_schema_type)
232  * @brief Immediately registers a compiled-in BFBS schema with the global registry.
233  *
234  * @details
235  * Expands to a direct call to
236  * @c ::vlink::FlatbuffersRegistry::register_schema<binary_schema_type>(). No static
237  * objects are created, so the macro may also be used from inside a function body.
238  */
239 #define VLINK_REGISTER_FLATBUFFERS_NOW(schema_name, binary_schema_type) \
240  ::vlink::FlatbuffersRegistry::register_schema<binary_schema_type>(schema_name)
241 
242 /**
243  * @def VLINK_REGISTER_FLATBUFFERS(schema_name, binary_schema_type)
244  * @brief Auto-registers a BFBS schema at static initialisation time.
245  *
246  * @details
247  * Defines a translation-unit-local helper type plus an unnamed @c static instance whose
248  * constructor invokes @c VLINK_REGISTER_FLATBUFFERS_NOW. Each expansion uses
249  * @c __COUNTER__ to obtain a unique helper specialisation so the macro can be used
250  * multiple times in the same translation unit and works correctly with namespaced or
251  * templated @p binary_schema_type values.
252  *
253  * @par Example
254  * @code
255  * VLINK_REGISTER_FLATBUFFERS("Helloworld.fbs.User",
256  * Helloworld::fbs::UserBinarySchema);
257  * @endcode
258  */
259 #define VLINK_REGISTER_FLATBUFFERS(schema_name, binary_schema_type) \
260  /* NOLINTBEGIN */ \
261  namespace { \
262  template <int Id> \
263  struct VlinkAutoRegisterFlatbuffersHelper; \
264  \
265  template <> \
266  struct VlinkAutoRegisterFlatbuffersHelper<__COUNTER__> { \
267  struct Init { \
268  Init() noexcept { \
269  using SchemaType = binary_schema_type; \
270  (void)VLINK_REGISTER_FLATBUFFERS_NOW(schema_name, SchemaType); \
271  } \
272  }; \
273  \
274  [[maybe_unused]] inline static const Init instance{}; \
275  }; \
276  } \
277  /* NOLINTEND */
278 
279 #endif // VLINK_HAS_SCHEMA_PLUGIN_FLATBUFFERS
#define VUNLIKELY(...)
Short alias for VLINK_UNLIKELY.
Definition: macros.h:289
#define VLINK_SINGLETON_DECLARE(classname)
Declares a Meyers singleton get and the matching uniqueness guards on classname.
Definition: macros.h:232
Definition: serializer-inl.h:142