current commit
260929.11 · deps: update lief to 1.0.0
/* Copyright 2017 - 2026 R. Thomas
 * Copyright 2017 - 2026 Quarkslab
 *
 * 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 LIEF_INTERNAL_UTILS_HEADER
#define LIEF_INTERNAL_UTILS_HEADER
#include <memory>
#include <string>
#include <vector>
#include <set>
#include <algorithm>
#include <unordered_map>
#include <sstream>
#include "spdlog/fmt/fmt.h"
#include "spdlog/fmt/ranges.h"

#include "LIEF/span.hpp"
#include "LIEF/optional.hpp"
#include "LIEF/errors.hpp"
#include "LIEF/iterators.hpp"


namespace LIEF {
std::string printable_string(const std::string& str);

inline bool is_printable(char c) {
  return ' ' <= c && c <= '~';
}

inline bool is_printable(const std::string& str) {
  return std::all_of(str.begin(), str.end(),
                     [](char c) { return is_printable(c); });
}

template<class T>
inline std::vector<std::remove_const_t<T>> as_vector(span<T> s) {
  return std::vector<T>(s.begin(), s.end());
}

template<class T>
inline std::vector<T> as_vector(span<const T> s) {
  return std::vector<T>(s.begin(), s.end());
}

template<class T>
inline const char* to_string_or(result<T> res, const char* defval = "???") {
  return res ? to_string(*res) : defval;
}

template<class T>
inline std::string to_string(const T& obj) {
  std::stringstream oss;
  oss << obj;
  return oss.str();
}


template<class T>
inline std::string to_hex(const T& container, size_t maxsize = 0) {
  if (container.empty()) {
    return "";
  }
  size_t count = maxsize;
  if (count == 0 || count > container.size()) {
    count = container.size();
  }
  std::string out;
  out.reserve(count * 2);
  for (size_t i = 0; i < count; ++i) {
    out += fmt::format("{:02x} ", container[i]);
  }
  if (count < container.size()) {
    out += "...";
  } else {
    out.pop_back(); // remove trailing ' '
  }
  return out;
}

template<typename HANDLER>
std::vector<std::string>
    optimize(const HANDLER& container,
             std::string (*getter)(const typename HANDLER::value_type&),
             size_t& offset_counter,
             std::unordered_map<std::string, size_t>* of_map_p = nullptr) {
  if (container.empty()) {
    return {};
  }

  std::set<std::string> string_table;
  std::vector<std::string> string_table_optimized;
  string_table_optimized.reserve(container.size());

  // reverse all symbol names and sort them so we can merge them in the linear
  // time: aaa, aadd, aaaa, cca, ca -> aaaa, aaa, acc, ac ddaa
  std::transform(container.begin(), container.end(),
                 std::inserter(string_table, string_table.end()), getter);

  for (const auto& val : string_table) {
    string_table_optimized.emplace_back(val);
    std::reverse(string_table_optimized.back().begin(),
                 string_table_optimized.back().end());
  }

  std::sort(string_table_optimized.begin(), string_table_optimized.end(),
            [](const std::string& lhs, const std::string& rhs) {
              bool ret = false;
              if (lhs.size() > rhs.size()) {
                auto res = lhs.compare(0, rhs.size(), rhs);
                ret = (res <= 0);
              } else {
                auto res = rhs.compare(0, lhs.size(), lhs);
                ret = (res > 0);
              }
              return ret;
            });

  // as all elements that can be merged are adjacent we can just go through the
  // list once and memorize ones we merged to calculate the offsets later
  std::unordered_map<std::string, std::string> merged_map;
  size_t to_set_idx = 0, cur_elm_idx = 1;
  for (; cur_elm_idx < string_table_optimized.size(); ++cur_elm_idx) {
    auto& cur_elm = string_table_optimized[cur_elm_idx];
    auto& to_set_elm = string_table_optimized[to_set_idx];
    if (to_set_elm.size() >= cur_elm.size()) {
      auto ret = to_set_elm.compare(0, cur_elm.size(), cur_elm);
      if (ret == 0) {
        // when memorizing reverse back symbol names
        std::string rev_cur_elm = cur_elm;
        std::string rev_to_set_elm = to_set_elm;
        std::reverse(rev_cur_elm.begin(), rev_cur_elm.end());
        std::reverse(rev_to_set_elm.begin(), rev_to_set_elm.end());
        merged_map[rev_cur_elm] = rev_to_set_elm;
        continue;
      }
    }
    ++to_set_idx;
    std::swap(string_table_optimized[to_set_idx], cur_elm);
  }
  // if the first one is empty
  if (string_table_optimized[0].empty()) {
    std::swap(string_table_optimized[0], string_table_optimized[to_set_idx]);
    --to_set_idx;
  }
  string_table_optimized.resize(to_set_idx + 1);

  // reverse symbols back and sort them again
  for (auto& val : string_table_optimized) {
    std::reverse(val.begin(), val.end());
  }
  std::sort(string_table_optimized.begin(), string_table_optimized.end());

  if (of_map_p != nullptr) {
    std::unordered_map<std::string, size_t>& offset_map = *of_map_p;
    offset_map[""] = 0;

    for (const auto& v : string_table_optimized) {
      if (!v.empty()) {
        offset_map[v] = offset_counter;
        offset_counter += v.size() + 1;
      }
    }
    for (const auto& kv : merged_map) {
      if (!kv.first.empty()) {
        offset_map[kv.first] =
            offset_map[kv.second] + (kv.second.size() - kv.first.size());
      }
    }
  }

  return string_table_optimized;
}

template<class T>
auto make_empty_iterator() {
  auto begin = std::make_unique<typename T::Iterator::implementation>();
  auto end = std::make_unique<typename T::Iterator::implementation>();
  return make_range<typename T::Iterator>(typename T::Iterator(std::move(begin)),
                                          typename T::Iterator(std::move(end)));
}

inline bool is_hex_number(const std::string& str) {
  return std::all_of(str.begin(), str.end(), ::isxdigit);
}

inline std::string hex_str(uint8_t c) {
  return fmt::format("{:02x}", c);
}

std::string hex_dump(const std::vector<uint8_t>& data,
                     const std::string& sep = ":");

std::string hex_dump(span<const uint8_t> data, const std::string& sep = ":");

std::string indent(const std::string& input, size_t level);

inline bool is_digit(char c) {
  return '0' <= c && c <= '9';
}

inline bool is_digit(const std::string& str) {
  return std::all_of(str.begin(), str.end(), (bool (*)(char))&is_digit);
}

inline bool is_digit(const char* str) {
  while (*str != 0) {
    if (!is_digit(*str)) {
      return false;
    }
    ++str;
  }
  return true;
}

inline uint32_t parse_android_version(const char* field, size_t size) {
  if (size < 4) {
    return 0;
  }
  if (!is_digit(field[0]) || !is_digit(field[1]) || !is_digit(field[2]) ||
      field[3] != '\0')
  {
    return 0;
  }
  return static_cast<uint32_t>(field[0] - '0') * 100 +
         static_cast<uint32_t>(field[1] - '0') * 10 +
         static_cast<uint32_t>(field[2] - '0');
}

std::string ts_to_str(uint64_t timestamp);

template<size_t N>
inline std::string uuid_to_str_impl(uint8_t (&uuid)[N]) {
  std::vector<std::string> hexstr;
  std::transform(uuid.begin(), uuid.end(), std::back_inserter(hexstr),
                 [](uint8_t x) { return fmt::format("{:02x}", x); });
  return fmt::to_string(fmt::join(hexstr, ":"));
}

template<size_t N>
inline std::string uuid_to_str_impl(const std::array<uint8_t, N>& uuid) {
  std::vector<std::string> hexstr;
  std::transform(uuid.begin(), uuid.end(), std::back_inserter(hexstr),
                 [](uint8_t x) { return fmt::format("{:02x}", x); });
  return fmt::to_string(fmt::join(hexstr, ":"));
}

inline bool endswith(const std::string& str, const std::string& suffix) {
  if (suffix.size() > str.size()) {
    return false;
  }
  return std::equal(suffix.rbegin(), suffix.rend(), str.rbegin());
}

inline optional<std::string> libname(const std::string& path, char sep = '/') {
  size_t pos = path.rfind(sep);
  if (pos == std::string::npos) {
    return nullopt();
  }
  return path.substr(pos + 1);
}

}

#endif