on master help sign in
Make sure the hashes of two equal Values are always the same
c2def3bZejun Wu
Data/Aeson/Types/Internal.hs+12−2
@@ -90,6 +90,10 @@ import Data.Monoid (Monoid(..))
90import Data.Traversable (Traversable(..))
90import Data.Traversable (Traversable(..))
91#endif
91#endif
92
92
93+#if !MIN_VERSION_unordered_containers(0,2,6)
94+import Data.List (foldl', sort)
95+#endif
96+
93-- | Elements of a JSON path used to describe the location of an
97-- | Elements of a JSON path used to describe the location of an
94-- error.
98-- error.
95data JSONPathElement = Key Text
99data JSONPathElement = Key Text
@@ -391,8 +395,14 @@ instance IsString Value where
391 {-# INLINE fromString #-}
395 {-# INLINE fromString #-}
392
396
393hashValue :: Int -> Value -> Int
397hashValue :: Int -> Value -> Int
398+#if MIN_VERSION_unordered_containers(0,2,6)
399+hashValue s (Object o) = s `hashWithSalt` (0::Int) `hashWithSalt` o
400+#else
394−hashValue s (Object o) = H.foldl' hashWithSalt
401+hashValue s (Object o) = foldl' hashWithSalt
395− (s `hashWithSalt` (0::Int)) o
402+ (s `hashWithSalt` (0::Int)) assocHashesSorted
403+ where
404+ assocHashesSorted = sort [hash k `hashWithSalt` v | (k, v) <- H.toList o]
405+#endif
396hashValue s (Array a) = V.foldl' hashWithSalt
406hashValue s (Array a) = V.foldl' hashWithSalt
397 (s `hashWithSalt` (1::Int)) a
407 (s `hashWithSalt` (1::Int)) a
398hashValue s (String str) = s `hashWithSalt` (2::Int) `hashWithSalt` str
408hashValue s (String str) = s `hashWithSalt` (2::Int) `hashWithSalt` str
aeson.cabal+1
@@ -158,6 +158,7 @@ test-suite tests
158 bytestring,
158 bytestring,
159 containers,
159 containers,
160 ghc-prim >= 0.2,
160 ghc-prim >= 0.2,
161+ hashable >= 1.1.2.0,
161 tagged,
162 tagged,
162 template-haskell,
163 template-haskell,
163 test-framework,
164 test-framework,
tests/UnitTests.hs+15−1
@@ -6,12 +6,13 @@ module UnitTests (ioTests, tests) where
6
6
7import Control.Applicative (Const(..))
7import Control.Applicative (Const(..))
8import Control.Monad (forM)
8import Control.Monad (forM)
9−import Data.Aeson (decode, eitherDecode, encode, genericToJSON, genericToEncoding, FromJSON(..), withObject, (.:), (.:?), (.:!))
9+import Data.Aeson (decode, eitherDecode, encode, genericToJSON, genericToEncoding, object, FromJSON(..), withObject, (.=), (.:), (.:?), (.:!))
10import Data.Aeson.Encode (encodeToTextBuilder)
10import Data.Aeson.Encode (encodeToTextBuilder)
11import Data.Aeson.Internal (JSONPathElement(..), formatError)
11import Data.Aeson.Internal (JSONPathElement(..), formatError)
12import Data.Aeson.TH (deriveJSON)
12import Data.Aeson.TH (deriveJSON)
13import Data.Aeson.Types (ToJSON(..), Value, camelTo, camelTo2, defaultOptions, omitNothingFields)
13import Data.Aeson.Types (ToJSON(..), Value, camelTo, camelTo2, defaultOptions, omitNothingFields)
14import Data.Char (toUpper)
14import Data.Char (toUpper)
15+import Data.Hashable (hash)
15import Data.List.NonEmpty (NonEmpty(..))
16import Data.List.NonEmpty (NonEmpty(..))
16import Data.Maybe (fromMaybe)
17import Data.Maybe (fromMaybe)
17import Data.Proxy (Proxy(..))
18import Data.Proxy (Proxy(..))
@@ -63,6 +64,7 @@ tests = testGroup "unit" [
63 , testGroup "To JSON representation" $ fmap (testCase "-") jsonEncoding
64 , testGroup "To JSON representation" $ fmap (testCase "-") jsonEncoding
64 , testGroup "From JSON representation" $ fmap (testCase "-") jsonDecoding
65 , testGroup "From JSON representation" $ fmap (testCase "-") jsonDecoding
65 , testGroup "JSONPath" $ fmap (testCase "-") jsonPath
66 , testGroup "JSONPath" $ fmap (testCase "-") jsonPath
67+ , testGroup "Hashable laws" $ fmap (testCase "-") hashableLaws
66 , testGroup "Issue #351" $ fmap (testCase "-") issue351
68 , testGroup "Issue #351" $ fmap (testCase "-") issue351
67 ]
69 ]
68
70
@@ -261,6 +263,18 @@ jsonPath = [
261 (eitherDecode "[0,1,true]" :: Either String (Seq Int))
263 (eitherDecode "[0,1,true]" :: Either String (Seq Int))
262 ]
264 ]
263
265
266+------------------------------------------------------------------------------
267+-- Check that the hashes of two equal Value are the same
268+------------------------------------------------------------------------------
269+
270+hashableLaws :: [Assertion]
271+hashableLaws = [
272+ assertEqual "Hashable Object" (hash a) (hash b)
273+ ]
274+ where
275+ a = object ["223" .= False, "807882556" .= True]
276+ b = object ["807882556" .= True, "223" .= False]
277+
264------------------------------------------------------------------------------
278------------------------------------------------------------------------------
265-- Regressions
279-- Regressions
266------------------------------------------------------------------------------
280------------------------------------------------------------------------------