• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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