Repository: Tencent/rapidjson (C++)

Issue:
Hasher's offset basis high and low values swapped
FNV-1a algorithm specifies the 64-bit offset basis as 0xcbf29ce484222325
https://en.wikipedia.org/wiki/Fowler–Noll–Vo_hash_function

In the code it's written as RAPIDJSON_UINT64_C2(0x84222325, 0xcbf29ce4). 
https://github.com/Tencent/rapidjson/blob/2648a732dbb8c7fa211e41616071202bfbd09c77/include/rapidjson/schema.h#L294

Shouldn't these high and low values be swapped?
JSON schema incorrect validation with array with uniqueItems
Tested against the release 1.1.0 and the latest master at the time of writing.

With the schema:

```json
{
  "$schema": "http://json-schema.org/schema#",
  "id": "http://test.com/schemas/test.schema.json",
  "type": "object",
  "properties": {
    "myArray": {
      "type": "array",
      "uniqueItems": true,
      "items": {
        "type": "object"
      }
    }
  }
}
```

And the JSON document:

```json
{
  "myArray": [
    {
      "a": "a"
    },
    {
      "b": "b"
    },
    {
      "c": "c"
    }
  ] 
} 
```

I get the following error from the schemavalidator example:

```text
Input JSON is invalid.
Invalid schema: #/properties/myArray
Invalid keyword: uniqueItems
Invalid code: 11
Invalid message: Array has duplicate items at indices '%duplicates' but 'uniqueItems' is true.
Invalid document: #/myArray/1
Error report:
{
    "uniqueItems": {
        "duplicates": [
            0,
            1
        ],
        "errorCode": 11,
        "instanceRef": "#/myArray",
        "schemaRef": "#/properties/myArray"
    }
}
Error Name: uniqueItems
Message: Array has duplicate items at indices '0,1' but 'uniqueItems' is true.
Instance: #/myArray
Schema: #/properties/myArray
```

But these objects are unique. If I change the first object in the array that it's saying is a duplicate:

```json
{
  "myArray": [
    {
      "a": "a1"
    },
    {
      "b": "b"
    },
    {
      "c": "c"
    }
  ]
}
```

I get an error with the next 2 elements instead:

```text
Input JSON is invalid.
Invalid schema: #/properties/myArray
Invalid keyword: uniqueItems
Invalid code: 11
Invalid message: Array has duplicate items at indices '%duplicates' but 'uniqueItems' is true.
Invalid document: #/myArray/2
Error report:
{
    "uniqueItems": {
        "duplicates": [
            1,
            2
        ],
        "errorCode": 11,
        "instanceRef": "#/myArray",
        "schemaRef": "#/properties/myArray"
    }
}
Error Name: uniqueItems
Message: Array has duplicate items at indices '1,2' but 'uniqueItems' is true.
Instance: #/myArray
Schema: #/properties/myArray
```

This same situation occurs with longer keys/values as well, which is where I came across the issue.


Discussion:
Thank you for noting this. Wanna give a PR?

Looks like the problem caused by buggy `Hasher` implementation, can you provide the testing code? I'll try to investigate.
> can you provide the testing code? I'll try to investigate.

I used the schemavalidator example https://github.com/Tencent/rapidjson/blob/master/example/schemavalidator/schemavalidator.cpp with the first schema and document in this issue post.
Confirmed, the problem is the object hashing, the algorithm used for keys and string values is the same, the `Hasher::Hash` produce zero hash value by performing xor between them. I will land the fix to my fork soon.
happy to see your question got some reply which means the project is not dead. No one tried to help 2204 yet. Anyway it's a good symptom.



Patch:
diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h
index 06f50efa2..973e935f1 100644
--- a/include/rapidjson/schema.h
+++ b/include/rapidjson/schema.h
@@ -367,7 +367,9 @@ class Hasher {
         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
+            // Issue #2205
+            // Hasing the key to avoid key=value cases with bug-prone zero-value hash
+            h ^= Hash(Hash(0, kv[i * 2]), kv[i * 2 + 1]);  // Use xor to achieve member order insensitive
         *stack_.template Push<uint64_t>() = h;
         return true;
     }
@@ -405,7 +407,7 @@ class Hasher {
     
     bool WriteBuffer(Type 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);
+        uint64_t h = Hash(RAPIDJSON_UINT64_C2(0xcbf29ce4, 0x84222325), type);
         const unsigned char* d = static_cast<const unsigned char*>(data);
         for (size_t i = 0; i < len; i++)
             h = Hash(h, d[i]);


Test patch:
diff --git a/test/unittest/schematest.cpp b/test/unittest/schematest.cpp
index 7387c0896..dbc467ea3 100644
--- a/test/unittest/schematest.cpp
+++ b/test/unittest/schematest.cpp
@@ -99,6 +99,9 @@ TEST(SchemaValidator, Hasher) {
     TEST_HASHER("{\"a\":1}", "{\"a\":1}", true);
     TEST_HASHER("{\"a\":1}", "{\"b\":1}", false);
     TEST_HASHER("{\"a\":1}", "{\"a\":2}", false);
+    TEST_HASHER("{\"a\":\"a\"}", "{\"b\":\"b\"}", false); // Key equals value hashing
+    TEST_HASHER("{\"a\":\"a\", \"b\":\"b\"}", "{\"c\":\"c\", \"d\":\"d\"}", false);
+    TEST_HASHER("{\"a\":\"a\"}", "{\"b\":\"b\", \"c\":\"c\"}", false);
     TEST_HASHER("{\"a\":1, \"b\":2}", "{\"b\":2, \"a\":1}", true); // Member order insensitive
     TEST_HASHER("{}", "null", false);
     TEST_HASHER("{}", "false", false);