@@ -107,6 +107,9 @@ module Data.HashMap.Internal
107107 , fromListWith
108108 , fromListWithKey
109109
110+ -- * Validity
111+ , valid
112+
110113 -- ** Internals used by the strict version
111114 , Hash
112115 , Bitmap
@@ -2456,3 +2459,85 @@ instance (Eq k, Hashable k) => Exts.IsList (HashMap k v) where
24562459 type Item (HashMap k v ) = (k , v )
24572460 fromList = fromList
24582461 toList = toList
2462+
2463+ ------------------------------------------------------------------------
2464+ -- Validity
2465+
2466+ data Validity k = Invalid (Error k ) SubHashPath | Valid
2467+ deriving (Show )
2468+
2469+ instance Semigroup (Validity k ) where
2470+ Valid <> y = y
2471+ x <> _ = x
2472+
2473+ instance Monoid (Validity k ) where
2474+ mempty = Valid
2475+
2476+ data Error k
2477+ = INV1_internal_Empty
2478+ | INV2_misplaced_hash ! Hash
2479+ | INV3_key_hash_mismatch k ! Hash
2480+ | INV4_collision_size ! Int
2481+ | INV5_bad_BitmapIndexed_size ! Int
2482+ | INV6_bitmap_array_size_mismatch ! Bitmap ! Int
2483+ | INV7_BitmapIndexed_invalid_only_subtree
2484+ | INV8_bad_Full_size ! Int
2485+ deriving (Show )
2486+
2487+ -- | A part of a 'Hash' with 'bitsPerSubkey' bits.
2488+ type SubHash = Word
2489+
2490+ data SubHashPath = Root | Cons ! SubHash ! SubHashPath
2491+ deriving (Show )
2492+
2493+ valid :: Hashable k => HashMap k v -> Validity k
2494+ valid Empty = Valid
2495+ valid t = validInternal Root t
2496+ where
2497+ validInternal p Empty = Invalid INV1_internal_Empty p
2498+ validInternal p (Leaf h l) = validHash p h <> validLeaf p h l
2499+ validInternal p (Collision h ary) = validHash p h <> validCollision p h ary
2500+ validInternal p (BitmapIndexed b ary) = validBitmapIndexed p b ary
2501+ validInternal p (Full ary) = validFull p ary
2502+
2503+ validHash p0 h0 = go p0 h0
2504+ where
2505+ go Root ! _ = Valid
2506+ go (Cons sh p) h | h .&. subkeyMask == sh = go p (h `unsafeShiftR` bitsPerSubkey)
2507+ | otherwise = Invalid (INV2_misplaced_hash h0) p0
2508+
2509+ validLeaf p h (L k _) | hash k == h = Valid
2510+ | otherwise = Invalid (INV3_key_hash_mismatch k h) p
2511+
2512+ -- | TODO: check that keys are distinct
2513+ validCollision p h ary = validCollisionSize (A. length ary) <> A. foldMap (validLeaf p h) ary
2514+ where
2515+ validCollisionSize n | n < 2 = Invalid (INV4_collision_size n) p
2516+ | otherwise = Valid
2517+
2518+ validBitmapIndexed p b ary = validArraySize <> validSubTrees p b ary
2519+ where
2520+ n = A. length ary
2521+ validArraySize | n < 1 || n >= maxChildren = Invalid (INV5_bad_BitmapIndexed_size n) p
2522+ | popCount b == n = Valid
2523+ | otherwise = Invalid (INV6_bitmap_array_size_mismatch b n) p
2524+
2525+ validSubTrees p b ary
2526+ | A. length ary == 1
2527+ , isLeafOrCollision (A. index ary 0 )
2528+ = Invalid INV7_BitmapIndexed_invalid_only_subtree p
2529+ | otherwise = go b
2530+ where
2531+ go 0 = Valid
2532+ go b' = validInternal (Cons (fromIntegral c) p) (A. index ary i) <> go b''
2533+ where
2534+ c = countTrailingZeros b'
2535+ m = 1 `unsafeShiftL` c
2536+ i = sparseIndex b m
2537+ b'' = b' .&. complement m
2538+
2539+ validFull p ary = validArraySize <> validSubTrees p fullBitmap ary
2540+ where
2541+ n = A. length ary
2542+ validArraySize | n == 32 = Valid
2543+ | otherwise = Invalid (INV8_bad_Full_size n) p
0 commit comments