/** * Copyright (c) 2021-2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef LIBPANDAFILE_HELPERS_H #define LIBPANDAFILE_HELPERS_H #include "macros.h" #include "utils/bit_helpers.h" #include "utils/leb128.h" #include "utils/logger.h" #include "utils/span.h" #include #include #include #include namespace panda::panda_file::helpers { constexpr size_t UINT_BYTE2_SHIFT = 8U; constexpr size_t UINT_BYTE3_SHIFT = 16U; constexpr size_t UINT_BYTE4_SHIFT = 24U; constexpr const char *INVALID_SPAN_OFFSET = "Invalid span offset"; class FileAccessException : public std::exception { public: explicit FileAccessException(const char *msg) : msg_(msg) {} explicit FileAccessException(const std::string_view &msg) : msg_(msg) {} const char *what() const noexcept override { return msg_.c_str(); } private: std::string msg_; }; #ifndef SUPPORT_KNOWN_EXCEPTION #define THROW_IF(cond, msg) \ if (UNLIKELY(cond)) { \ LOG(FATAL, PANDAFILE) << (msg); \ } #else #define THROW_IF(cond, msg) \ if (UNLIKELY(cond)) { \ throw helpers::FileAccessException(msg); \ } #endif template inline auto Read(Span *sp) { constexpr size_t BYTE_WIDTH = std::numeric_limits::digits; constexpr size_t BITWIDTH = BYTE_WIDTH * width; using unsigned_type = panda::helpers::TypeHelperT; unsigned_type result = 0; THROW_IF(sp->Size() < width, INVALID_SPAN_OFFSET); for (size_t i = 0; i < width; i++) { unsigned_type tmp = static_cast((*sp)[i]) << (i * BYTE_WIDTH); result |= tmp; } *sp = sp->SubSpan(width); return result; } template <> inline auto Read(Span *sp) { uint16_t result = 0; THROW_IF(sp->Size() < sizeof(uint16_t), INVALID_SPAN_OFFSET); auto *p = sp->data(); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) result = *(p++); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic, hicpp-signed-bitwise) result |= static_cast(*p) << UINT_BYTE2_SHIFT; *sp = sp->SubSpan(sizeof(uint16_t)); return result; } template <> inline auto Read(Span *sp) { uint32_t result = 0; THROW_IF(sp->Size() < sizeof(uint32_t), INVALID_SPAN_OFFSET); auto *p = sp->data(); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) result = *(p++); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) result |= static_cast(*(p++)) << UINT_BYTE2_SHIFT; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) result |= static_cast(*(p++)) << UINT_BYTE3_SHIFT; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) result |= static_cast(*p) << UINT_BYTE4_SHIFT; *sp = sp->SubSpan(sizeof(uint32_t)); return result; } template inline auto Read(Span sp) { return Read(&sp); } inline uint32_t ReadULeb128(Span *sp) { THROW_IF(sp->Size() == 0U, INVALID_SPAN_OFFSET); uint32_t result; size_t n; bool is_full; std::tie(result, n, is_full) = leb128::DecodeUnsigned(sp->data()); THROW_IF(!is_full || sp->Size() < n, INVALID_SPAN_OFFSET); *sp = sp->SubSpan(n); return result; } inline void SkipULeb128(Span *sp) { if (sp->Size() > 0U && (*sp)[0U] <= leb128::PAYLOAD_MASK) { *sp = sp->SubSpan(1U); return; } if (sp->Size() > 1U && (*sp)[1U] <= leb128::PAYLOAD_MASK) { *sp = sp->SubSpan(2U); return; } if (sp->Size() > 2U && (*sp)[2U] <= leb128::PAYLOAD_MASK) { *sp = sp->SubSpan(3U); return; } if (sp->Size() > 3U && (*sp)[3U] <= leb128::PAYLOAD_MASK) { *sp = sp->SubSpan(4U); } } inline int32_t ReadLeb128(Span *sp) { uint32_t result; size_t n; bool is_full; std::tie(result, n, is_full) = leb128::DecodeSigned(sp->data()); THROW_IF(!is_full || sp->Size() < n, INVALID_SPAN_OFFSET); *sp = sp->SubSpan(n); return result; } template inline const uint8_t *Align(const uint8_t *ptr) { auto intptr = reinterpret_cast(ptr); uintptr_t aligned = (intptr - 1U + alignment) & -alignment; return reinterpret_cast(aligned); } template inline T Align(T n) { return (n - 1U + alignment) & -alignment; } template inline std::optional GetOptionalTaggedValue(Span sp, E tag, Span *next) { // NB! This is a workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80635 // which fails Release builds for GCC 8 and 9. std::optional novalue = {}; if (UNLIKELY(sp.Size() == 0U)) { return novalue; } if (sp[0] == static_cast(tag)) { sp = sp.SubSpan(1); T value = static_cast(Read(&sp)); *next = sp; return value; } *next = sp; return novalue; } template inline void EnumerateTaggedValues(Span sp, E tag, Callback cb, Span *next) { while (sp.Size() > 0U && sp[0] == static_cast(tag)) { sp = sp.SubSpan(1); T value(Read(&sp)); cb(value); } if (next == nullptr) { return; } *next = sp; } template inline bool EnumerateTaggedValuesWithEarlyStop(Span sp, E tag, Callback cb) { while (sp.Size() > 0U && sp[0] == static_cast(tag)) { sp = sp.SubSpan(1); T value(Read(&sp)); if (cb(value)) { return true; } } return false; } } // namespace panda::panda_file::helpers #endif // LIBPANDAFILE_HELPERS_H