1 /*
2  * Copyright 2020 The Android Open Source Project
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 #pragma once
18 
19 #include <numeric>
20 #include <optional>
21 #include <type_traits>
22 #include <vector>
23 
24 #include <utils/Flattenable.h>
25 
26 #define RETURN_IF_ERROR(op) \
27     if (const status_t status = (op); status != OK) return status;
28 
29 namespace android {
30 
31 struct FlattenableHelpers {
32     // Helpers for reading and writing POD structures which are not LightFlattenable.
33     template <class T,
34               typename = std::enable_if_t<
35                       std::conjunction_v<std::is_trivially_copyable<T>,
36                                          std::negation<std::is_base_of<LightFlattenable<T>, T>>>>>
getFlattenedSizeFlattenableHelpers37     static constexpr size_t getFlattenedSize(const T&) {
38         return sizeof(T);
39     }
40 
41     template <class T,
42               typename = std::enable_if_t<
43                       std::conjunction_v<std::is_trivially_copyable<T>,
44                                          std::negation<std::is_base_of<LightFlattenable<T>, T>>>>>
flattenFlattenableHelpers45     static status_t flatten(void** buffer, size_t* size, const T& value) {
46         if (*size < sizeof(T)) return NO_MEMORY;
47         FlattenableUtils::write(*buffer, *size, value);
48         return OK;
49     }
50 
51     template <class T,
52               typename = std::enable_if_t<
53                       std::conjunction_v<std::is_trivially_copyable<T>,
54                                          std::negation<std::is_base_of<LightFlattenable<T>, T>>>>>
unflattenFlattenableHelpers55     static status_t unflatten(const void** buffer, size_t* size, T* value) {
56         if (*size < sizeof(T)) return NO_MEMORY;
57         FlattenableUtils::read(*buffer, *size, *value);
58         return OK;
59     }
60 
61     // Helpers for reading and writing std::string
getFlattenedSizeFlattenableHelpers62     static size_t getFlattenedSize(const std::string& str) {
63         return sizeof(uint64_t) + str.length();
64     }
65 
flattenFlattenableHelpers66     static status_t flatten(void** buffer, size_t* size, const std::string& str) {
67         if (*size < getFlattenedSize(str)) return NO_MEMORY;
68         flatten(buffer, size, (uint64_t)str.length());
69         memcpy(reinterpret_cast<char*>(*buffer), str.c_str(), str.length());
70         FlattenableUtils::advance(*buffer, *size, str.length());
71         return OK;
72     }
73 
unflattenFlattenableHelpers74     static status_t unflatten(const void** buffer, size_t* size, std::string* str) {
75         uint64_t length;
76         RETURN_IF_ERROR(unflatten(buffer, size, &length));
77         if (*size < length) return NO_MEMORY;
78         str->assign(reinterpret_cast<const char*>(*buffer), length);
79         FlattenableUtils::advance(*buffer, *size, length);
80         return OK;
81     }
82 
83     // Helpers for reading and writing LightFlattenable
84     template <class T>
getFlattenedSizeFlattenableHelpers85     static size_t getFlattenedSize(const LightFlattenable<T>& value) {
86         return value.getFlattenedSize();
87     }
88 
89     template <class T>
flattenFlattenableHelpers90     static status_t flatten(void** buffer, size_t* size, const LightFlattenable<T>& value) {
91         RETURN_IF_ERROR(value.flatten(*buffer, *size));
92         FlattenableUtils::advance(*buffer, *size, value.getFlattenedSize());
93         return OK;
94     }
95 
96     template <class T>
unflattenFlattenableHelpers97     static status_t unflatten(const void** buffer, size_t* size, LightFlattenable<T>* value) {
98         RETURN_IF_ERROR(value->unflatten(*buffer, *size));
99         FlattenableUtils::advance(*buffer, *size, value->getFlattenedSize());
100         return OK;
101     }
102 
103     // Helpers for reading and writing std::optional
104     template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>>
getFlattenedSizeFlattenableHelpers105     static size_t getFlattenedSize(const std::optional<T>& value) {
106         return sizeof(bool) + (value ? getFlattenedSize(*value) : 0);
107     }
108 
109     template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>>
flattenFlattenableHelpers110     static status_t flatten(void** buffer, size_t* size, const std::optional<T>& value) {
111         if (value) {
112             RETURN_IF_ERROR(flatten(buffer, size, true));
113             RETURN_IF_ERROR(flatten(buffer, size, *value));
114         } else {
115             RETURN_IF_ERROR(flatten(buffer, size, false));
116         }
117         return OK;
118     }
119 
120     template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>>
unflattenFlattenableHelpers121     static status_t unflatten(const void** buffer, size_t* size, std::optional<T>* value) {
122         bool isPresent;
123         RETURN_IF_ERROR(unflatten(buffer, size, &isPresent));
124         if (isPresent) {
125             *value = T();
126             RETURN_IF_ERROR(unflatten(buffer, size, &(**value)));
127         } else {
128             value->reset();
129         }
130         return OK;
131     }
132 
133     // Helpers for reading and writing std::vector
134     template <class T>
getFlattenedSizeFlattenableHelpers135     static size_t getFlattenedSize(const std::vector<T>& value) {
136         return std::accumulate(value.begin(), value.end(), sizeof(uint64_t),
137                                [](size_t sum, const T& element) {
138                                    return sum + getFlattenedSize(element);
139                                });
140     }
141 
142     template <class T>
flattenFlattenableHelpers143     static status_t flatten(void** buffer, size_t* size, const std::vector<T>& value) {
144         RETURN_IF_ERROR(flatten(buffer, size, (uint64_t)value.size()));
145         for (const auto& element : value) {
146             RETURN_IF_ERROR(flatten(buffer, size, element));
147         }
148         return OK;
149     }
150 
151     template <class T>
unflattenFlattenableHelpers152     static status_t unflatten(const void** buffer, size_t* size, std::vector<T>* value) {
153         uint64_t numElements;
154         RETURN_IF_ERROR(unflatten(buffer, size, &numElements));
155         // We don't need an extra size check since each iteration of the loop does that
156         std::vector<T> elements;
157         for (size_t i = 0; i < numElements; i++) {
158             T element;
159             RETURN_IF_ERROR(unflatten(buffer, size, &element));
160             elements.push_back(element);
161         }
162         *value = std::move(elements);
163         return OK;
164     }
165 };
166 
167 } // namespace android
168 
169 #undef RETURN_IF_ERROR