on master help sign in
Merge pull request #371 from watashi/hash_value

Make sure the hashes of two equal Values are always the same

commit2a9915211087edb19c3fcded890e78ad7d50ea69 authorAdam Bergmark <adam@bergmark.nl> dateThu 10 Mar 2016, 10:56 (11 years ago) parentd9f1ad3c2def3b
3 files changed+28−3
Data/Aeson/Types/Internal.hs+12−2 aeson.cabal+1 tests/UnitTests.hs+15−1
Data/Aeson/Types/Internal.hs+12−2
@@ -90,6 +90,10 @@ import Data.Monoid (Monoid(..))
9090import Data.Traversable (Traversable(..))
9191#endif
9292
93+#if !MIN_VERSION_unordered_containers(0,2,6)
94+import Data.List (foldl', sort)
95+#endif
96+
9397-- | Elements of a JSON path used to describe the location of an
9498-- error.
9599data JSONPathElement = Key Text
@@ -391,8 +395,14 @@ instance IsString Value where
391395 {-# INLINE fromString #-}
392396
393397hashValue :: Int -> Value -> Int
394−hashValue s (Object o) = H.foldl' hashWithSalt
395− (s `hashWithSalt` (0::Int)) o
398+#if MIN_VERSION_unordered_containers(0,2,6)
399+hashValue s (Object o) = s `hashWithSalt` (0::Int) `hashWithSalt` o
400+#else
401+hashValue s (Object o) = foldl' hashWithSalt
402+ (s `hashWithSalt` (0::Int)) assocHashesSorted
403+ where
404+ assocHashesSorted = sort [hash k `hashWithSalt` v | (k, v) <- H.toList o]
405+#endif
396406hashValue s (Array a) = V.foldl' hashWithSalt
397407 (s `hashWithSalt` (1::Int)) a
398408hashValue s (String str) = s `hashWithSalt` (2::Int) `hashWithSalt` str
aeson.cabal+1
@@ -158,6 +158,7 @@ test-suite tests
158158 bytestring,
159159 containers,
160160 ghc-prim >= 0.2,
161+ hashable >= 1.1.2.0,
161162 tagged,
162163 template-haskell,
163164 test-framework,
tests/UnitTests.hs+15−1
@@ -6,12 +6,13 @@ module UnitTests (ioTests, tests) where
66
77import Control.Applicative (Const(..))
88import 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, (.=), (.:), (.:?), (.:!))
1010import Data.Aeson.Encode (encodeToTextBuilder)
1111import Data.Aeson.Internal (JSONPathElement(..), formatError)
1212import Data.Aeson.TH (deriveJSON)
1313import Data.Aeson.Types (ToJSON(..), Value, camelTo, camelTo2, defaultOptions, omitNothingFields)
1414import Data.Char (toUpper)
15+import Data.Hashable (hash)
1516import Data.List.NonEmpty (NonEmpty(..))
1617import Data.Maybe (fromMaybe)
1718import Data.Proxy (Proxy(..))
@@ -63,6 +64,7 @@ tests = testGroup "unit" [
6364 , testGroup "To JSON representation" $ fmap (testCase "-") jsonEncoding
6465 , testGroup "From JSON representation" $ fmap (testCase "-") jsonDecoding
6566 , testGroup "JSONPath" $ fmap (testCase "-") jsonPath
67+ , testGroup "Hashable laws" $ fmap (testCase "-") hashableLaws
6668 , testGroup "Issue #351" $ fmap (testCase "-") issue351
6769 ]
6870
@@ -261,6 +263,18 @@ jsonPath = [
261263 (eitherDecode "[0,1,true]" :: Either String (Seq Int))
262264 ]
263265
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+
264278------------------------------------------------------------------------------
265279-- Regressions
266280------------------------------------------------------------------------------