Skip to content

Implement a property set I/O library. #1357

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 163 additions & 0 deletions llvm/include/llvm/Support/PropertySetIO.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
//==-- PropertySetIO.h -- models a sequence of property sets and their I/O -==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Models a sequence of property sets and their input and output operations.
// TODO use Yaml as I/O engine.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Such as https://llvm.org/docs/YamlIO.html ?
That sounds like a good idea.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, exactly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just do it. :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, in TODO list.

// PropertyValue set format:
// '['<PropertyValue set name>']'
// <property name>=<property type>'|'<property value>
// <property name>=<property type>'|'<property value>
// ...
// '['<PropertyValue set name>']'
// <property name>=<property type>'|'<property value>
// where
// <PropertyValue set name>, <property name> are strings
// <property type> - string representation of the property type
// <property value> - string representation of the property value.
//
// For example:
// [Staff/Ages]
// person1=1|20
// person2=1|25
// [Staff/Experience]
// person1=1|1
// person2=1|2
// person3=1|12
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_SUPPORT_PROPERTYSETIO_H
#define LLVM_SUPPORT_PROPERTYSETIO_H

#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"

#include <istream>
#include <map>
#include <memory>
#include <string>

namespace llvm {
namespace util {

// Represents a property value. PropertyValue name is stored in the encompassing
// container.
class PropertyValue {
public:
// Defines supported property types
enum Type { first = 0, NONE = first, UINT32, last = UINT32 };

// Translates C++ type to the corresponding type tag.
template <typename T> static Type getTypeTag();

// Casts from int value to a type tag.
static Expected<Type> getTypeTag(int T) {
if (T < first || T > last)
return createStringError(std::error_code(), "bad property type " + T);
return static_cast<Type>(T);
}

PropertyValue() = default;
PropertyValue(Type T) : Ty(T) {}

PropertyValue(uint32_t Val) : Ty(UINT32), Val({Val}) {}
PropertyValue(const PropertyValue &P) = default;
PropertyValue(PropertyValue &&P) = default;

PropertyValue &operator=(PropertyValue &&P) = default;

PropertyValue &operator=(const PropertyValue &P) = default;

// get property value as unsigned 32-bit integer
uint32_t asUint32() const {
assert(Ty == UINT32);
return Val.UInt32Val;
}

bool isValid() const { return getType() != NONE; }

// set property value; the 'T' type must be convertible to a property type tag
template <typename T> void set(T V) {
assert(getTypeTag<T>() == Ty);
getValueRef<T>() = V;
}

Type getType() const { return Ty; }

size_t size() const {
switch (Ty) {
case UINT32:
return sizeof(Val.UInt32Val);
default:
llvm_unreachable_internal("unsupported property type");
}
}

private:
template <typename T> T &getValueRef();

Type Ty = NONE;
union {
uint32_t UInt32Val;
} Val;
};

std::ostream &operator<<(std::ostream &Out, const PropertyValue &V);

// A property set. Preserves insertion order when iterating elements.
using PropertySet = MapVector<StringRef, PropertyValue>;

// A "registry" of multiple property sets. Maps a property set name to its
// contents. Can be read/written.
class PropertySetRegistry {
public:
using MapTy = MapVector<StringRef, PropertySet>;

// Specific property category names used by tools.
static constexpr char SYCL_SPECIALIZATION_CONSTANTS[] =
"SYCL/specialization constants";

// Function for bulk addition of an entire property set under given category
// (property set name).
template <typename T>
void add(StringRef Category, const std::map<StringRef, T> &Props) {
assert(PropSetMap.find(Category) == PropSetMap.end() &&
"category already added");
auto &PropSet = PropSetMap[Category];

for (const auto &Prop : Props)
PropSet.insert(std::make_pair(Prop.first, PropertyValue(Prop.second)));
}

// Parses and creates a property set registry.
static Expected<std::unique_ptr<PropertySetRegistry>>
read(const MemoryBuffer *Buf);

// Dumps a property set registry to a stream.
void write(raw_ostream &Out) const;

// Start iterator of all preperty sets in the registry.
MapTy::const_iterator begin() const { return PropSetMap.begin(); }
// End iterator of all preperty sets in the registry.
MapTy::const_iterator end() const { return PropSetMap.end(); }

// Retrieves a property set with given name.
PropertySet &operator[](StringRef Name) { return PropSetMap[Name]; }
// Constant access to the underlying map.
const MapTy &getPropSets() const { return PropSetMap; }

private:
MapTy PropSetMap;
};

} // namespace util
} // namespace llvm

#endif // #define LLVM_SUPPORT_PROPERTYSETIO_H
1 change: 1 addition & 0 deletions llvm/lib/Support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ add_llvm_component_library(LLVMSupport
Parallel.cpp
PluginLoader.cpp
PrettyStackTrace.cpp
PropertySetIO.cpp
RandomNumberGenerator.cpp
Regex.cpp
ScaledNumber.cpp
Expand Down
116 changes: 116 additions & 0 deletions llvm/lib/Support/PropertySetIO.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
//==- PropertySetIO.cpp - models a sequence of property sets and their I/O -==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "llvm/Support/PropertySetIO.h"

#include "llvm/ADT/APInt.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/LineIterator.h"

using namespace llvm::util;
using namespace llvm;

Expected<std::unique_ptr<PropertySetRegistry>>
PropertySetRegistry::read(const MemoryBuffer *Buf) {
auto Res = std::make_unique<PropertySetRegistry>();
PropertySet *CurPropSet = nullptr;
std::error_code EC;

for (line_iterator LI(*Buf); !LI.is_at_end(); LI++) {
// see if this line starts a new property set
if (LI->startswith("[")) {
// yes - parse the category (property name)
auto EndPos = LI->rfind(']');
if (EndPos == StringRef::npos)
return createStringError(EC, "invalid line: " + *LI);
StringRef Category = LI->substr(1, EndPos - 1);
CurPropSet = &(*Res)[Category];
continue;
}
if (!CurPropSet)
return createStringError(EC, "property category missing");
// parse name and type+value
auto Parts = LI->split('=');

if (Parts.first.empty() || Parts.second.empty())
return createStringError(EC, "invalid property line: " + *LI);
auto TypeVal = Parts.second.split('|');

if (TypeVal.first.empty() || TypeVal.second.empty())
return createStringError(EC, "invalid property value: " + Parts.second);
APInt Tint;

// parse type
if (TypeVal.first.getAsInteger(10, Tint))
return createStringError(EC, "invalid property type: " + TypeVal.first);
Expected<PropertyValue::Type> Ttag =
PropertyValue::getTypeTag(static_cast<int>(Tint.getSExtValue()));
StringRef Val = TypeVal.second;

if (!Ttag)
return Ttag.takeError();
PropertyValue Prop(Ttag.get());

// parse value depending on its type
switch (Ttag.get()) {
case PropertyValue::Type::UINT32: {
APInt ValV;
if (Val.getAsInteger(10, ValV))
return createStringError(EC, "invalid property value: " + Val);
Prop.set(static_cast<uint32_t>(ValV.getZExtValue()));
break;
}
default:
return createStringError(EC, "unsupported property type: " + Ttag.get());
}
(*CurPropSet)[Parts.first] = Prop;
}
if (!CurPropSet)
return createStringError(EC, "invalid property set registry");

return Expected<std::unique_ptr<PropertySetRegistry>>(std::move(Res));
}

namespace llvm {
// output a property to a stream
raw_ostream &operator<<(raw_ostream &Out, const PropertyValue &Prop) {
Out << static_cast<int>(Prop.getType()) << "|";
switch (Prop.getType()) {
case PropertyValue::Type::UINT32:
Out << Prop.asUint32();
break;
default:
llvm_unreachable_internal("unsupported property type: " + Prop.getType());
}
return Out;
}
} // namespace llvm

void PropertySetRegistry::write(raw_ostream &Out) const {
for (const auto &PropSet : PropSetMap) {
Out << "[" << PropSet.first << "]\n";

for (const auto &Props : PropSet.second) {
Out << std::string(Props.first) << "=" << Props.second << "\n";
}
}
}

namespace llvm {
namespace util {

template <> uint32_t &PropertyValue::getValueRef<uint32_t>() {
return Val.UInt32Val;
}
template <> PropertyValue::Type PropertyValue::getTypeTag<uint32_t>() {
return UINT32;
}

constexpr char PropertySetRegistry::SYCL_SPECIALIZATION_CONSTANTS[];
} // namespace util
} // namespace llvm
1 change: 1 addition & 0 deletions llvm/unittests/Support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ add_llvm_unittest(SupportTests
Path.cpp
ProcessTest.cpp
ProgramTest.cpp
PropertySetIOTest.cpp
RegexTest.cpp
ReverseIterationTest.cpp
ReplaceFileTest.cpp
Expand Down
45 changes: 45 additions & 0 deletions llvm/unittests/Support/PropertySetIOTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//===- llvm/unittest/Support/PropertySetIO.cpp - Property set I/O tests ---===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "llvm/Support/PropertySetIO.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/raw_ostream.h"

#include "gtest/gtest.h"

using namespace llvm;
using namespace llvm::util;

namespace {

TEST(PropertySet, IntValuesIO) {
// '1' in '1|20' means 'integer property'
auto Content = "[Staff/Ages]\n"
"person1=1|20\n"
"person2=1|25\n"
"[Staff/Experience]\n"
"person1=1|1\n"
"person2=1|2\n"
"person3=1|12\n";
auto MemBuf = MemoryBuffer::getMemBuffer(Content);
// Parse a property set registry
auto PropSetsPtr = PropertySetRegistry::read(MemBuf.get());

if (!PropSetsPtr)
FAIL() << "PropertySetRegistry::read failed\n";

std::string Serialized;
{
llvm::raw_string_ostream OS(Serialized);
// Serialize
PropSetsPtr->get()->write(OS);
}
// Check that the original and the serialized version are equal
ASSERT_EQ(Serialized, Content);
}
} // namespace