Skip to content

Commit

Permalink
[YAML] Support serializing MutableArrayRef
Browse files Browse the repository at this point in the history
While the YAML format itself doesn't support fixed-sized arrays, some
of the data structures we encode in and out of YAML (specifically in
ObjectYAML) are actually fixed-sized arrays which we end up expressing
as resizable arrays.

Enabling the YAML tooling to support reading and writing from
fixed-sized arrays using MutableArrayRef can simplify some of the error
reporting and use logic for cases where the sizes of arrays are defined
by the target format.

Note: my SFINAE-foo isn't the best, so if there is a cleaner way to
implement the traits please advise.

Reviewed By: MaskRay

Differential Revision: https://reviews.llvm.org/D140427
  • Loading branch information
llvm-beanz committed Jan 3, 2023
1 parent 750e1c8 commit a95717e
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 6 deletions.
39 changes: 33 additions & 6 deletions llvm/include/llvm/Support/YAMLTraits.h
Expand Up @@ -9,6 +9,7 @@
#ifndef LLVM_SUPPORT_YAMLTRAITS_H
#define LLVM_SUPPORT_YAMLTRAITS_H

#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
Expand Down Expand Up @@ -1945,13 +1946,15 @@ operator<<(Output &yout, T &seq) {
template <bool B> struct IsFlowSequenceBase {};
template <> struct IsFlowSequenceBase<true> { static const bool flow = true; };

template <typename T, bool Flow>
struct SequenceTraitsImpl : IsFlowSequenceBase<Flow> {
private:
using type = typename T::value_type;
template <typename T, typename U = void>
struct IsResizable : std::false_type {};

public:
static size_t size(IO &io, T &seq) { return seq.size(); }
template <typename T>
struct IsResizable<T, std::void_t<decltype(std::declval<T>().resize(0))>>
: public std::true_type {};

template <typename T, bool B> struct IsResizableBase {
using type = typename T::value_type;

static type &element(IO &io, T &seq, size_t index) {
if (index >= seq.size())
Expand All @@ -1960,6 +1963,25 @@ struct SequenceTraitsImpl : IsFlowSequenceBase<Flow> {
}
};

template <typename T> struct IsResizableBase<T, false> {
using type = typename T::value_type;

static type &element(IO &io, T &seq, size_t index) {
if (index >= seq.size()) {
io.setError(Twine("value sequence extends beyond static size (") +
Twine(seq.size()) + ")");
return seq[0];
}
return seq[index];
}
};

template <typename T, bool Flow>
struct SequenceTraitsImpl
: IsFlowSequenceBase<Flow>, IsResizableBase<T, IsResizable<T>::value> {
static size_t size(IO &io, T &seq) { return seq.size(); }
};

// Simple helper to check an expression can be used as a bool-valued template
// argument.
template <bool> struct CheckIsBool { static const bool value = true; };
Expand All @@ -1981,6 +2003,11 @@ struct SequenceTraits<
SmallVectorImpl<T>,
std::enable_if_t<CheckIsBool<SequenceElementTraits<T>::flow>::value>>
: SequenceTraitsImpl<SmallVectorImpl<T>, SequenceElementTraits<T>::flow> {};
template <typename T>
struct SequenceTraits<
MutableArrayRef<T>,
std::enable_if_t<CheckIsBool<SequenceElementTraits<T>::flow>::value>>
: SequenceTraitsImpl<MutableArrayRef<T>, SequenceElementTraits<T>::flow> {};

// Sequences of fundamental types use flow formatting.
template <typename T>
Expand Down
69 changes: 69 additions & 0 deletions llvm/unittests/Support/YAMLIOTest.cpp
Expand Up @@ -3266,3 +3266,72 @@ TEST(YAMLIO, TestScannerNoNullScanPlainScalarInFlow) {
yin.setCurrentDocument();
EXPECT_TRUE(yin.error());
}

struct FixedArray {
FixedArray() {
// Initialize to int max as a sentinel value.
for (auto &v : values)
v = std::numeric_limits<int>::max();
}
int values[4];
};

namespace llvm {
namespace yaml {
template <>
struct MappingTraits<FixedArray> {
static void mapping(IO &io, FixedArray& st) {
MutableArrayRef<int> array = st.values;
io.mapRequired("Values", array);
}
};
}
}

TEST(YAMLIO, FixedSizeArray) {
FixedArray faval;
Input yin("---\nValues: [ 1, 2, 3, 4 ]\n...\n");
yin >> faval;

EXPECT_FALSE(yin.error());
EXPECT_EQ(faval.values[0], 1);
EXPECT_EQ(faval.values[1], 2);
EXPECT_EQ(faval.values[2], 3);
EXPECT_EQ(faval.values[3], 4);

std::string serialized;
{
llvm::raw_string_ostream os(serialized);
Output yout(os);
yout << faval;
}
auto expected = "---\n"
"Values: [ 1, 2, 3, 4 ]\n"
"...\n";
ASSERT_EQ(serialized, expected);
}

TEST(YAMLIO, FixedSizeArrayMismatch) {
{
FixedArray faval;
Input yin("---\nValues: [ 1, 2, 3 ]\n...\n");
yin >> faval;

// No error for too small, leaves the default initialized value
EXPECT_FALSE(yin.error());
EXPECT_EQ(faval.values[0], 1);
EXPECT_EQ(faval.values[1], 2);
EXPECT_EQ(faval.values[2], 3);
EXPECT_EQ(faval.values[3], std::numeric_limits<int>::max());
}

{
FixedArray faval;
Input yin("---\nValues: [ 1, 2, 3, 4, 5 ]\n...\n");
yin >> faval;

// Error for too many elements.
EXPECT_TRUE(!!yin.error());
}

}

0 comments on commit a95717e

Please sign in to comment.