Action Engine
Loading...
Searching...
No Matches
core_helpers.h
1// Copyright 2025 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#ifndef ACTIONENGINE_MSGPACK_CORE_HELPERS_H_
16#define ACTIONENGINE_MSGPACK_CORE_HELPERS_H_
17
18#include <any>
19#include <bit>
20
21#include <absl/container/inlined_vector.h>
22#include <absl/status/status.h>
23#include <absl/status/statusor.h>
24#include <absl/strings/str_cat.h>
25#include <absl/strings/str_format.h>
26
27namespace act::msgpack {
28
29using Byte = uint8_t;
30// Using absl::InlinedVector to optimize for small sizes; 9 is chosen
31// to accomodate a format byte and a typical 64-bit value.
32using SerializedBytesVector = absl::InlinedVector<Byte, 9>;
33
34inline std::vector<Byte> ToStdVector(SerializedBytesVector bytes) {
35 return {std::make_move_iterator(bytes.begin()),
36 std::make_move_iterator(bytes.end())};
37}
38
39struct InsertInfo {
40 SerializedBytesVector* absl_nonnull bytes;
41 SerializedBytesVector::iterator absl_nonnull at;
42};
43
44struct LookupPointer {
45 Byte* absl_nonnull pos;
46 Byte* absl_nonnull end;
47 Byte* absl_nullable begin;
48
49 LookupPointer(Byte* absl_nonnull pos, Byte* absl_nonnull end,
50 Byte* absl_nullable begin = nullptr)
51 : pos(pos), end(end), begin(begin) {}
52
53 bool operator==(const LookupPointer& other) const {
54 return pos == other.pos && end == other.end && begin == other.begin;
55 }
56};
57
58template <typename T>
59struct Deserialized {
60 T value;
61 uint32_t extent;
62};
63
64template <typename T>
65SerializedBytesVector ToBigEndianBytes(const T& value, uint8_t pad = 0) {
66 SerializedBytesVector bytes(sizeof(T) + pad);
67 if constexpr (std::endian::native == std::endian::little) {
68 for (size_t i = 0; i < sizeof(T); ++i) {
69 bytes[i + pad] = (value >> ((sizeof(T) - 1 - i) * 8)) & 0xFF;
70 }
71 } else {
72 for (size_t i = 0; i < sizeof(T); ++i) {
73 bytes[i + pad] = (value >> (i * 8)) & 0xFF;
74 }
75 }
76 return bytes;
77}
78
79#if __STDCPP_FLOAT32_T__ != 1
80using float32_t = float;
81using float64_t = double;
82#endif
83
84template <>
85inline SerializedBytesVector ToBigEndianBytes<float32_t>(const float32_t& value,
86 uint8_t pad) {
87 return ToBigEndianBytes<uint32_t>(reinterpret_cast<const uint32_t&>(value),
88 pad);
89}
90
91template <>
92inline SerializedBytesVector ToBigEndianBytes<float64_t>(const float64_t& value,
93 uint8_t pad) {
94 return ToBigEndianBytes<uint64_t>(reinterpret_cast<const uint64_t&>(value),
95 pad);
96}
97
98template <typename T>
99T FromBigEndianBytes(const Byte* absl_nonnull bytes) {
100 if constexpr (std::endian::native == std::endian::big) {
101 return *reinterpret_cast<T*>(bytes);
102 }
103 absl::InlinedVector<Byte, sizeof(T)> reversed_bytes(sizeof(T));
104 for (size_t i = 0; i < sizeof(T); ++i) {
105 reversed_bytes[i] = bytes[sizeof(T) - 1 - i];
106 }
107 return *reinterpret_cast<T*>(reversed_bytes.data());
108}
109
110enum FormatSignature {
111 kPositiveFixint = 0b00000000, // 0x00 to 0x7F
112 kFixMap = 0b10000000, // 0x80 to 0x8F
113 kFixArray = 0b10010000, // 0x90 to 0x9F
114 kFixStr = 0b10100000, // 0xA0 to 0xBF
115 kNil = 0b11000000, // 0xC0
116 kNeverUsed = 0b11000001, // Reserved for future use, should not be used
117 kFalse = 0b11000010, // 0xC2
118 kTrue = 0b11000011, // 0xC3
119 kBin8 = 0b11000100, // 0xC4
120 kBin16 = 0b11000101, // 0xC5
121 kBin32 = 0b11000110, // 0xC6
122 kExt8 = 0b11000111, // 0xC7
123 kExt16 = 0b11001000, // 0xC8
124 kExt32 = 0b11001001, // 0xC9
125 kFloat32 = 0b11001010, // 0xCA
126 kFloat64 = 0b11001011, // 0xCB
127 kUInt8 = 0b11001100, // 0xCC
128 kUInt16 = 0b11001101, // 0xCD
129 kUInt32 = 0b11001110, // 0xCE
130 kUInt64 = 0b11001111, // 0xCF
131 kInt8 = 0b11010000, // 0xD0
132 kInt16 = 0b11010001, // 0xD1
133 kInt32 = 0b11010010, // 0xD2
134 kInt64 = 0b11010011, // 0xD3
135 kFixExt1 = 0b11010100, // 0xD4
136 kFixExt2 = 0b11010101, // 0xD5
137 kFixExt4 = 0b11010110, // 0xD6
138 kFixExt8 = 0b11010111, // 0xD7
139 kFixExt16 = 0b11011000, // 0xD8
140 kStr8 = 0b11011001, // 0xD9
141 kStr16 = 0b11011010, // 0xDA
142 kStr32 = 0b11011011, // 0xDB
143 kArray16 = 0b11011100, // 0xDC
144 kArray32 = 0b11011101, // 0xDD
145 kMap16 = 0b11011110, // 0xDE
146 kMap32 = 0b11011111, // 0xDF
147 kNegativeFixint = 0b11100000 // 0xE0 to 0xFF, two's complement
148};
149
150inline std::string GetPositionString(
151 const Byte* absl_nonnull pos, const Byte* absl_nullable begin = nullptr) {
152 if (begin != nullptr) {
153 return absl::StrFormat("%d", pos - begin);
154 }
155 return absl::StrFormat("0x%02x", *pos);
156}
157
158inline absl::Status GetInvalidFormatSignatureError(
159 const Byte* absl_nonnull pos, std::string_view type_name,
160 const Byte* absl_nullable begin = nullptr) {
161
162 return absl::InvalidArgumentError(absl::StrCat(
163 "Expected a ", type_name,
164 " value, but found a different format signature at position ",
165 GetPositionString(pos, begin), ": ", absl::StrFormat("0x%02x", *pos)));
166}
167
168inline absl::Status GetInsufficientDataError(const LookupPointer& data,
169 std::string_view type_name) {
170 return absl::InvalidArgumentError(absl::StrCat(
171 "Insufficient data to read a ", type_name, " value at position ",
172 GetPositionString(data.pos, data.begin), ": ",
173 absl::StrFormat("0x%02x", *data.pos), ". Only have ",
174 absl::StrFormat("%d", data.end - data.pos), " bytes."));
175}
176
177// Forward declarations for EgltMsgpackGetExtent, Serialize, and Deserialize.
178template <typename T>
179absl::StatusOr<uint32_t> GetExtent(Byte* absl_nonnull pos,
180 Byte* absl_nonnull end) {
181 if (pos >= end) {
182 return absl::InvalidArgumentError(
183 "Position is not within the bounds of the data.");
184 }
185
186 if (*pos == FormatSignature::kNeverUsed) {
187 return absl::InvalidArgumentError(
188 "FormatSignature::kNeverUsed is not a valid format signature.");
189 }
190
191 return EgltMsgpackGetExtent(LookupPointer(pos, end),
192 static_cast<T*>(nullptr));
193}
194
195template <typename T>
196absl::StatusOr<SerializedBytesVector> Serialize(T&& value) {
197 SerializedBytesVector output;
198 auto status = EgltMsgpackSerialize(std::forward<T>(value),
199 InsertInfo{&output, output.end()});
200 if (!status.ok()) {
201 return status;
202 }
203 return output;
204}
205
206template <typename T>
207absl::Status Serialize(T&& value, const InsertInfo& insert) {
208 return EgltMsgpackSerialize(std::forward<T>(value), insert);
209}
210
211template <typename T>
212concept HasDefinedGetExtent = requires(T t) {
213 {
214 EgltMsgpackGetExtent(std::declval<LookupPointer>(), std::declval<T*>())
215 } -> std::same_as<absl::StatusOr<uint32_t>>;
216};
217
218template <typename T>
219absl::StatusOr<uint32_t> Deserialize(const LookupPointer& data,
220 T* absl_nonnull output)
221 requires(!HasDefinedGetExtent<T>) {
222 const auto [pos, end, _] = data;
223 if (pos >= end) {
224 return absl::InvalidArgumentError(
225 "Position is not within the bounds of the data.");
226 }
227
228 if (*pos == FormatSignature::kNeverUsed) {
229 return absl::InvalidArgumentError(
230 "FormatSignature::kNeverUsed is not a valid format signature.");
231 }
232
233 auto extent = EgltMsgpackDeserialize(LookupPointer(pos, end), output);
234 if (!extent.ok()) {
235 return extent.status();
236 }
237 return *extent;
238}
239
240template <typename T>
241absl::StatusOr<uint32_t> Deserialize(const LookupPointer& data,
242 T* absl_nonnull output)
243 requires(HasDefinedGetExtent<T>) {
244 const auto [pos, end, _] = data;
245 if (pos >= end) {
246 return absl::InvalidArgumentError(
247 "Position is not within the bounds of the data.");
248 }
249
250 if (*pos == FormatSignature::kNeverUsed) {
251 return absl::InvalidArgumentError(
252 "FormatSignature::kNeverUsed is not a valid format signature.");
253 }
254
255#ifndef NDEBUG
256 absl::StatusOr<uint32_t> expected_extent = GetExtent<T>(pos, end);
257 if (!expected_extent.ok()) {
258 return expected_extent.status();
259 }
260#endif // NDEBUG
261
262 auto actual_extent = EgltMsgpackDeserialize(LookupPointer(pos, end), output);
263 if (!actual_extent.ok()) {
264 return actual_extent.status();
265 }
266
267#ifndef NDEBUG
268 if (*expected_extent != *actual_extent) {
269 return absl::InvalidArgumentError(absl::StrFormat(
270 "Expected extent: %d, actual extent: %d at position %s",
271 *expected_extent, *actual_extent, GetPositionString(pos, data.begin)));
272 }
273#endif // NDEBUG
274
275 return *actual_extent;
276}
277
278template <typename T>
279absl::StatusOr<Deserialized<T>> Deserialize(const LookupPointer& data) {
280 Deserialized<T> deserialized;
281 deserialized.extent = 0;
282 auto status_or_extent = Deserialize(data, &deserialized.value);
283 if (!status_or_extent.ok()) {
284 return status_or_extent.status();
285 }
286 deserialized.extent = *status_or_extent;
287 return deserialized;
288}
289
290// These are unimplemented functions that should be defined on platforms
291// that use non-IEC559 floating-point types. They are placeholders for
292// future implementations and should not be used in the current codebase.
293absl::Status SerializeNonIec559Float32(float32_t value,
294 const InsertInfo& insert);
295absl::Status SerializeNonIec559Float64(float64_t value,
296 const InsertInfo& insert);
297
298} // namespace act::msgpack
299
300#endif // ACTIONENGINE_MSGPACK_CORE_HELPERS_H_