@@ -107,6 +107,9 @@ module Data.HashMap.Internal
107
107
, fromListWith
108
108
, fromListWithKey
109
109
110
+ -- * Validity
111
+ , valid
112
+
110
113
-- ** Internals used by the strict version
111
114
, Hash
112
115
, Bitmap
@@ -2456,3 +2459,85 @@ instance (Eq k, Hashable k) => Exts.IsList (HashMap k v) where
2456
2459
type Item (HashMap k v ) = (k , v )
2457
2460
fromList = fromList
2458
2461
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