Skip to content

Commit

Permalink
tools: Add skip_space_iterator struct
Browse files Browse the repository at this point in the history
This restores the option to filter-out whitespace before parsing hex.
This is useful e.g. when loading hex from a file.
  • Loading branch information
chfast committed May 26, 2022
1 parent 866ce45 commit 6806151
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 5 deletions.
1 change: 1 addition & 0 deletions test/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ add_executable(
loader_mock.h
loader_test.cpp
mocked_host_test.cpp
skip_space_iterator_test.cpp
tooling_test.cpp
hex_test.cpp
)
Expand Down
83 changes: 83 additions & 0 deletions test/unittests/skip_space_iterator_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// EVMC: Ethereum Client-VM Connector API.
// Copyright 2022 The EVMC Authors.
// Licensed under the Apache License, Version 2.0.

#include <tools/evmc/skip_space_iterator.hpp>
#include <gtest/gtest.h>

using evmc::skip_space_iterator;

namespace
{
std::string remove_space(std::string_view in)
{
// Copy input to additional buffer. This helps with out-of-buffer reads detection by sanitizers.
const auto in_buffer = std::make_unique<char[]>(in.size());
const auto begin = in_buffer.get();
const auto end = begin + in.size();
std::copy(in.begin(), in.end(), begin);

// Filter the input.
std::string out;
std::copy(skip_space_iterator{begin, end}, skip_space_iterator{end, end},
std::back_inserter(out));
return out;
}
} // namespace

TEST(skip_space_iterator, empty)
{
EXPECT_EQ(remove_space(""), "");
EXPECT_EQ(remove_space(" "), "");
EXPECT_EQ(remove_space(" "), "");
}

TEST(skip_space_iterator, filter_middle)
{
EXPECT_EQ(remove_space("x y"), "xy");
EXPECT_EQ(remove_space("x y"), "xy");
}

TEST(skip_space_iterator, filter_front)
{
EXPECT_EQ(remove_space(" x"), "x");
EXPECT_EQ(remove_space(" x"), "x");
}

TEST(skip_space_iterator, filter_back)
{
EXPECT_EQ(remove_space("x "), "x");
EXPECT_EQ(remove_space("x "), "x");
}

TEST(skip_space_iterator, filter_mixed)
{
EXPECT_EQ(remove_space(" x y z "), "xyz");
EXPECT_EQ(remove_space(" x y z "), "xyz");
}

TEST(skip_space_iterator, isspace)
{
// Test internal isspace() compliance with std::isspace().
// The https://en.cppreference.com/w/cpp/string/byte/isspace has the list of "space" characters.

for (int i = int{std::numeric_limits<char>::min()}; i <= std::numeric_limits<char>::max(); ++i)
{
const auto c = static_cast<char>(i);
EXPECT_EQ(evmc::isspace(c), (std::isspace(c) != 0));
switch (c)
{
case ' ':
case '\f':
case '\n':
case '\r':
case '\t':
case '\v':
EXPECT_TRUE(evmc::isspace(c));
break;
default:
EXPECT_FALSE(evmc::isspace(c));
break;
}
}
}
2 changes: 1 addition & 1 deletion tools/evmc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
hunter_add_package(CLI11)
find_package(CLI11 REQUIRED)

add_executable(evmc-tool main.cpp)
add_executable(evmc-tool main.cpp skip_space_iterator.hpp)
add_executable(evmc::tool ALIAS evmc-tool)
set_target_properties(evmc-tool PROPERTIES OUTPUT_NAME evmc)
set_source_files_properties(main.cpp PROPERTIES
Expand Down
11 changes: 7 additions & 4 deletions tools/evmc/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Copyright 2019-2020 The EVMC Authors.
// Licensed under the Apache License, Version 2.0.

#include "skip_space_iterator.hpp"
#include <CLI/CLI.hpp>
#include <evmc/hex.hpp>
#include <evmc/loader.h>
Expand All @@ -18,11 +19,13 @@ evmc::bytes load_from_hex(const std::string& str)
if (str[0] == '@') // The argument is file path.
{
std::ifstream file{str.c_str() + 1};
std::string content{std::istreambuf_iterator<char>{file}, std::istreambuf_iterator<char>{}};
auto o = evmc::from_hex(content);
if (!o)
const std::istreambuf_iterator<char> file_begin{file};
const std::istreambuf_iterator<char> file_end;
evmc::bytes out;
if (!evmc::from_hex(evmc::skip_space_iterator{file_begin, file_end},
evmc::skip_space_iterator{file_end, file_end}, std::back_inserter(out)))
throw std::invalid_argument{"invalid hex"};
return std::move(*o);
return out;
}

return evmc::from_hex(str).value(); // Should be validated already.
Expand Down
60 changes: 60 additions & 0 deletions tools/evmc/skip_space_iterator.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// EVMC: Ethereum Client-VM Connector API.
// Copyright 2022 The EVMC Authors.
// Licensed under the Apache License, Version 2.0.
#pragma once

#include <iterator>

namespace evmc
{
/// The constexpr variant of std::isspace().
inline constexpr bool isspace(char ch) noexcept
{
// Implementation taken from LLVM's libc.
return ch == ' ' || (static_cast<unsigned>(ch) - '\t') < 5;
}

/// The input filter iterator which skips whitespace characters from the base input iterator.
template <typename BaseIterator>
struct skip_space_iterator
{
using difference_type = typename std::iterator_traits<BaseIterator>::difference_type;
using value_type = typename std::iterator_traits<BaseIterator>::value_type;
using pointer = typename std::iterator_traits<BaseIterator>::pointer;
using reference = typename std::iterator_traits<BaseIterator>::reference;
using iterator_category = std::input_iterator_tag;

private:
BaseIterator base;
BaseIterator base_end;
value_type value;

constexpr void forward_to_next_value()
{
for (; base != base_end; ++base)
{
value = *base;
if (isspace(value) == 0)
break;
}
}

public:
constexpr skip_space_iterator(BaseIterator it, BaseIterator end) noexcept
: base{it}, base_end{end}
{
forward_to_next_value();
}

constexpr auto operator*() { return value; }

constexpr void operator++()
{
++base;
forward_to_next_value();
}

constexpr bool operator!=(const skip_space_iterator& o) { return base != o.base; }
constexpr bool operator==(const skip_space_iterator& o) { return base == o.base; }
};
} // namespace evmc

0 comments on commit 6806151

Please sign in to comment.