Program Listing for File utils.h

Return to documentation for file (rcdiscover/utils.h)

/*
 * rcdiscover - the network discovery tool for Roboception devices
 *
 * Copyright (c) 2017 Roboception GmbH
 * All rights reserved
 *
 * Author: Raphael Schaller
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef RCDISCOVER_UTILS_H
#define RCDISCOVER_UTILS_H

#include <cstdint>
#include <iomanip>
#include <sstream>
#include <stdexcept>
#include <algorithm>
#include <array>

inline std::string mac2string(const uint64_t mac)
{
  std::ostringstream out;

  out << std::hex << std::setfill('0');
  out << std::setw(2) << ((mac>>40)&0xff) << ':' << std::setw(2) << ((mac>>32)&0xff) << ':'
      << std::setw(2) << ((mac>>24)&0xff) << ':' << std::setw(2) << ((mac>>16)&0xff) << ':'
      << std::setw(2) << ((mac>>8)&0xff) << ':' << std::setw(2) << (mac&0xff);

  return out.str();
}

inline std::string ip2string(const uint32_t ip)
{
  std::ostringstream out;

  out << ((ip>>24)&0xff) << '.' << ((ip>>16)&0xff) << '.'
      << ((ip>>8)&0xff) << '.' << (ip&0xff);

  return out.str();
}

template<uint32_t n>
std::array<std::string, n> split(const std::string& s, const char sep)
{
  std::array<std::string, n> result;

  if (!s.empty())
  {
    if (s.front() == sep || s.back() == sep)
    {
      throw std::invalid_argument("strings starts or ends with separator");
    }
  }

  std::istringstream iss(s);
  for (uint32_t i = 0; i < n; ++i)
  {
    if (!std::getline(iss, result[i], sep))
    {
      throw std::out_of_range("n");
    }
  }

  std::string tmp;
  if (std::getline(iss, tmp, sep))
  {
    throw std::out_of_range("n");
  }

  return result;
}

template<uint32_t n>
std::array<uint8_t, n> string2byte(const std::string& s,
                                   const int base,
                                   const char sep)
{
  const auto splitted = split<n>(s, sep);

  std::array<uint8_t, n> result;

  std::transform(std::begin(splitted),
                 std::end(splitted),
                 std::begin(result),
                 [&base](const std::string& s) -> std::uint8_t
  {
    const auto v = std::stoul(s, nullptr, base);
    if (v > 255)
    {
      throw std::out_of_range("number is larger than 255");
    }
    return static_cast<std::uint8_t>(v);
  });

  return result;
}

inline std::array<uint8_t, 6> string2mac(const std::string& mac)
{
  return string2byte<6>(mac, 16, ':');
}

inline std::array<uint8_t, 4> string2ip(const std::string& ip)
{
  return string2byte<4>(ip, 10, '.');
}

template<std::size_t N> struct MinFittingType { };
template<> struct MinFittingType<1> { using type = std::uint8_t; };
template<> struct MinFittingType<2> { using type = std::uint16_t; };
template<> struct MinFittingType<3> { using type = std::uint32_t; };
template<> struct MinFittingType<4> { using type = std::uint32_t; };
template<> struct MinFittingType<5> { using type = std::uint64_t; };
template<> struct MinFittingType<6> { using type = std::uint64_t; };
template<> struct MinFittingType<7> { using type = std::uint64_t; };
template<> struct MinFittingType<8> { using type = std::uint64_t; };

template<std::size_t N>
typename MinFittingType<N>::type
byteArrayToInt(const std::array<std::uint8_t, N> &a)
{
  using ReturnType = typename MinFittingType<N>::type;
  ReturnType result{};
  for (std::size_t i = 0; i < N; ++i)
  {
    result |= (static_cast<ReturnType>(a[i]) << ((N - 1 - i) * 8));
  }
  return result;
}

inline bool wildcardMatch(std::string::const_iterator str_first,
                          std::string::const_iterator str_last,
                          std::string::const_iterator p_first,
                          std::string::const_iterator p_last)
{
  if (str_first == str_last && p_first == p_last)
  { return true; }

  if (str_first == str_last)
  {
    if (*p_first == '*')
    {
      // if there is no more character after * => match
      return std::next(p_first) == p_last;
    }
  }

  if (p_first == p_last)
  {
    return false;
  }

  if (*p_first == '?' || std::tolower(*p_first) == std::tolower(*str_first))
  {
    return wildcardMatch(std::next(str_first), str_last,
                         std::next(p_first), p_last);
  }

  if (*p_first == '*')
  {
    return wildcardMatch(std::next(str_first), str_last, p_first, p_last) ||
           wildcardMatch(str_first, str_last, std::next(p_first), p_last);
  }

  return false;
}

#endif // RCDISCOVER_UTILS_H