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 LIBPANDAFILE_HELPERS_H
17 #define LIBPANDAFILE_HELPERS_H
18
19 #include "macros.h"
20 #include "utils/bit_helpers.h"
21 #include "utils/leb128.h"
22 #include "utils/logger.h"
23 #include "utils/span.h"
24
25 #include <cstdint>
26 #include <exception>
27 #include <limits>
28 #include <optional>
29
30 namespace panda::panda_file::helpers {
31
32 constexpr size_t UINT_BYTE2_SHIFT = 8U;
33 constexpr size_t UINT_BYTE3_SHIFT = 16U;
34 constexpr size_t UINT_BYTE4_SHIFT = 24U;
35 constexpr const char *INVALID_SPAN_OFFSET = "Invalid span offset";
36
37 class FileAccessException : public std::exception {
38 public:
FileAccessException(const char * msg)39 explicit FileAccessException(const char *msg) : msg_(msg) {}
FileAccessException(const std::string_view & msg)40 explicit FileAccessException(const std::string_view &msg) : msg_(msg) {}
what()41 const char *what() const noexcept override
42 {
43 return msg_.c_str();
44 }
45
46 private:
47 std::string msg_;
48 };
49
50 #ifndef SUPPORT_KNOWN_EXCEPTION
51 #define THROW_IF(cond, msg) \
52 if (UNLIKELY(cond)) { \
53 LOG(FATAL, PANDAFILE) << (msg); \
54 }
55 #else
56 #define THROW_IF(cond, msg) \
57 if (UNLIKELY(cond)) { \
58 throw helpers::FileAccessException(msg); \
59 }
60 #endif
61
62 template <size_t width>
Read(Span<const uint8_t> * sp)63 inline auto Read(Span<const uint8_t> *sp)
64 {
65 constexpr size_t BYTE_WIDTH = std::numeric_limits<uint8_t>::digits;
66 constexpr size_t BITWIDTH = BYTE_WIDTH * width;
67 using unsigned_type = panda::helpers::TypeHelperT<BITWIDTH, false>;
68
69 unsigned_type result = 0;
70 THROW_IF(sp->Size() < width, INVALID_SPAN_OFFSET);
71
72 for (size_t i = 0; i < width; i++) {
73 unsigned_type tmp = static_cast<unsigned_type>((*sp)[i]) << (i * BYTE_WIDTH);
74 result |= tmp;
75 }
76 *sp = sp->SubSpan(width);
77 return result;
78 }
79
80 template <>
81 inline auto Read<sizeof(uint16_t)>(Span<const uint8_t> *sp)
82 {
83 uint16_t result = 0;
84 THROW_IF(sp->Size() < sizeof(uint16_t), INVALID_SPAN_OFFSET);
85
86 auto *p = sp->data();
87 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
88 result = *(p++);
89 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic, hicpp-signed-bitwise)
90 result |= static_cast<uint16_t>(*p) << UINT_BYTE2_SHIFT;
91 *sp = sp->SubSpan(sizeof(uint16_t));
92 return result;
93 }
94
95 template <>
96 inline auto Read<sizeof(uint32_t)>(Span<const uint8_t> *sp)
97 {
98 uint32_t result = 0;
99 THROW_IF(sp->Size() < sizeof(uint32_t), INVALID_SPAN_OFFSET);
100
101 auto *p = sp->data();
102 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
103 result = *(p++);
104 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
105 result |= static_cast<uint32_t>(*(p++)) << UINT_BYTE2_SHIFT;
106 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
107 result |= static_cast<uint32_t>(*(p++)) << UINT_BYTE3_SHIFT;
108 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
109 result |= static_cast<uint32_t>(*p) << UINT_BYTE4_SHIFT;
110 *sp = sp->SubSpan(sizeof(uint32_t));
111 return result;
112 }
113
114 template <size_t width>
Read(Span<const uint8_t> sp)115 inline auto Read(Span<const uint8_t> sp)
116 {
117 return Read<width>(&sp);
118 }
119
ReadULeb128(Span<const uint8_t> * sp)120 inline uint32_t ReadULeb128(Span<const uint8_t> *sp)
121 {
122 THROW_IF(sp->Size() == 0U, INVALID_SPAN_OFFSET);
123
124 uint32_t result;
125 size_t n;
126 bool is_full;
127 std::tie(result, n, is_full) = leb128::DecodeUnsigned<uint32_t>(sp->data());
128 THROW_IF(!is_full || sp->Size() < n, INVALID_SPAN_OFFSET);
129 *sp = sp->SubSpan(n);
130 return result;
131 }
132
SkipULeb128(Span<const uint8_t> * sp)133 inline void SkipULeb128(Span<const uint8_t> *sp)
134 {
135 if (sp->Size() > 0U && (*sp)[0U] <= leb128::PAYLOAD_MASK) {
136 *sp = sp->SubSpan(1U);
137 return;
138 }
139
140 if (sp->Size() > 1U && (*sp)[1U] <= leb128::PAYLOAD_MASK) {
141 *sp = sp->SubSpan(2U);
142 return;
143 }
144
145 if (sp->Size() > 2U && (*sp)[2U] <= leb128::PAYLOAD_MASK) {
146 *sp = sp->SubSpan(3U);
147 return;
148 }
149
150 if (sp->Size() > 3U && (*sp)[3U] <= leb128::PAYLOAD_MASK) {
151 *sp = sp->SubSpan(4U);
152 }
153 }
154
ReadLeb128(Span<const uint8_t> * sp)155 inline int32_t ReadLeb128(Span<const uint8_t> *sp)
156 {
157 uint32_t result;
158 size_t n;
159 bool is_full;
160 std::tie(result, n, is_full) = leb128::DecodeSigned<int32_t>(sp->data());
161 THROW_IF(!is_full || sp->Size() < n, INVALID_SPAN_OFFSET);
162 *sp = sp->SubSpan(n);
163 return result;
164 }
165
166 template <size_t alignment>
Align(const uint8_t * ptr)167 inline const uint8_t *Align(const uint8_t *ptr)
168 {
169 auto intptr = reinterpret_cast<uintptr_t>(ptr);
170 uintptr_t aligned = (intptr - 1U + alignment) & -alignment;
171 return reinterpret_cast<const uint8_t *>(aligned);
172 }
173
174 template <size_t alignment, class T>
Align(T n)175 inline T Align(T n)
176 {
177 return (n - 1U + alignment) & -alignment;
178 }
179
180 template <class T, class E>
GetOptionalTaggedValue(Span<const uint8_t> sp,E tag,Span<const uint8_t> * next)181 inline std::optional<T> GetOptionalTaggedValue(Span<const uint8_t> sp, E tag, Span<const uint8_t> *next)
182 {
183 // NB! This is a workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80635
184 // which fails Release builds for GCC 8 and 9.
185 std::optional<T> novalue = {};
186 if (UNLIKELY(sp.Size() == 0U)) {
187 return novalue;
188 }
189
190 if (sp[0] == static_cast<uint8_t>(tag)) {
191 sp = sp.SubSpan(1);
192 T value = static_cast<T>(Read<sizeof(T)>(&sp));
193 *next = sp;
194 return value;
195 }
196 *next = sp;
197
198 return novalue;
199 }
200
201 template <class T, class E, class Callback>
EnumerateTaggedValues(Span<const uint8_t> sp,E tag,Callback cb,Span<const uint8_t> * next)202 inline void EnumerateTaggedValues(Span<const uint8_t> sp, E tag, Callback cb, Span<const uint8_t> *next)
203 {
204 while (sp.Size() > 0U && sp[0] == static_cast<uint8_t>(tag)) {
205 sp = sp.SubSpan(1);
206 T value(Read<sizeof(T)>(&sp));
207 cb(value);
208 }
209
210 if (next == nullptr) {
211 return;
212 }
213
214 *next = sp;
215 }
216
217 template <class T, class E, class Callback>
EnumerateTaggedValuesWithEarlyStop(Span<const uint8_t> sp,E tag,Callback cb)218 inline bool EnumerateTaggedValuesWithEarlyStop(Span<const uint8_t> sp, E tag, Callback cb)
219 {
220 while (sp.Size() > 0U && sp[0] == static_cast<uint8_t>(tag)) {
221 sp = sp.SubSpan(1);
222 T value(Read<sizeof(T)>(&sp));
223 if (cb(value)) {
224 return true;
225 }
226 }
227
228 return false;
229 }
230
231 } // namespace panda::panda_file::helpers
232
233 #endif // LIBPANDAFILE_HELPERS_H
234