Skip to content

Commit

Permalink
Merge pull request #404 from wasmx/span
Browse files Browse the repository at this point in the history
Span utility
  • Loading branch information
chfast authored Jul 7, 2020
2 parents d7c6c48 + 88c4a50 commit 9a6fc2e
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 2 deletions.
6 changes: 4 additions & 2 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
---
Language: Cpp
BasedOnStyle: Chromium
Language: Cpp
Standard: c++17
BasedOnStyle: Chromium

AccessModifierOffset: -4
AlignAfterOpenBracket: DontAlign
BinPackParameters: true
Expand Down
1 change: 1 addition & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Checks: >
-readability-magic-numbers,
-readability-named-parameter,
-readability-qualified-auto,
-readability-uppercase-literal-suffix,
WarningsAsErrors: '*'
FormatStyle: file
1 change: 1 addition & 0 deletions lib/fizzy/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ target_sources(
parser.cpp
parser.hpp
parser_expr.cpp
span.hpp
stack.hpp
types.hpp
utf8.cpp
Expand Down
65 changes: 65 additions & 0 deletions lib/fizzy/span.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Fizzy: A fast WebAssembly interpreter
// Copyright 2019-2020 The Fizzy Authors.
// SPDX-License-Identifier: Apache-2.0

#pragma once

#if __cplusplus > 201703L

#include <span>

namespace fizzy
{
template <typename T>
using span = std::span<T>;
}

#else

#include <iterator>
#include <type_traits>

namespace fizzy
{
/// The span describes an object that can refer to a contiguous sequence of objects with the first
/// element of the sequence at position zero.
///
/// This is minimal implementation of C++20's std::span:
/// https://en.cppreference.com/w/cpp/container/span
/// Only `const T` is supported.
template <typename T, typename = typename std::enable_if_t<std::is_const_v<T>>>
class span
{
T* const m_begin = nullptr;
const std::size_t m_size = 0;

public:
using value_type = std::remove_cv_t<T>;
using iterator = T*;
using reverse_iterator = std::reverse_iterator<iterator>;

constexpr span() = default;
constexpr span(const span&) = default;

constexpr span(T* begin, std::size_t size) noexcept : m_begin{begin}, m_size{size} {}

/// Constructor from a container type. Requires the Container to implement std::data() and
/// std::size().
template <typename Container>
constexpr span(const Container& container) noexcept
: m_begin{std::data(container)}, m_size{std::size(container)}
{}

constexpr T& operator[](std::size_t index) const noexcept { return m_begin[index]; }

[[nodiscard]] constexpr std::size_t size() const noexcept { return m_size; }

constexpr iterator begin() const noexcept { return m_begin; }
constexpr iterator end() const noexcept { return m_begin + m_size; }

constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; }
constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; }
};
} // namespace fizzy

#endif /* __cplusplus > 201703L */
1 change: 1 addition & 0 deletions test/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ target_sources(
leb128_test.cpp
parser_expr_test.cpp
parser_test.cpp
span_test.cpp
stack_test.cpp
test_utils_test.cpp
types_test.cpp
Expand Down
142 changes: 142 additions & 0 deletions test/unittests/span_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// Fizzy: A fast WebAssembly interpreter
// Copyright 2020 The Fizzy Authors.
// SPDX-License-Identifier: Apache-2.0

#include "span.hpp"
#include "stack.hpp"
#include <gtest/gtest.h>
#include <array>
#include <vector>

using namespace fizzy;
using namespace testing;

TEST(span, vector)
{
std::vector<uint64_t> vec{1, 2, 3, 4, 5, 6};
span<const uint64_t> s(&vec[1], 3);
EXPECT_EQ(s.size(), 3);
EXPECT_EQ(s[0], 2);
EXPECT_EQ(s[1], 3);
EXPECT_EQ(s[2], 4);
EXPECT_EQ(*s.begin(), 2);
EXPECT_EQ(*(s.end() - 1), 4);

vec[1] = 100;
EXPECT_EQ(s[0], 100);

const span<const uint64_t> s2 = vec; // Implicit conversion from vector.
for (size_t i = 0; i < vec.size(); ++i)
EXPECT_EQ(s2[i], vec[i]);
}

TEST(span, array)
{
float a1[] = {1, 2, 3};
span<const float> s1 = a1;
EXPECT_EQ(s1.size(), 3);
EXPECT_EQ(s1[0], 1.0f);
EXPECT_EQ(s1[1], 2.0f);
EXPECT_EQ(s1[2], 3.0f);

const std::array a2 = {0.1f, 0.2f, 0.3f};
span<const float> s2 = a2;
EXPECT_EQ(s2.size(), 3);
EXPECT_EQ(s2[0], 0.1f);
EXPECT_EQ(s2[1], 0.2f);
EXPECT_EQ(s2[2], 0.3f);
}

TEST(span, stack)
{
OperandStack stack(4);
stack.push(10);
stack.push(11);
stack.push(12);
stack.push(13);

constexpr auto num_items = 2;
span<const uint64_t> s(stack.rend() - num_items, num_items);
EXPECT_EQ(s.size(), 2);
EXPECT_EQ(s[0], 12);
EXPECT_EQ(s[1], 13);

stack[0] = 0;
EXPECT_EQ(s[1], 0);
}

TEST(span, initializer_list)
{
// This only works for lvalue initializer_lists, but not as `span{1, 2, 3}`.
// Dangerous usage because user need to keep the initializer_list alive
// as long as span is being used.
const std::initializer_list<uint64_t> init = {1, 2, 3};
const auto s = span<const uint64_t>(init);
EXPECT_EQ(s.size(), 3);
EXPECT_EQ(s[0], 1);
EXPECT_EQ(s[1], 2);
EXPECT_EQ(s[2], 3);

// For range loop also works.
uint64_t i = 0;
for (const auto& x : s)
EXPECT_EQ(x, ++i);
}

TEST(span, iterator)
{
std::string str{"__abc__"};
span<const char> slice{str.data() + 2, 3};

auto it = slice.begin();
EXPECT_EQ(*it, 'a');
++it;
EXPECT_EQ(*it, 'b');
++it;
EXPECT_EQ(*it, 'c');
++it;
EXPECT_EQ(it, slice.end());

EXPECT_EQ(slice.end() - slice.begin(), slice.size());
}

TEST(span, iterator_range)
{
std::string str{"__abc__"};
span<const char> sp = str;

std::string copy;
std::copy(std::begin(sp), std::end(sp), std::back_inserter(copy));
EXPECT_EQ(copy, str);
}

TEST(span, for_range)
{
std::string str{"**xyz**"};
span<const char> sp = str;

std::string copy;
for (const auto c : sp)
copy.push_back(c);

EXPECT_EQ(copy, str);
}

TEST(span, reverse_iterator)
{
int a[] = {1, 2, 3, 4, 5, 6};
span<const int> s{&a[1], 4};

auto it = s.rbegin();
EXPECT_EQ(*it, 5);
++it;
EXPECT_EQ(*it, 4);
++it;
EXPECT_EQ(*it, 3);
++it;
EXPECT_EQ(*it, 2);
++it;
EXPECT_EQ(it, s.rend());

EXPECT_EQ(s.rend() - s.rbegin(), s.size());
}

0 comments on commit 9a6fc2e

Please sign in to comment.