aboutsummaryrefslogtreecommitdiff
path: root/include/cru/common/StringToNumberConverter.h
blob: 758b26c87d273c2def480463d2edd400c71a7d16 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#pragma once
#include "Base.h"
#include "Bitmask.h"

#include <cstddef>
#include <ostream>

namespace cru {
namespace details {
struct StringToNumberFlagTag {};
}  // namespace details

using StringToNumberFlag = Bitmask<details::StringToNumberFlagTag>;

struct StringToNumberFlags {
  constexpr static StringToNumberFlag kAllowLeadingSpaces =
      StringToNumberFlag::FromOffset(0);
  constexpr static StringToNumberFlag kAllowTrailingSpaces =
      StringToNumberFlag::FromOffset(1);
  constexpr static StringToNumberFlag kAllowTrailingJunk =
      StringToNumberFlag::FromOffset(2);
  constexpr static StringToNumberFlag kAllowLeadingZeroForInteger =
      StringToNumberFlag::FromOffset(3);
  constexpr static StringToNumberFlag kThrowOnError =
      StringToNumberFlag::FromOffset(4);
};

template <typename TResult>
struct IStringToNumberConverter : virtual Interface {
  virtual TResult Parse(const char* str, Index size,
                        Index* processed_characters_count) const = 0;

  template <std::size_t Size>
  TResult Parse(const char (&str)[Size],
                Index* processed_characters_count) const {
    return Parse(str, Size - 1, processed_characters_count);
  }
};

struct CRU_BASE_API StringToIntegerResult {
  StringToIntegerResult() = default;
  StringToIntegerResult(bool negate, unsigned long long value)
      : negate(negate), value(value) {}

  bool negate;
  unsigned long long value;
};

inline bool CRU_BASE_API operator==(const StringToIntegerResult& left,
                                    const StringToIntegerResult& right) {
  return left.negate == right.negate && left.value == right.value;
}

inline bool CRU_BASE_API operator!=(const StringToIntegerResult& left,
                                    const StringToIntegerResult& right) {
  return !(left == right);
}

inline std::ostream& CRU_BASE_API
operator<<(std::ostream& stream, const StringToIntegerResult& result) {
  return stream << "StringToIntegerConverterImplResult("
                << (result.negate ? "-" : "") << result.value << ")";
}

/**
 * \brief A converter that convert number into long long.
 */
struct CRU_BASE_API StringToIntegerConverter
    : IStringToNumberConverter<StringToIntegerResult> {
 public:
  explicit StringToIntegerConverter(StringToNumberFlag flags, int base = 0)
      : flags(flags), base(base) {}

  bool CheckParams() const;

  /**
   * \brief Convert string to long long.
   * \param str The string to convert.
   * \param size The size of the string.
   * \param processed_characters_count The number of characters that were
   * processed. Or nullptr to not retrieve.
   */
  StringToIntegerResult Parse(const char* str, Index size,
                              Index* processed_characters_count) const override;
  using IStringToNumberConverter<StringToIntegerResult>::Parse;

  StringToNumberFlag flags;
  /**
   * \brief The base of the number used for parse or 0 for auto detect.
   * \remarks Base can only be of range [2, 36] or 0. If base is 0, decimal is
   * assumed by default ,or if str is started with "0x" or "0X" hexadecimal is
   * assumed, or if str is started with a single "0" octoral is assumed, or if
   * str is started with "0b" or "0B" binary is assumed. Otherwise it is an
   * error.
   */
  int base;
};

struct CRU_BASE_API StringToFloatConverter {
  StringToFloatConverter(StringToNumberFlag flags) : flags(flags) {}

  double Parse(const char* str, Index size,
               Index* processed_characters_count) const;

  StringToNumberFlag flags;
};
}  // namespace cru