Build "spec id->default val str" mapping from string

Add function `ParseDefaultValuesString()` to build the spec id->default
value string mapping required by `SetSpecConstantDefaultValuePass`.
This commit is contained in:
qining 2016-09-09 11:47:42 -04:00
parent 5ac63523d7
commit 66f5b4bfc5
3 changed files with 202 additions and 0 deletions

View File

@ -15,6 +15,8 @@
#include "set_spec_constant_default_value_pass.h"
#include <cstring>
#include <cctype>
#include <string>
#include <tuple>
#include <unordered_map>
#include <vector>
@ -23,6 +25,7 @@
#include "util/parse_number.h"
#include "def_use_manager.h"
#include "make_unique.h"
#include "type_manager.h"
#include "types.h"
@ -32,6 +35,7 @@ namespace opt {
namespace {
using spvutils::NumberType;
using spvutils::EncodeNumberStatus;
using spvutils::ParseNumber;
using spvutils::ParseAndEncodeNumber;
// Given a numeric value in a null-terminated c string and the expected type of
@ -248,5 +252,58 @@ bool SetSpecConstantDefaultValuePass::Process(ir::Module* module) {
return modified;
}
// Returns true if the given char is ':', '\0' or considered as blank space
// (i.e.: '\n', '\r', '\v', '\t', '\f' and ' ').
bool IsSeparator(char ch) {
return std::strchr(":\0", ch) || std::isspace(ch) != 0;
}
std::unique_ptr<SetSpecConstantDefaultValuePass::SpecIdToValueStrMap>
SetSpecConstantDefaultValuePass::ParseDefaultValuesString(const char* str) {
if (!str) return nullptr;
auto spec_id_to_value = MakeUnique<SpecIdToValueStrMap>();
// The parsing loop, break when points to the end.
while (*str) {
// Find the spec id.
while (std::isspace(*str)) str++; // skip leading spaces.
const char* entry_begin = str;
while (!IsSeparator(*str)) str++;
const char* entry_end = str;
std::string spec_id_str(entry_begin, entry_end - entry_begin);
uint32_t spec_id = 0;
if (!ParseNumber(spec_id_str.c_str(), &spec_id)) {
// The spec id is not a valid uint32 number.
return nullptr;
}
auto iter = spec_id_to_value->find(spec_id);
if (iter != spec_id_to_value->end()) {
// Same spec id has been defined before
return nullptr;
}
// Find the ':', spaces between the spec id and the ':' are not allowed.
if (*str++ != ':') {
// ':' not found
return nullptr;
}
// Find the value string
const char* val_begin = str;
while (!IsSeparator(*str)) str++;
const char* val_end = str;
if (val_end == val_begin) {
// Value string is empty.
return nullptr;
}
// Update the mapping with spec id and value string.
(*spec_id_to_value)[spec_id] = std::string(val_begin, val_end - val_begin);
// Skip trailing spaces.
while (std::isspace(*str)) str++;
}
return spec_id_to_value;
}
} // namespace opt
} // namespace spvtools

View File

@ -43,6 +43,42 @@ class SetSpecConstantDefaultValuePass : public Pass {
const char* name() const override { return "set-spec-const-default-value"; }
bool Process(ir::Module*) override;
// Parses the given null-terminated C string to get a mapping from Spec Id to
// default value strings. Returns a unique pointer of the mapping from spec
// ids to spec constant default value strings built from the given |str| on
// success. Returns a nullptr if the given string is not valid for building
// the mapping.
// A valid string for building the mapping should follow the rule below:
//
// "<spec id A>:<default value for A> <spec id B>:<default value for B> ..."
// Example:
// "200:0x11 201:3.14 202:1.4728"
//
// Entries are separated with blank spaces (i.e.:' ', '\n', '\r', '\t',
// '\f', '\v'). Each entry corresponds to a Spec Id and default value pair.
// Multiple spaces between, before or after entries are allowed. However,
// spaces are not allowed within spec id or the default value string because
// spaces are always considered as delimiter to separate entries.
//
// In each entry, the spec id and value string is separated by ':'. Missing
// ':' in any entry is invalid. And it is invalid to have blank spaces in
// between the spec id and ':' or the default value and ':'.
//
// <spec id>: specifies the spec id value.
// The text must represent a valid uint32_t number.
// Hex format with '0x' prefix is allowed.
// Empty <spec id> is not allowed.
// One spec id value can only be defined once, multiple default values
// defined for the same spec id is not allowed. Spec ids with same value
// but different formats (e.g. 0x100 and 256) are considered the same.
//
// <default value>: the default value string.
// Spaces before and after default value text is allowed.
// Spaces within the text is not allowed.
// Empty <default value> is not allowed.
static std::unique_ptr<SpecIdToValueStrMap> ParseDefaultValuesString(
const char* str);
private:
// The mapping from spec ids to their default values to be set.
const SpecIdToValueStrMap spec_id_to_value_;

View File

@ -14,12 +14,121 @@
#include "pass_fixture.h"
#include <gmock/gmock.h>
namespace {
using namespace spvtools;
using testing::Eq;
using SpecIdToValueStrMap =
opt::SetSpecConstantDefaultValuePass::SpecIdToValueStrMap;
struct DefaultValuesStringParsingTestCase {
const char* default_values_str;
bool expect_success;
SpecIdToValueStrMap expected_map;
};
using DefaultValuesStringParsingTest =
::testing::TestWithParam<DefaultValuesStringParsingTestCase>;
TEST_P(DefaultValuesStringParsingTest, TestCase) {
const auto& tc = GetParam();
auto actual_map =
opt::SetSpecConstantDefaultValuePass::ParseDefaultValuesString(
tc.default_values_str);
if (tc.expect_success) {
EXPECT_NE(nullptr, actual_map);
if (actual_map) EXPECT_THAT(*actual_map, Eq(tc.expected_map));
} else {
EXPECT_EQ(nullptr, actual_map);
}
}
INSTANTIATE_TEST_CASE_P(
ValidString, DefaultValuesStringParsingTest,
::testing::ValuesIn(std::vector<DefaultValuesStringParsingTestCase>{
// 0. empty map
{"", true, SpecIdToValueStrMap{}},
// 1. one pair
{"100:1024", true, SpecIdToValueStrMap{{100, "1024"}}},
// 2. two pairs
{"100:1024 200:2048", true,
SpecIdToValueStrMap{{100, "1024"}, {200, "2048"}}},
// 3. spaces between entries
{"100:1024 \n \r \t \v \f 200:2048", true,
SpecIdToValueStrMap{{100, "1024"}, {200, "2048"}}},
// 4. \t, \n, \r and spaces before spec id
{" \n \r\t \t \v \f 100:1024", true,
SpecIdToValueStrMap{{100, "1024"}}},
// 5. \t, \n, \r and spaces after value string
{"100:1024 \n \r\t \t \v \f ", true,
SpecIdToValueStrMap{{100, "1024"}}},
// 6. maximum spec id
{"4294967295:0", true, SpecIdToValueStrMap{{4294967295, "0"}}},
// 7. minimum spec id
{"0:100", true, SpecIdToValueStrMap{{0, "100"}}},
// 8. random content without spaces are allowed
{"200:random_stuff", true, SpecIdToValueStrMap{{200, "random_stuff"}}},
// 9. support hex format spec id (just because we use the
// ParseNumber() utility)
{"0x100:1024", true, SpecIdToValueStrMap{{256, "1024"}}},
// 10. multiple entries
{"101:1 102:2 103:3 104:4 200:201 9999:1000 0x100:333", true,
SpecIdToValueStrMap{{101, "1"},
{102, "2"},
{103, "3"},
{104, "4"},
{200, "201"},
{9999, "1000"},
{256, "333"}}},
// 11. default value in hex float format
{"100:0x0.3p10", true, SpecIdToValueStrMap{{100, "0x0.3p10"}}},
// 12. default value in decimal float format
{"100:1.5e-13", true, SpecIdToValueStrMap{{100, "1.5e-13"}}},
}));
INSTANTIATE_TEST_CASE_P(
InvalidString, DefaultValuesStringParsingTest,
::testing::ValuesIn(std::vector<DefaultValuesStringParsingTestCase>{
// 0. missing default value
{"100:", false, SpecIdToValueStrMap{}},
// 1. spec id is not an integer
{"100.0:200", false, SpecIdToValueStrMap{}},
// 2. spec id is not a number
{"something_not_a_number:1", false, SpecIdToValueStrMap{}},
// 3. only spec id number
{"100", false, SpecIdToValueStrMap{}},
// 4. same spec id defined multiple times
{"100:20 100:21", false, SpecIdToValueStrMap{}},
// 5. Multiple definition of an identical spec id in different forms
// is not allowed
{"0x100:100 256:200", false, SpecIdToValueStrMap{}},
// 6. empty spec id
{":3", false, SpecIdToValueStrMap{}},
// 7. only colon
{":", false, SpecIdToValueStrMap{}},
// 8. spec id overflow
{"4294967296:200", false, SpecIdToValueStrMap{}},
// 9. spec id less than 0
{"-1:200", false, SpecIdToValueStrMap{}},
// 10. nullptr
{nullptr, false, SpecIdToValueStrMap{}},
// 11. only a number is invalid
{"1234", false, SpecIdToValueStrMap{}},
// 12. invalid entry separator
{"12:34;23:14", false, SpecIdToValueStrMap{}},
// 13. invalid spec id and default value separator
{"12@34", false, SpecIdToValueStrMap{}},
// 14. spaces before colon
{"100 :1024", false, SpecIdToValueStrMap{}},
// 15. spaces after colon
{"100: 1024", false, SpecIdToValueStrMap{}},
// 16. spec id represented in hex float format is invalid
{"0x3p10:200", false, SpecIdToValueStrMap{}},
}));
struct SetSpecConstantDefaultValueTestCase {
const char* code;
SpecIdToValueStrMap default_values;