Coverage Report

Created: 2026-11-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/include/po/parser.h
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright The WasmEdge Authors
3
4
//===-- wasmedge/po/parser.h - Single Argument parser ---------------------===//
5
//
6
// Part of the WasmEdge Project.
7
//
8
//===----------------------------------------------------------------------===//
9
#pragma once
10
11
#include "po/error.h"
12
#include <algorithm>
13
#include <cstdint>
14
#include <fmt/format.h>
15
#include <string>
16
#include <type_traits>
17
#include <utility>
18
19
namespace WasmEdge {
20
namespace PO {
21
22
0
inline void tolower(std::string &String) noexcept {
23
0
  std::transform(
24
0
      String.begin(), String.end(), String.begin(),
25
0
      [](char C) noexcept { return static_cast<char>(std::tolower(C)); });
26
0
}
27
28
template <typename ConvResultT, typename ResultT = ConvResultT>
29
inline cxx20::expected<ResultT, Error>
30
stringToInteger(ConvResultT (&Conv)(const char *, char **, int),
31
166k
                std::string Value) noexcept {
32
166k
  using namespace std::literals;
33
166k
  char *EndPtr;
34
166k
  const char *CStr = Value.c_str();
35
166k
  auto SavedErrNo = std::exchange(errno, 0);
36
166k
  const auto Result = Conv(CStr, &EndPtr, 10);
37
166k
  std::swap(SavedErrNo, errno);
38
166k
  if (Value.empty() || *EndPtr != '\0') {
39
160
    return cxx20::unexpected<Error>(
40
160
        std::in_place, ErrCode::InvalidArgument,
41
160
        fmt::format("invalid integer value: {}"sv, Value));
42
160
  }
43
  // The unsigned conversions accept a minus sign and negate the value.
44
166k
  if (std::is_unsigned_v<ResultT> && Value.find('-') != std::string::npos) {
45
118
    return cxx20::unexpected<Error>(
46
118
        std::in_place, ErrCode::OutOfRange,
47
118
        fmt::format("integer value out of range: {}"sv, Value));
48
118
  }
49
166k
  auto InsideRange = [](auto WiderResult) constexpr noexcept {
50
166k
    using WiderResultT = decltype(WiderResult);
51
166k
    if constexpr (std::is_same_v<ResultT, WiderResultT>) {
52
166k
      return true;
53
166k
    } else {
54
0
      return static_cast<WiderResultT>(std::numeric_limits<ResultT>::min()) <=
55
0
                 WiderResult &&
56
0
             WiderResult <=
57
0
                 static_cast<WiderResultT>(std::numeric_limits<ResultT>::max());
58
0
    }
59
166k
  };
Unexecuted instantiation: auto WasmEdge::PO::stringToInteger<long, int>(long (&)(char const*, char**, int), std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)::{lambda(auto:1)#1}::operator()<long>(long) const
Unexecuted instantiation: auto WasmEdge::PO::stringToInteger<unsigned long, unsigned int>(unsigned long (&)(char const*, char**, int), std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)::{lambda(auto:1)#1}::operator()<unsigned long>(unsigned long) const
Unexecuted instantiation: auto WasmEdge::PO::stringToInteger<long, signed char>(long (&)(char const*, char**, int), std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)::{lambda(auto:1)#1}::operator()<long>(long) const
Unexecuted instantiation: auto WasmEdge::PO::stringToInteger<unsigned long, unsigned char>(unsigned long (&)(char const*, char**, int), std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)::{lambda(auto:1)#1}::operator()<unsigned long>(unsigned long) const
Unexecuted instantiation: auto WasmEdge::PO::stringToInteger<long, short>(long (&)(char const*, char**, int), std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)::{lambda(auto:1)#1}::operator()<long>(long) const
Unexecuted instantiation: auto WasmEdge::PO::stringToInteger<unsigned long, unsigned short>(unsigned long (&)(char const*, char**, int), std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)::{lambda(auto:1)#1}::operator()<unsigned long>(unsigned long) const
Unexecuted instantiation: auto WasmEdge::PO::stringToInteger<long, long>(long (&)(char const*, char**, int), std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)::{lambda(auto:1)#1}::operator()<long>(long) const
Unexecuted instantiation: auto WasmEdge::PO::stringToInteger<long long, long long>(long long (&)(char const*, char**, int), std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)::{lambda(auto:1)#1}::operator()<long long>(long long) const
auto WasmEdge::PO::stringToInteger<unsigned long, unsigned long>(unsigned long (&)(char const*, char**, int), std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)::{lambda(auto:1)#1}::operator()<unsigned long>(unsigned long) const
Line
Count
Source
49
166k
  auto InsideRange = [](auto WiderResult) constexpr noexcept {
50
166k
    using WiderResultT = decltype(WiderResult);
51
166k
    if constexpr (std::is_same_v<ResultT, WiderResultT>) {
52
166k
      return true;
53
    } else {
54
      return static_cast<WiderResultT>(std::numeric_limits<ResultT>::min()) <=
55
                 WiderResult &&
56
             WiderResult <=
57
                 static_cast<WiderResultT>(std::numeric_limits<ResultT>::max());
58
    }
59
166k
  };
Unexecuted instantiation: auto WasmEdge::PO::stringToInteger<unsigned long long, unsigned long long>(unsigned long long (&)(char const*, char**, int), std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)::{lambda(auto:1)#1}::operator()<unsigned long long>(unsigned long long) const
60
166k
  if (SavedErrNo == ERANGE || !InsideRange(Result)) {
61
21
    return cxx20::unexpected<Error>(
62
21
        std::in_place, ErrCode::OutOfRange,
63
21
        fmt::format("integer value out of range: {}"sv, Value));
64
21
  }
65
166k
  return static_cast<ResultT>(Result);
66
166k
}
Unexecuted instantiation: cxx20::expected<int, WasmEdge::PO::Error> WasmEdge::PO::stringToInteger<long, int>(long (&)(char const*, char**, int), std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Unexecuted instantiation: cxx20::expected<unsigned int, WasmEdge::PO::Error> WasmEdge::PO::stringToInteger<unsigned long, unsigned int>(unsigned long (&)(char const*, char**, int), std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Unexecuted instantiation: cxx20::expected<signed char, WasmEdge::PO::Error> WasmEdge::PO::stringToInteger<long, signed char>(long (&)(char const*, char**, int), std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Unexecuted instantiation: cxx20::expected<unsigned char, WasmEdge::PO::Error> WasmEdge::PO::stringToInteger<unsigned long, unsigned char>(unsigned long (&)(char const*, char**, int), std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Unexecuted instantiation: cxx20::expected<short, WasmEdge::PO::Error> WasmEdge::PO::stringToInteger<long, short>(long (&)(char const*, char**, int), std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Unexecuted instantiation: cxx20::expected<unsigned short, WasmEdge::PO::Error> WasmEdge::PO::stringToInteger<unsigned long, unsigned short>(unsigned long (&)(char const*, char**, int), std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Unexecuted instantiation: cxx20::expected<long, WasmEdge::PO::Error> WasmEdge::PO::stringToInteger<long, long>(long (&)(char const*, char**, int), std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Unexecuted instantiation: cxx20::expected<long long, WasmEdge::PO::Error> WasmEdge::PO::stringToInteger<long long, long long>(long long (&)(char const*, char**, int), std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
cxx20::expected<unsigned long, WasmEdge::PO::Error> WasmEdge::PO::stringToInteger<unsigned long, unsigned long>(unsigned long (&)(char const*, char**, int), std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Line
Count
Source
31
166k
                std::string Value) noexcept {
32
166k
  using namespace std::literals;
33
166k
  char *EndPtr;
34
166k
  const char *CStr = Value.c_str();
35
166k
  auto SavedErrNo = std::exchange(errno, 0);
36
166k
  const auto Result = Conv(CStr, &EndPtr, 10);
37
166k
  std::swap(SavedErrNo, errno);
38
166k
  if (Value.empty() || *EndPtr != '\0') {
39
160
    return cxx20::unexpected<Error>(
40
160
        std::in_place, ErrCode::InvalidArgument,
41
160
        fmt::format("invalid integer value: {}"sv, Value));
42
160
  }
43
  // The unsigned conversions accept a minus sign and negate the value.
44
166k
  if (std::is_unsigned_v<ResultT> && Value.find('-') != std::string::npos) {
45
118
    return cxx20::unexpected<Error>(
46
118
        std::in_place, ErrCode::OutOfRange,
47
118
        fmt::format("integer value out of range: {}"sv, Value));
48
118
  }
49
166k
  auto InsideRange = [](auto WiderResult) constexpr noexcept {
50
166k
    using WiderResultT = decltype(WiderResult);
51
166k
    if constexpr (std::is_same_v<ResultT, WiderResultT>) {
52
166k
      return true;
53
166k
    } else {
54
166k
      return static_cast<WiderResultT>(std::numeric_limits<ResultT>::min()) <=
55
166k
                 WiderResult &&
56
166k
             WiderResult <=
57
166k
                 static_cast<WiderResultT>(std::numeric_limits<ResultT>::max());
58
166k
    }
59
166k
  };
60
166k
  if (SavedErrNo == ERANGE || !InsideRange(Result)) {
61
21
    return cxx20::unexpected<Error>(
62
21
        std::in_place, ErrCode::OutOfRange,
63
21
        fmt::format("integer value out of range: {}"sv, Value));
64
21
  }
65
166k
  return static_cast<ResultT>(Result);
66
166k
}
Unexecuted instantiation: cxx20::expected<unsigned long long, WasmEdge::PO::Error> WasmEdge::PO::stringToInteger<unsigned long long, unsigned long long>(unsigned long long (&)(char const*, char**, int), std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
67
68
template <typename ConvResultT, typename ResultT = ConvResultT>
69
inline cxx20::expected<ResultT, Error>
70
stringToFloating(ConvResultT (&Conv)(const char *, char **),
71
0
                 std::string Value) noexcept {
72
0
  using namespace std::literals;
73
0
  char *EndPtr;
74
0
  const char *CStr = Value.c_str();
75
0
  auto SavedErrNo = std::exchange(errno, 0);
76
0
  const auto Result = Conv(CStr, &EndPtr);
77
0
  std::swap(SavedErrNo, errno);
78
0
  if (Value.empty() || *EndPtr != '\0') {
79
0
    return cxx20::unexpected<Error>(
80
0
        std::in_place, ErrCode::InvalidArgument,
81
0
        fmt::format("invalid floating-point value: {}"sv, Value));
82
0
  }
83
0
  if (SavedErrNo == ERANGE) {
84
0
    return cxx20::unexpected<Error>(
85
0
        std::in_place, ErrCode::OutOfRange,
86
0
        fmt::format("floating-point value out of range: {}"sv, Value));
87
0
  }
88
0
  return Result;
89
0
}
Unexecuted instantiation: cxx20::expected<float, WasmEdge::PO::Error> WasmEdge::PO::stringToFloating<float, float>(float (&)(char const*, char**), std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Unexecuted instantiation: cxx20::expected<double, WasmEdge::PO::Error> WasmEdge::PO::stringToFloating<double, double>(double (&)(char const*, char**), std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
Unexecuted instantiation: cxx20::expected<long double, WasmEdge::PO::Error> WasmEdge::PO::stringToFloating<long double, long double>(long double (&)(char const*, char**), std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)
90
91
template <typename T> struct Parser;
92
93
template <> struct Parser<std::string> {
94
1.42M
  static cxx20::expected<std::string, Error> parse(std::string Value) noexcept {
95
1.42M
    return Value;
96
1.42M
  }
97
};
98
99
template <> struct Parser<bool> {
100
0
  static cxx20::expected<bool, Error> parse(std::string Value) noexcept {
101
0
    using namespace std::literals;
102
0
    if (!Value.empty()) {
103
0
      switch (Value[0]) {
104
0
      case 'T':
105
0
      case 't':
106
0
        if (Value.size() == 4) {
107
0
          tolower(Value);
108
0
          if (Value == "true"sv) {
109
0
            return true;
110
0
          }
111
0
        }
112
0
        break;
113
0
      case '1':
114
0
        if (Value.size() == 1) {
115
0
          return true;
116
0
        }
117
0
        break;
118
0
      case 'F':
119
0
      case 'f':
120
0
        if (Value.size() == 5) {
121
0
          tolower(Value);
122
0
          if (Value == "false"sv) {
123
0
            return false;
124
0
          }
125
0
        }
126
0
        break;
127
0
      case '0':
128
0
        if (Value.size() == 1) {
129
0
          return false;
130
0
        }
131
0
        break;
132
0
      default:
133
0
        break;
134
0
      }
135
0
    }
136
0
    return cxx20::unexpected<Error>(std::in_place, ErrCode::InvalidArgument,
137
0
                                    "invalid boolean string: "s + Value);
138
0
  }
139
};
140
141
template <> struct Parser<int> {
142
0
  static cxx20::expected<int, Error> parse(std::string Value) noexcept {
143
0
    return stringToInteger<long, int>(std::strtol, std::move(Value));
144
0
  }
145
};
146
147
template <> struct Parser<unsigned int> {
148
  static cxx20::expected<unsigned int, Error>
149
0
  parse(std::string Value) noexcept {
150
0
    return stringToInteger<unsigned long, unsigned int>(std::strtoul,
151
0
                                                        std::move(Value));
152
0
  }
153
};
154
155
template <> struct Parser<signed char> {
156
0
  static cxx20::expected<signed char, Error> parse(std::string Value) noexcept {
157
0
    return stringToInteger<long, signed char>(std::strtol, std::move(Value));
158
0
  }
159
};
160
161
template <> struct Parser<unsigned char> {
162
  static cxx20::expected<unsigned char, Error>
163
0
  parse(std::string Value) noexcept {
164
0
    return stringToInteger<unsigned long, unsigned char>(std::strtoul,
165
0
                                                         std::move(Value));
166
0
  }
167
};
168
169
template <> struct Parser<short> {
170
0
  static cxx20::expected<short, Error> parse(std::string Value) noexcept {
171
0
    return stringToInteger<long, short>(std::strtol, std::move(Value));
172
0
  }
173
};
174
175
template <> struct Parser<unsigned short> {
176
  static cxx20::expected<unsigned short, Error>
177
0
  parse(std::string Value) noexcept {
178
0
    return stringToInteger<unsigned long, unsigned short>(std::strtoul,
179
0
                                                          std::move(Value));
180
0
  }
181
};
182
183
template <> struct Parser<long> {
184
0
  static cxx20::expected<long, Error> parse(std::string Value) noexcept {
185
0
    return stringToInteger(std::strtol, std::move(Value));
186
0
  }
187
};
188
189
template <> struct Parser<long long> {
190
0
  static cxx20::expected<long long, Error> parse(std::string Value) noexcept {
191
0
    return stringToInteger(std::strtoll, std::move(Value));
192
0
  }
193
};
194
195
template <> struct Parser<unsigned long> {
196
  static cxx20::expected<unsigned long, Error>
197
166k
  parse(std::string Value) noexcept {
198
166k
    return stringToInteger(std::strtoul, std::move(Value));
199
166k
  }
200
};
201
202
template <> struct Parser<unsigned long long> {
203
  static cxx20::expected<unsigned long long, Error>
204
0
  parse(std::string Value) noexcept {
205
0
    return stringToInteger(std::strtoull, std::move(Value));
206
0
  }
207
};
208
209
template <> struct Parser<float> {
210
0
  static cxx20::expected<float, Error> parse(std::string Value) noexcept {
211
0
    return stringToFloating(std::strtof, std::move(Value));
212
0
  }
213
};
214
215
template <> struct Parser<double> {
216
0
  static cxx20::expected<double, Error> parse(std::string Value) noexcept {
217
0
    return stringToFloating(std::strtod, std::move(Value));
218
0
  }
219
};
220
221
template <> struct Parser<long double> {
222
0
  static cxx20::expected<long double, Error> parse(std::string Value) noexcept {
223
0
    return stringToFloating(std::strtold, std::move(Value));
224
0
  }
225
};
226
227
} // namespace PO
228
} // namespace WasmEdge