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
30namespace panda::panda_file::helpers {
31
32constexpr size_t UINT_BYTE2_SHIFT = 8U;
33constexpr size_t UINT_BYTE3_SHIFT = 16U;
34constexpr size_t UINT_BYTE4_SHIFT = 24U;
35constexpr const char *INVALID_SPAN_OFFSET = "Invalid span offset";
36
37class FileAccessException : public std::exception {
38public:
39    explicit FileAccessException(const char *msg) : msg_(msg) {}
40    explicit FileAccessException(const std::string_view &msg) : msg_(msg) {}
41    const char *what() const noexcept override
42    {
43        return msg_.c_str();
44    }
45
46private:
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
62template <size_t width>
63inline 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
80template <>
81inline 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
95template <>
96inline 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
114template <size_t width>
115inline auto Read(Span<const uint8_t> sp)
116{
117    return Read<width>(&sp);
118}
119
120inline 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
133inline 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
155inline 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
166template <size_t alignment>
167inline 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
174template <size_t alignment, class T>
175inline T Align(T n)
176{
177    return (n - 1U + alignment) & -alignment;
178}
179
180template <class T, class E>
181inline 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
201template <class T, class E, class Callback>
202inline 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
217template <class T, class E, class Callback>
218inline 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