mirror of
https://github.com/Tencent/rapidjson.git
synced 2025-03-10 03:29:59 +01:00
Add hasher
This commit is contained in:
parent
8f5405a939
commit
1af660c8cb
@ -72,6 +72,91 @@ public:
|
||||
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
|
||||
|
||||
|
@ -17,6 +17,75 @@
|
||||
|
||||
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
|
||||
|
||||
#define VALIDATE(schema, json, expected) \
|
||||
|
Loading…
x
Reference in New Issue
Block a user