Skip to content

Commit 4f95aea

Browse files
committed
Add valid
Context: #366
1 parent f15c3c2 commit 4f95aea

File tree

1 file changed

+85
-0
lines changed

1 file changed

+85
-0
lines changed

Data/HashMap/Internal.hs

+85
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)