Skip to content
This repository was archived by the owner on Apr 8, 2026. It is now read-only.

Commit 3b05ee3

Browse files
committed
tools: Add skip_space_iterator struct
This restores the option to filter-out whitespace before parsing hex. This is useful e.g. when loading hex from a file.
1 parent 6b07fa7 commit 3b05ee3

5 files changed

Lines changed: 152 additions & 5 deletions

File tree

test/unittests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ add_executable(
2323
loader_mock.h
2424
loader_test.cpp
2525
mocked_host_test.cpp
26+
skip_space_iterator_test.cpp
2627
tooling_test.cpp
2728
hex_test.cpp
2829
)
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// EVMC: Ethereum Client-VM Connector API.
2+
// Copyright 2022 The EVMC Authors.
3+
// Licensed under the Apache License, Version 2.0.
4+
5+
#include <tools/evmc/skip_space_iterator.hpp>
6+
#include <gtest/gtest.h>
7+
8+
using evmc::skip_space_iterator;
9+
10+
namespace
11+
{
12+
std::string remove_space(std::string_view in)
13+
{
14+
// Copy input to additional buffer. This helps with out-of-buffer reads detection by sanitizers.
15+
const auto in_buffer = std::make_unique<char[]>(in.size());
16+
const auto begin = in_buffer.get();
17+
const auto end = begin + in.size();
18+
std::copy(in.begin(), in.end(), begin);
19+
20+
// Filter the input.
21+
std::string out;
22+
std::copy(skip_space_iterator{begin, end}, skip_space_iterator{end, end},
23+
std::back_inserter(out));
24+
return out;
25+
}
26+
} // namespace
27+
28+
TEST(skip_space_iterator, empty)
29+
{
30+
EXPECT_EQ(remove_space(""), "");
31+
EXPECT_EQ(remove_space(" "), "");
32+
EXPECT_EQ(remove_space(" "), "");
33+
}
34+
35+
TEST(skip_space_iterator, filter_middle)
36+
{
37+
EXPECT_EQ(remove_space("x y"), "xy");
38+
EXPECT_EQ(remove_space("x y"), "xy");
39+
}
40+
41+
TEST(skip_space_iterator, filter_front)
42+
{
43+
EXPECT_EQ(remove_space(" x"), "x");
44+
EXPECT_EQ(remove_space(" x"), "x");
45+
}
46+
47+
TEST(skip_space_iterator, filter_back)
48+
{
49+
EXPECT_EQ(remove_space("x "), "x");
50+
EXPECT_EQ(remove_space("x "), "x");
51+
}
52+
53+
TEST(skip_space_iterator, filter_mixed)
54+
{
55+
EXPECT_EQ(remove_space(" x y z "), "xyz");
56+
EXPECT_EQ(remove_space(" x y z "), "xyz");
57+
}
58+
59+
TEST(skip_space_iterator, isspace)
60+
{
61+
// Test internal isspace() compliance with std::isspace().
62+
// The https://en.cppreference.com/w/cpp/string/byte/isspace has the list of "space" characters.
63+
64+
for (int i = int{std::numeric_limits<char>::min()}; i <= std::numeric_limits<char>::max(); ++i)
65+
{
66+
const auto c = static_cast<char>(i);
67+
EXPECT_EQ(evmc::isspace(c), (std::isspace(c) != 0));
68+
switch (c)
69+
{
70+
case ' ':
71+
case '\f':
72+
case '\n':
73+
case '\r':
74+
case '\t':
75+
case '\v':
76+
EXPECT_TRUE(evmc::isspace(c));
77+
break;
78+
default:
79+
EXPECT_FALSE(evmc::isspace(c));
80+
break;
81+
}
82+
}
83+
}

tools/evmc/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
hunter_add_package(CLI11)
66
find_package(CLI11 REQUIRED)
77

8-
add_executable(evmc-tool main.cpp)
8+
add_executable(evmc-tool main.cpp skip_space_iterator.hpp)
99
add_executable(evmc::tool ALIAS evmc-tool)
1010
set_target_properties(evmc-tool PROPERTIES OUTPUT_NAME evmc)
1111
set_source_files_properties(main.cpp PROPERTIES

tools/evmc/main.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Copyright 2019-2020 The EVMC Authors.
33
// Licensed under the Apache License, Version 2.0.
44

5+
#include "skip_space_iterator.hpp"
56
#include <CLI/CLI.hpp>
67
#include <evmc/hex.hpp>
78
#include <evmc/loader.h>
@@ -18,11 +19,13 @@ evmc::bytes load_from_hex(const std::string& str)
1819
if (str[0] == '@') // The argument is file path.
1920
{
2021
std::ifstream file{str.c_str() + 1};
21-
std::string content{std::istreambuf_iterator<char>{file}, std::istreambuf_iterator<char>{}};
22-
auto o = evmc::from_hex(content);
23-
if (!o)
22+
const std::istreambuf_iterator<char> file_begin{file};
23+
const std::istreambuf_iterator<char> file_end;
24+
evmc::bytes out;
25+
if (!evmc::from_hex(evmc::skip_space_iterator{file_begin, file_end},
26+
evmc::skip_space_iterator{file_end, file_end}, std::back_inserter(out)))
2427
throw std::invalid_argument{"invalid hex"};
25-
return std::move(*o);
28+
return out;
2629
}
2730

2831
return evmc::from_hex(str).value(); // Should be validated already.

tools/evmc/skip_space_iterator.hpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// EVMC: Ethereum Client-VM Connector API.
2+
// Copyright 2022 The EVMC Authors.
3+
// Licensed under the Apache License, Version 2.0.
4+
#pragma once
5+
6+
#include <iterator>
7+
8+
namespace evmc
9+
{
10+
/// The constexpr variant of std::isspace().
11+
inline constexpr bool isspace(char ch) noexcept
12+
{
13+
// Implementation taken from LLVM's libc.
14+
return ch == ' ' || (static_cast<unsigned>(ch) - '\t') < 5;
15+
}
16+
17+
/// The input filter iterator which skips whitespace characters from the base input iterator.
18+
template <typename BaseIterator>
19+
struct skip_space_iterator
20+
{
21+
using difference_type = typename std::iterator_traits<BaseIterator>::difference_type;
22+
using value_type = typename std::iterator_traits<BaseIterator>::value_type;
23+
using pointer = typename std::iterator_traits<BaseIterator>::pointer;
24+
using reference = typename std::iterator_traits<BaseIterator>::reference;
25+
using iterator_category = std::input_iterator_tag;
26+
27+
private:
28+
BaseIterator base;
29+
BaseIterator base_end;
30+
value_type value;
31+
32+
constexpr void forward_to_next_value()
33+
{
34+
for (; base != base_end; ++base)
35+
{
36+
value = *base;
37+
if (isspace(value) == 0)
38+
break;
39+
}
40+
}
41+
42+
public:
43+
constexpr skip_space_iterator(BaseIterator it, BaseIterator end) noexcept
44+
: base{it}, base_end{end}
45+
{
46+
forward_to_next_value();
47+
}
48+
49+
constexpr auto operator*() { return value; }
50+
51+
constexpr void operator++()
52+
{
53+
++base;
54+
forward_to_next_value();
55+
}
56+
57+
constexpr bool operator!=(const skip_space_iterator& o) { return base != o.base; }
58+
constexpr bool operator==(const skip_space_iterator& o) { return base == o.base; }
59+
};
60+
} // namespace evmc

0 commit comments

Comments
 (0)