Add hasher
This commit is contained in:
parent
8f5405a939
commit
1af660c8cb
@ -72,6 +72,91 @@ public:
|
|||||||
virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) const = 0;
|
virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&) const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Hasher
|
||||||
|
|
||||||
|
// For comparison of compound value
|
||||||
|
template<typename ValueType, typename Allocator>
|
||||||
|
class Hasher {
|
||||||
|
public:
|
||||||
|
typedef typename ValueType::Ch Ch;
|
||||||
|
|
||||||
|
Hasher() : stack_(0, kDefaultSize) {}
|
||||||
|
|
||||||
|
bool Null() { return WriteType(kNullType); }
|
||||||
|
bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); }
|
||||||
|
bool Int(int i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
|
||||||
|
bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
|
||||||
|
bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
|
||||||
|
bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
|
||||||
|
bool Double(double d) {
|
||||||
|
Number n;
|
||||||
|
if (d < 0) n.u.i = static_cast<int64_t>(d);
|
||||||
|
else n.u.u = static_cast<uint64_t>(d);
|
||||||
|
n.d = d;
|
||||||
|
return WriteNumber(n);
|
||||||
|
}
|
||||||
|
bool String(const Ch* str, SizeType len, bool) {
|
||||||
|
WriteBuffer(kStringType, str, len * sizeof(Ch));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool StartObject() { return true; }
|
||||||
|
bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); }
|
||||||
|
bool EndObject(SizeType memberCount) {
|
||||||
|
uint64_t h = Hash(0, kObjectType);
|
||||||
|
uint64_t* kv = stack_.template Pop<uint64_t>(memberCount * 2);
|
||||||
|
for (SizeType i = 0; i < memberCount; i++)
|
||||||
|
h ^= Hash(kv[i * 2], kv[i * 2 + 1]); // Use xor to achieve member order insensitive
|
||||||
|
*stack_.template Push<uint64_t>() = h;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool StartArray() { return true; }
|
||||||
|
bool EndArray(SizeType elementCount) {
|
||||||
|
uint64_t h = Hash(0, kArrayType);
|
||||||
|
uint64_t* e = stack_.template Pop<uint64_t>(elementCount);
|
||||||
|
for (SizeType i = 0; i < elementCount; i++)
|
||||||
|
h = Hash(h, e[i]); // Use hash to achieve element order sensitive
|
||||||
|
*stack_.template Push<uint64_t>() = h;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t GetHashCode() const {
|
||||||
|
RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(uint64_t));
|
||||||
|
return *stack_.template Top<uint64_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const size_t kDefaultSize = 256;
|
||||||
|
struct Number {
|
||||||
|
union U {
|
||||||
|
uint64_t u;
|
||||||
|
int64_t i;
|
||||||
|
}u;
|
||||||
|
double d;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool WriteType(unsigned char type) { WriteBuffer(type, 0, 0); return true; }
|
||||||
|
bool WriteNumber(const Number& n) { WriteBuffer(kNumberType, &n, sizeof(n)); return true; }
|
||||||
|
bool WriteBuffer(unsigned char type, const void* data, size_t len) {
|
||||||
|
// FNV-1a from http://isthe.com/chongo/tech/comp/fnv/
|
||||||
|
uint64_t h = Hash(RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4), type);
|
||||||
|
const unsigned char* d = static_cast<const unsigned char*>(data);
|
||||||
|
for (size_t i = 0; i < len; i++)
|
||||||
|
h = Hash(h, d[i]);
|
||||||
|
*stack_.template Push<uint64_t>() = h;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t Hash(uint64_t h, uint64_t d) {
|
||||||
|
static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3);
|
||||||
|
h ^= d;
|
||||||
|
h *= kPrime;
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stack<Allocator> stack_;
|
||||||
|
};
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// SchemaValidationContext
|
// SchemaValidationContext
|
||||||
|
|
||||||
|
@ -17,6 +17,75 @@
|
|||||||
|
|
||||||
using namespace rapidjson;
|
using namespace rapidjson;
|
||||||
|
|
||||||
|
#define TEST_HASHER(json1, json2, expected) \
|
||||||
|
{\
|
||||||
|
Document d1, d2;\
|
||||||
|
d1.Parse(json1);\
|
||||||
|
ASSERT_FALSE(d1.HasParseError());\
|
||||||
|
d2.Parse(json2);\
|
||||||
|
ASSERT_FALSE(d2.HasParseError());\
|
||||||
|
internal::Hasher<Value, CrtAllocator> h1, h2;\
|
||||||
|
d1.Accept(h1);\
|
||||||
|
d2.Accept(h2);\
|
||||||
|
printf("%s: 0x%016llx\n%s: 0x%016llx\n\n", json1, h1.GetHashCode(), json2, h2.GetHashCode());\
|
||||||
|
EXPECT_TRUE(expected == (h1.GetHashCode() == h2.GetHashCode()));\
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SchemaValidator, Hasher) {
|
||||||
|
TEST_HASHER("null", "null", true);
|
||||||
|
|
||||||
|
TEST_HASHER("true", "true", true);
|
||||||
|
TEST_HASHER("false", "false", true);
|
||||||
|
TEST_HASHER("true", "false", false);
|
||||||
|
TEST_HASHER("false", "true", false);
|
||||||
|
TEST_HASHER("true", "null", false);
|
||||||
|
TEST_HASHER("false", "null", false);
|
||||||
|
|
||||||
|
TEST_HASHER("1", "1", true);
|
||||||
|
TEST_HASHER("1.5", "1.5", true);
|
||||||
|
TEST_HASHER("1", "1.0", true);
|
||||||
|
TEST_HASHER("1", "-1", false);
|
||||||
|
TEST_HASHER("0.0", "-0.0", false);
|
||||||
|
TEST_HASHER("1", "true", false);
|
||||||
|
TEST_HASHER("0", "false", false);
|
||||||
|
TEST_HASHER("0", "null", false);
|
||||||
|
|
||||||
|
TEST_HASHER("\"\"", "\"\"", true);
|
||||||
|
TEST_HASHER("\"\"", "\"\\u0000\"", false);
|
||||||
|
TEST_HASHER("\"Hello\"", "\"Hello\"", true);
|
||||||
|
TEST_HASHER("\"Hello\"", "\"World\"", false);
|
||||||
|
TEST_HASHER("\"Hello\"", "null", false);
|
||||||
|
TEST_HASHER("\"Hello\\u0000\"", "\"Hello\"", false);
|
||||||
|
TEST_HASHER("\"\"", "null", false);
|
||||||
|
TEST_HASHER("\"\"", "true", false);
|
||||||
|
TEST_HASHER("\"\"", "false", false);
|
||||||
|
|
||||||
|
TEST_HASHER("[]", "[ ]", true);
|
||||||
|
TEST_HASHER("[1, true, false]", "[1, true, false]", true);
|
||||||
|
TEST_HASHER("[1, true, false]", "[1, true]", false);
|
||||||
|
TEST_HASHER("[1, 2]", "[2, 1]", false);
|
||||||
|
TEST_HASHER("[[1], 2]", "[[1, 2]]", false);
|
||||||
|
TEST_HASHER("[1, 2]", "[1, [2]]", false);
|
||||||
|
TEST_HASHER("[]", "null", false);
|
||||||
|
TEST_HASHER("[]", "true", false);
|
||||||
|
TEST_HASHER("[]", "false", false);
|
||||||
|
TEST_HASHER("[]", "0", false);
|
||||||
|
TEST_HASHER("[]", "0.0", false);
|
||||||
|
TEST_HASHER("[]", "\"\"", false);
|
||||||
|
|
||||||
|
TEST_HASHER("{}", "{ }", true);
|
||||||
|
TEST_HASHER("{\"a\":1}", "{\"a\":1}", true);
|
||||||
|
TEST_HASHER("{\"a\":1}", "{\"b\":1}", false);
|
||||||
|
TEST_HASHER("{\"a\":1}", "{\"a\":2}", false);
|
||||||
|
TEST_HASHER("{\"a\":1, \"b\":2}", "{\"b\":2, \"a\":1}", true); // Member order insensitive
|
||||||
|
TEST_HASHER("{}", "null", false);
|
||||||
|
TEST_HASHER("{}", "false", false);
|
||||||
|
TEST_HASHER("{}", "true", false);
|
||||||
|
TEST_HASHER("{}", "0", false);
|
||||||
|
TEST_HASHER("{}", "0.0", false);
|
||||||
|
TEST_HASHER("{}", "\"\"", false);
|
||||||
|
}
|
||||||
|
|
||||||
// Test cases following http://spacetelescope.github.io/understanding-json-schema
|
// Test cases following http://spacetelescope.github.io/understanding-json-schema
|
||||||
|
|
||||||
#define VALIDATE(schema, json, expected) \
|
#define VALIDATE(schema, json, expected) \
|
||||||
|
Loading…
x
Reference in New Issue
Block a user