Skip to content

Commit

Permalink
Add scan_int and scan_int_exhaustive_valid
Browse files Browse the repository at this point in the history
  • Loading branch information
eliaskosunen committed Nov 5, 2023
1 parent 66554b8 commit f5bbe80
Show file tree
Hide file tree
Showing 12 changed files with 477 additions and 6 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ set(SCN_PRIVATE_SOURCES
src/scn/vscan_impl_ios.cpp
src/scn/vscan_impl_erased.cpp

src/scn/vscan_int.cpp

src/scn/impl/locale.cpp

src/scn/impl/algorithms/find_whitespace.cpp
Expand Down
31 changes: 31 additions & 0 deletions benchmark/runtime/integer/repeated.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,37 @@ BENCHMARK_TEMPLATE(scan_int_repeated_scn_value, int);
BENCHMARK_TEMPLATE(scan_int_repeated_scn_value, long long);
BENCHMARK_TEMPLATE(scan_int_repeated_scn_value, unsigned);

template <typename Int>
static void scan_int_repeated_scn_int(benchmark::State& state)
{
repeated_state<Int> s{get_integer_string<Int>()};

for (auto _ : state) {
s.skip_classic_ascii_space();
auto sv = std::string_view{s.view().data(), s.view().size()};

auto result = scn::scan_int<Int>(sv);

if (!result) {
if (result.error() == scn::scan_error::end_of_range) {
s.reset();
}
else {
state.SkipWithError("Scan error");
break;
}
}
else {
s.push(result->value());
s.it = scn::detail::to_address(result->begin());
}
}
state.SetBytesProcessed(s.get_bytes_processed(state));
}
BENCHMARK_TEMPLATE(scan_int_repeated_scn_int, int);
BENCHMARK_TEMPLATE(scan_int_repeated_scn_int, long long);
BENCHMARK_TEMPLATE(scan_int_repeated_scn_int, unsigned);

template <typename Int>
static void scan_int_repeated_sstream(benchmark::State& state)
{
Expand Down
39 changes: 39 additions & 0 deletions benchmark/runtime/integer/single.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,45 @@ BENCHMARK_TEMPLATE(scan_int_single_scn_value, int);
BENCHMARK_TEMPLATE(scan_int_single_scn_value, long long);
BENCHMARK_TEMPLATE(scan_int_single_scn_value, unsigned);

template <typename Int>
static void scan_int_single_scn_int(benchmark::State& state)
{
single_state<Int> s{get_integer_list<Int>()};

for (auto _ : state) {
s.reset_if_necessary();

if (auto result = scn::scan_int<Int>(*s.it); !result) {
state.SkipWithError("Benchmark errored");
break;
}
else {
s.push(result->value());
}
}
state.SetBytesProcessed(s.get_bytes_processed(state));
}
BENCHMARK_TEMPLATE(scan_int_single_scn_int, int);
BENCHMARK_TEMPLATE(scan_int_single_scn_int, long long);
BENCHMARK_TEMPLATE(scan_int_single_scn_int, unsigned);

template <typename Int>
static void scan_int_single_scn_int_exhaustive_valid(benchmark::State& state)
{
single_state<Int> s{get_integer_list<Int>()};

for (auto _ : state) {
s.reset_if_necessary();

auto val = scn::scan_int_exhaustive_valid<Int>(*s.it);
s.push(val);
}
state.SetBytesProcessed(s.get_bytes_processed(state));
}
BENCHMARK_TEMPLATE(scan_int_single_scn_int_exhaustive_valid, int);
BENCHMARK_TEMPLATE(scan_int_single_scn_int_exhaustive_valid, long long);
BENCHMARK_TEMPLATE(scan_int_single_scn_int_exhaustive_valid, unsigned);

template <typename Int>
static void scan_int_single_sstream(benchmark::State& state)
{
Expand Down
1 change: 1 addition & 0 deletions include/scn/detail/erased_range.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <scn/util/span.h>

#include <memory>
#include <array>

namespace scn {
SCN_BEGIN_NAMESPACE
Expand Down
55 changes: 55 additions & 0 deletions include/scn/detail/scan.h
Original file line number Diff line number Diff line change
Expand Up @@ -325,5 +325,60 @@ namespace scn {

#endif // SCN_USE_IOSTREAMS

namespace detail {
template <typename T>
inline constexpr bool is_scan_int_type =
std::is_integral_v<T> && !std::is_same_v<T, char> &&
!std::is_same_v<T, wchar_t> && !std::is_same_v<T, char32_t> &&
!std::is_same_v<T, bool>;
}

/**
* Fast integer reading.
*
* Quickly reads an integer from a std::string_view. Skips preceding
* whitespace.
*
* Reads in the specified base,
* allowing a base prefix. Set `base` to `0` to detect the base from the
* input. `base` must either be `0`, or in range `[2, 36]`.
*/
template <typename T,
std::enable_if_t<detail::is_scan_int_type<T>>* = nullptr>
SCN_NODISCARD auto scan_int(std::string_view source, int base = 10)
-> scan_result_type<std::string_view, T>
{
T value{};
SCN_TRY(it, detail::scan_int_impl(source, value, base));
return scan_result{ranges::subrange{it, source.end()},
std::tuple{value}};
}

/**
* Very fast integer reading.
*
* Quickly reads an integer from a std::string_view.
*
* Be very careful when using this one!
* Its speed comes from some very heavy assumptions about the validity of
* the input:
* - `source` must not be empty.
* - `source` contains nothing but the integer: no leading or trailing
* whitespace, no extra junk. Leading `-` is allowed for signed types,
* no `+` is allowed.
* - The parsed value does not overflow.
* - The input is a valid base-10 integer.
* Breaking these assumptions will lead to UB.
*/
template <typename T,
std::enable_if_t<detail::is_scan_int_type<T>>* = nullptr>
SCN_NODISCARD auto scan_int_exhaustive_valid(std::string_view source) -> T
{
static_assert(
!SCN_IS_BIG_ENDIAN,
"scan_int_exhaustive_valid requires a little endian environment");
return detail::scan_int_exhaustive_valid_impl<T>(source);
}

SCN_END_NAMESPACE
} // namespace scn
91 changes: 91 additions & 0 deletions include/scn/detail/vscan.h
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,97 @@ namespace scn {
} // namespace detail
#endif

namespace detail {
template <typename T>
auto scan_int_impl(std::string_view source, T& value, int base)
-> scan_expected<std::string_view::iterator>;

template <typename T>
auto scan_int_exhaustive_valid_impl(std::string_view source) -> T;

#if !SCN_DISABLE_TYPE_SCHAR
extern template auto scan_int_impl(std::string_view source,
signed char& value,
int base)
-> scan_expected<std::string_view::iterator>;
extern template auto scan_int_exhaustive_valid_impl(std::string_view)
-> signed char;
#endif
#if !SCN_DISABLE_TYPE_SHORT
extern template auto scan_int_impl(std::string_view source,
short& value,
int base)
-> scan_expected<std::string_view::iterator>;
extern template auto scan_int_exhaustive_valid_impl(std::string_view)
-> short;
#endif
#if !SCN_DISABLE_TYPE_INT
extern template auto scan_int_impl(std::string_view source,
int& value,
int base)
-> scan_expected<std::string_view::iterator>;
extern template auto scan_int_exhaustive_valid_impl(std::string_view)
-> int;
#endif
#if !SCN_DISABLE_TYPE_LONG
extern template auto scan_int_impl(std::string_view source,
long& value,
int base)
-> scan_expected<std::string_view::iterator>;
extern template auto scan_int_exhaustive_valid_impl(std::string_view)
-> long;
#endif
#if !SCN_DISABLE_TYPE_LONG_LONG
extern template auto scan_int_impl(std::string_view source,
long long& value,
int base)
-> scan_expected<std::string_view::iterator>;
extern template auto scan_int_exhaustive_valid_impl(std::string_view)
-> long long;
#endif
#if !SCN_DISABLE_TYPE_UCHAR
extern template auto scan_int_impl(std::string_view source,
unsigned char& value,
int base)
-> scan_expected<std::string_view::iterator>;
extern template auto scan_int_exhaustive_valid_impl(std::string_view)
-> unsigned char;
#endif
#if !SCN_DISABLE_TYPE_USHORT
extern template auto scan_int_impl(std::string_view source,
unsigned short& value,
int base)
-> scan_expected<std::string_view::iterator>;
extern template auto scan_int_exhaustive_valid_impl(std::string_view)
-> unsigned short;
#endif
#if !SCN_DISABLE_TYPE_UINT
extern template auto scan_int_impl(std::string_view source,
unsigned int& value,
int base)
-> scan_expected<std::string_view::iterator>;
extern template auto scan_int_exhaustive_valid_impl(std::string_view)
-> unsigned int;
#endif
#if !SCN_DISABLE_TYPE_ULONG
extern template auto scan_int_impl(std::string_view source,
unsigned long& value,
int base)
-> scan_expected<std::string_view::iterator>;
extern template auto scan_int_exhaustive_valid_impl(std::string_view)
-> unsigned long;
#endif
#if !SCN_DISABLE_TYPE_ULONG_LONG
extern template auto scan_int_impl(std::string_view source,
unsigned long long& value,
int base)
-> scan_expected<std::string_view::iterator>;
extern template auto scan_int_exhaustive_valid_impl(std::string_view)
-> unsigned long long;
#endif

} // namespace detail

SCN_GCC_POP // -Wnoexcept

SCN_END_NAMESPACE
Expand Down
71 changes: 71 additions & 0 deletions src/scn/impl/reader/integer_reader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -483,49 +483,120 @@ namespace scn {
return it;
}

template <typename T>
void parse_int_value_exhaustive_valid(std::string_view source, T& value)
{
SCN_EXPECT(!source.empty());

bool negative_sign = false;
if constexpr (std::is_signed_v<T>) {
if (source.front() == '-') {
source = source.substr(1);
negative_sign = true;
}
}
SCN_EXPECT(!source.empty());
SCN_EXPECT(numeric_reader_base::char_to_int(source.front()) < 10);

for (; source.size() >= 8; source = source.substr(8)) {
uint64_t word{};
std::memcpy(&word, source.data(), 8);

for (char ch : source.substr(0, 8)) {
SCN_EXPECT(numeric_reader_base::char_to_int(ch) < 10);
}

// See above, do_read_decimal_fast64

constexpr uint64_t mask = 0x000000FF000000FFull;
constexpr uint64_t mul1 = 100 + (1000000ull << 32);
constexpr uint64_t mul2 = 1 + (10000ull << 32);

word -= 0x3030303030303030ull;
word = (word * 10) + (word >> 8);
word =
(((word & mask) * mul1) + (((word >> 16) & mask) * mul2)) >>
32;

value *= power_of_10(8);
value += word;
}

for (auto ch : source) {
auto digit = numeric_reader_base::char_to_int(ch);
SCN_EXPECT(digit < 10);
value *= 10;
value += digit;
}

if constexpr (std::is_signed_v<T>) {
if (negative_sign) {
value = -value;
}
}
}

#define SCN_DEFINE_INTEGER_READER_TEMPLATE(CharT, IntT) \
template auto integer_reader<CharT>::parse_value_impl(IntT&) \
-> scan_expected<ranges::iterator_t<std::basic_string_view<CharT>>>;

#if !SCN_DISABLE_TYPE_SCHAR
SCN_DEFINE_INTEGER_READER_TEMPLATE(char, signed char)
SCN_DEFINE_INTEGER_READER_TEMPLATE(wchar_t, signed char)
template void parse_int_value_exhaustive_valid(std::string_view,
signed char&);
#endif
#if !SCN_DISABLE_TYPE_SHORT
SCN_DEFINE_INTEGER_READER_TEMPLATE(char, short)
SCN_DEFINE_INTEGER_READER_TEMPLATE(wchar_t, short)
template void parse_int_value_exhaustive_valid(std::string_view,
short&);
#endif
#if !SCN_DISABLE_TYPE_INT
SCN_DEFINE_INTEGER_READER_TEMPLATE(char, int)
SCN_DEFINE_INTEGER_READER_TEMPLATE(wchar_t, int)
template void parse_int_value_exhaustive_valid(std::string_view, int&);
#endif
#if !SCN_DISABLE_TYPE_LONG
SCN_DEFINE_INTEGER_READER_TEMPLATE(char, long)
SCN_DEFINE_INTEGER_READER_TEMPLATE(wchar_t, long)
template void parse_int_value_exhaustive_valid(std::string_view, long&);
#endif
#if !SCN_DISABLE_TYPE_LONG_LONG
SCN_DEFINE_INTEGER_READER_TEMPLATE(char, long long)
SCN_DEFINE_INTEGER_READER_TEMPLATE(wchar_t, long long)
template void parse_int_value_exhaustive_valid(std::string_view,
long long&);
#endif
#if !SCN_DISABLE_TYPE_UCHAR
SCN_DEFINE_INTEGER_READER_TEMPLATE(char, unsigned char)
SCN_DEFINE_INTEGER_READER_TEMPLATE(wchar_t, unsigned char)
template void parse_int_value_exhaustive_valid(std::string_view,
unsigned char&);
#endif
#if !SCN_DISABLE_TYPE_USHORT
SCN_DEFINE_INTEGER_READER_TEMPLATE(char, unsigned short)
SCN_DEFINE_INTEGER_READER_TEMPLATE(wchar_t, unsigned short)
template void parse_int_value_exhaustive_valid(std::string_view,
unsigned short&);
#endif
#if !SCN_DISABLE_TYPE_UINT
SCN_DEFINE_INTEGER_READER_TEMPLATE(char, unsigned int)
SCN_DEFINE_INTEGER_READER_TEMPLATE(wchar_t, unsigned int)
template void parse_int_value_exhaustive_valid(std::string_view,
unsigned int&);
#endif
#if !SCN_DISABLE_TYPE_ULONG
SCN_DEFINE_INTEGER_READER_TEMPLATE(char, unsigned long)
SCN_DEFINE_INTEGER_READER_TEMPLATE(wchar_t, unsigned long)
template void parse_int_value_exhaustive_valid(std::string_view,
unsigned long&);
#endif
#if !SCN_DISABLE_TYPE_ULONG_LONG
SCN_DEFINE_INTEGER_READER_TEMPLATE(char, unsigned long long)
SCN_DEFINE_INTEGER_READER_TEMPLATE(wchar_t, unsigned long long)
template void parse_int_value_exhaustive_valid(std::string_view,
unsigned long long&);
#endif

#undef SCN_DEFINE_INTEGER_READER_TEMPLATE
Expand Down
12 changes: 9 additions & 3 deletions src/scn/impl/reader/integer_reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -386,9 +386,15 @@ namespace scn {
bool m_zero_parsed{false}, m_base_prefix_parsed{false};
};

#define SCN_DECLARE_INTEGER_READER_TEMPLATE(CharT, IntT) \
extern template auto integer_reader<CharT>::parse_value_impl(IntT&) \
-> scan_expected<ranges::iterator_t<std::basic_string_view<CharT>>>;
template <typename T>
void parse_int_value_exhaustive_valid(std::string_view source,
T& value);

#define SCN_DECLARE_INTEGER_READER_TEMPLATE(CharT, IntT) \
extern template auto integer_reader<CharT>::parse_value_impl(IntT&) \
-> scan_expected<ranges::iterator_t<std::basic_string_view<CharT>>>; \
extern template void parse_int_value_exhaustive_valid(std::string_view, \
IntT&);

#if !SCN_DISABLE_TYPE_SCHAR
SCN_DECLARE_INTEGER_READER_TEMPLATE(char, signed char)
Expand Down
Loading

0 comments on commit f5bbe80

Please sign in to comment.