1/**
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
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
16#ifndef LIBPANDABASE_SERIALIZER_SERIALIZER_H
17#define LIBPANDABASE_SERIALIZER_SERIALIZER_H
18
19#include <securec.h>
20
21#include "utils/expected.h"
22#include "for_each_tuple.h"
23#include "tuple_to_struct.h"
24#include "struct_to_tuple.h"
25#include "concepts.h"
26#include <vector>
27#include <string>
28#include <unordered_map>
29#include <cstring>
30
31namespace panda::serializer {
32
33inline uintptr_t ToUintPtr(const uint8_t *p)
34{
35    return reinterpret_cast<uintptr_t>(p);
36}
37
38inline const uint8_t *ToUint8tPtr(uintptr_t v)
39{
40    return reinterpret_cast<const uint8_t *>(v);
41}
42
43template <typename T>
44// NOLINTNEXTLINE(google-runtime-references)
45inline auto TypeToBuffer(const T &value, /* out */ std::vector<uint8_t> &buffer)
46    -> std::enable_if_t<std::is_pod_v<T>, Expected<size_t, const char *>>
47{
48    const auto *ptr = reinterpret_cast<const uint8_t *>(&value);
49    std::copy(ptr, ToUint8tPtr(ToUintPtr(ptr) + sizeof(value)), std::back_inserter(buffer));
50    return sizeof(value);
51}
52
53template <class VecT>
54// NOLINTNEXTLINE(google-runtime-references)
55inline auto TypeToBuffer(const VecT &vec, /* out */ std::vector<uint8_t> &buffer)
56    -> std::enable_if_t<is_vectorable_v<VecT> && std::is_pod_v<typename VecT::value_type>,
57                        Expected<size_t, const char *>>
58{
59    using type = typename VecT::value_type;
60    // pack size
61    uint32_t size = vec.size() * sizeof(type);
62    auto ret = TypeToBuffer(size, buffer);
63    if (!ret) {
64        return ret;
65    }
66
67    // pack data
68    const auto *ptr = reinterpret_cast<const uint8_t *>(vec.data());
69    const uint8_t *ptr_end = ToUint8tPtr(ToUintPtr(ptr) + size);
70    std::copy(ptr, ptr_end, std::back_inserter(buffer));
71    return ret.Value() + size;
72}
73
74template <class UnMap>
75// NOLINTNEXTLINE(google-runtime-references)
76inline auto TypeToBuffer(const UnMap &map, /* out */ std::vector<uint8_t> &buffer)
77    -> std::enable_if_t<is_hash_mappable_v<UnMap>, Expected<size_t, const char *>>
78{
79    // pack size
80    auto ret = TypeToBuffer(static_cast<uint32_t>(map.size()), buffer);
81    if (!ret) {
82        return ret;
83    }
84
85    // At the moment, we can't use: [key, value]
86    // because clang-format-8 can't correctly detect the source code language.
87    // https://bugs.llvm.org/show_bug.cgi?id=37433
88    //
89    // TODO(v.cherkashin): Fix this loop when we switch to clang-format-9.
90    for (const auto &it : map) {
91        // pack key
92        auto k = TypeToBuffer(it.first, buffer);
93        if (!k) {
94            return k;
95        }
96        ret.Value() += k.Value();
97
98        // pack value
99        auto v = TypeToBuffer(it.second, buffer);
100        if (!v) {
101            return v;
102        }
103        ret.Value() += v.Value();
104    }
105
106    return ret;
107}
108
109template <typename T>
110Expected<size_t, const char *> BufferToType(const uint8_t *data, size_t size, /* out */ T &value)
111{
112    static_assert(std::is_pod<T>::value, "Type is not supported");
113
114    if (sizeof(value) > size) {
115        return Unexpected("Cannot deserialize POD type, the buffer is too small.");
116    }
117
118    auto *ptr = reinterpret_cast<uint8_t *>(&value);
119    memcpy_s(ptr, sizeof(value), data, sizeof(value));
120    return sizeof(value);
121}
122
123// NOLINTNEXTLINE(google-runtime-references)
124inline Expected<size_t, const char *> BufferToType(const uint8_t *data, size_t size, /* out */ std::string &str)
125{
126    // unpack size
127    uint32_t str_size = 0;
128    auto r = BufferToType(data, size, str_size);
129    if (!r || str_size == 0) {
130        return r;
131    }
132    ASSERT(r.Value() <= size);
133    data = ToUint8tPtr(ToUintPtr(data) + r.Value());
134    size -= r.Value();
135    // unpack string
136    if (size < str_size) {
137        return Unexpected("Cannot deserialize string, the buffer is too small.");
138    }
139
140    str.resize(str_size);
141    memcpy_s(str.data(), str_size, data, str_size);
142    return r.Value() + str_size;
143}
144
145template <typename T>
146Expected<size_t, const char *> BufferToType(const uint8_t *data, size_t size, /* out */ std::vector<T> &vector)
147{
148    static_assert(std::is_pod<T>::value, "Type is not supported");
149
150    // unpack size
151    uint32_t vector_size = 0;
152    auto r = BufferToType(data, size, vector_size);
153    if (!r || vector_size == 0) {
154        return r;
155    }
156    ASSERT(r.Value() <= size);
157    data = ToUint8tPtr(ToUintPtr(data) + r.Value());
158    size -= r.Value();
159    // unpack data
160    if (size < vector_size || (vector_size % sizeof(T))) {
161        return Unexpected("Cannot deserialize vector, the buffer is too small.");
162    }
163
164    vector.resize(vector_size / sizeof(T));
165    memcpy_s(vector.data(), vector_size, data, vector_size);
166
167    return r.Value() + vector_size;
168}
169
170template <typename K, typename V>
171Expected<size_t, const char *> BufferToType(const uint8_t *data, size_t size, /* out */ std::unordered_map<K, V> &map)
172{
173    size_t backup_size = size;
174    uint32_t count = 0;
175    auto r = BufferToType(data, size, count);
176    if (!r) {
177        return r;
178    }
179    ASSERT(r.Value() <= size);
180    data = ToUint8tPtr(ToUintPtr(data) + r.Value());
181    size -= r.Value();
182
183    for (uint32_t i = 0; i < count; ++i) {
184        K key {};
185        auto v = serializer::BufferToType(data, size, key);
186        if (!v) {
187            return v;
188        }
189        ASSERT(v.Value() <= size);
190        data = ToUint8tPtr(ToUintPtr(data) + v.Value());
191        size -= v.Value();
192
193        V value {};
194        v = serializer::BufferToType(data, size, value);
195        if (!v) {
196            return v;
197        }
198        ASSERT(v.Value() <= size);
199        data = ToUint8tPtr(ToUintPtr(data) + v.Value());
200        size -= v.Value();
201
202        auto ret = map.emplace(std::make_pair(std::move(key), std::move(value)));
203        if (!ret.second) {
204            return Unexpected("Cannot emplace KeyValue to map.");
205        }
206    }
207    return backup_size - size;
208}
209
210namespace internal {
211
212class Serializer {
213public:
214    // NOLINTNEXTLINE(google-runtime-references)
215    explicit Serializer(std::vector<uint8_t> &buffer) : buffer_(buffer) {}
216
217    template <typename T>
218    void operator()(const T &value)
219    {
220        TypeToBuffer(value, buffer_);
221    }
222
223    virtual ~Serializer() = default;
224
225    NO_COPY_SEMANTIC(Serializer);
226    NO_MOVE_SEMANTIC(Serializer);
227
228private:
229    std::vector<uint8_t> &buffer_;
230};
231
232class Deserializer {
233public:
234    Deserializer(const uint8_t *data, size_t size) : data_(data), size_(size) {}
235
236    bool IsError() const
237    {
238        return error_ != nullptr;
239    }
240
241    const char *GetError() const
242    {
243        return error_;
244    }
245
246    size_t GetEndPosition() const
247    {
248        return pos_;
249    }
250
251    template <typename T>
252    void operator()(T &value)
253    {
254        if (error_ != nullptr) {
255            return;
256        }
257
258        ASSERT(pos_ < size_);
259        const uint8_t *ptr = ToUint8tPtr(ToUintPtr(data_) + pos_);
260        auto ret = BufferToType(ptr, size_ - pos_, value);
261        if (!ret) {
262            error_ = ret.Error();
263            return;
264        }
265        pos_ += ret.Value();
266    }
267
268    virtual ~Deserializer() = default;
269
270    NO_COPY_SEMANTIC(Deserializer);
271    NO_MOVE_SEMANTIC(Deserializer);
272
273private:
274    const uint8_t *data_;
275    size_t size_;
276    size_t pos_ = 0;
277    const char *error_ = nullptr;
278};
279
280}  // namespace internal
281
282template <size_t N, typename Struct>
283bool StructToBuffer(Struct &&str, /* out */ std::vector<uint8_t> &buffer)  // NOLINT(google-runtime-references)
284{
285    internal::ForEachTuple(internal::StructToTuple<N>(std::forward<Struct>(str)), internal::Serializer(buffer));
286    return true;
287}
288
289template <size_t N, typename Struct>
290Expected<size_t, const char *> RawBufferToStruct(const uint8_t *data, size_t size, /* out */ Struct &str)
291{
292    using S = std::remove_reference_t<Struct>;
293    using TupleType = decltype(internal::StructToTuple<N, S>({}));
294
295    TupleType tuple;
296    internal::Deserializer deserializer(data, size);
297    internal::ForEachTuple(tuple, deserializer);
298    if (deserializer.IsError()) {
299        return Unexpected(deserializer.GetError());
300    }
301
302    str = std::move(internal::TupleToStruct<S>(tuple));
303    return deserializer.GetEndPosition();
304}
305
306template <size_t N, typename Struct>
307bool BufferToStruct(const uint8_t *data, size_t size, /* out */ Struct &str)
308{
309    auto r = RawBufferToStruct<N>(data, size, str);
310    if (!r) {
311        return false;
312    }
313    return r.Value() == size;
314}
315
316template <size_t N, typename Struct>
317bool BufferToStruct(const std::vector<uint8_t> &buffer, /* out */ Struct &str)
318{
319    return BufferToStruct<N>(buffer.data(), buffer.size(), str);
320}
321
322}  // namespace panda::serializer
323
324#endif  // LIBPANDABASE_SERIALIZER_SERIALIZER_H
325