diff options
Diffstat (limited to 'dht/src/Hans')
-rw-r--r-- | dht/src/Hans/Checksum.hs | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/dht/src/Hans/Checksum.hs b/dht/src/Hans/Checksum.hs new file mode 100644 index 00000000..7afc93c7 --- /dev/null +++ b/dht/src/Hans/Checksum.hs | |||
@@ -0,0 +1,136 @@ | |||
1 | {-# LANGUAGE RecordWildCards #-} | ||
2 | {-# LANGUAGE BangPatterns #-} | ||
3 | |||
4 | -- BANNERSTART | ||
5 | -- - Copyright 2006-2008, Galois, Inc. | ||
6 | -- - This software is distributed under a standard, three-clause BSD license. | ||
7 | -- - Please see the file LICENSE, distributed with this software, for specific | ||
8 | -- - terms and conditions. | ||
9 | -- Author: Adam Wick <awick@galois.com> | ||
10 | -- BANNEREND | ||
11 | -- |A module providing checksum computations to other parts of Hans. The | ||
12 | -- checksum here is the standard Internet 16-bit checksum (the one's | ||
13 | -- complement of the one's complement sum of the data). | ||
14 | |||
15 | module Hans.Checksum( | ||
16 | -- * Checksums | ||
17 | computeChecksum, | ||
18 | Checksum(..), | ||
19 | PartialChecksum(), | ||
20 | emptyPartialChecksum, | ||
21 | finalizeChecksum, | ||
22 | stepChecksum, | ||
23 | |||
24 | Pair8(..), | ||
25 | ) where | ||
26 | |||
27 | import Data.Bits (Bits(shiftL,shiftR,complement,clearBit,(.&.))) | ||
28 | import Data.List (foldl') | ||
29 | import Data.Word (Word8,Word16,Word32) | ||
30 | import qualified Data.ByteString as S | ||
31 | import qualified Data.ByteString.Lazy as L | ||
32 | import qualified Data.ByteString.Short as Sh | ||
33 | import qualified Data.ByteString.Unsafe as S | ||
34 | |||
35 | |||
36 | data PartialChecksum = PartialChecksum { pcAccum :: {-# UNPACK #-} !Word32 | ||
37 | , pcCarry :: !(Maybe Word8) | ||
38 | } deriving (Eq,Show) | ||
39 | |||
40 | emptyPartialChecksum :: PartialChecksum | ||
41 | emptyPartialChecksum = PartialChecksum | ||
42 | { pcAccum = 0 | ||
43 | , pcCarry = Nothing | ||
44 | } | ||
45 | |||
46 | finalizeChecksum :: PartialChecksum -> Word16 | ||
47 | finalizeChecksum pc = complement (fromIntegral (fold32 (fold32 result))) | ||
48 | where | ||
49 | fold32 :: Word32 -> Word32 | ||
50 | fold32 x = (x .&. 0xFFFF) + (x `shiftR` 16) | ||
51 | |||
52 | result = case pcCarry pc of | ||
53 | Nothing -> pcAccum pc | ||
54 | Just prev -> stepChecksum (pcAccum pc) prev 0 | ||
55 | {-# INLINE finalizeChecksum #-} | ||
56 | |||
57 | |||
58 | computeChecksum :: Checksum a => a -> Word16 | ||
59 | computeChecksum a = finalizeChecksum (extendChecksum a emptyPartialChecksum) | ||
60 | {-# INLINE computeChecksum #-} | ||
61 | |||
62 | -- | Incremental checksum computation interface. | ||
63 | class Checksum a where | ||
64 | extendChecksum :: a -> PartialChecksum -> PartialChecksum | ||
65 | |||
66 | |||
67 | data Pair8 = Pair8 !Word8 !Word8 | ||
68 | |||
69 | instance Checksum Pair8 where | ||
70 | extendChecksum (Pair8 hi lo) = \ PartialChecksum { .. } -> | ||
71 | case pcCarry of | ||
72 | Nothing -> PartialChecksum { pcAccum = stepChecksum pcAccum hi lo | ||
73 | , pcCarry = Nothing } | ||
74 | Just c -> PartialChecksum { pcAccum = stepChecksum pcAccum c hi | ||
75 | , pcCarry = Just lo } | ||
76 | {-# INLINE extendChecksum #-} | ||
77 | |||
78 | instance Checksum Word16 where | ||
79 | extendChecksum w = \pc -> extendChecksum (Pair8 hi lo) pc | ||
80 | where | ||
81 | lo = fromIntegral w | ||
82 | hi = fromIntegral (w `shiftR` 8) | ||
83 | {-# INLINE extendChecksum #-} | ||
84 | |||
85 | instance Checksum Word32 where | ||
86 | extendChecksum w = \pc -> | ||
87 | extendChecksum (fromIntegral w :: Word16) $ | ||
88 | extendChecksum (fromIntegral (w `shiftR` 16) :: Word16) pc | ||
89 | {-# INLINE extendChecksum #-} | ||
90 | |||
91 | instance Checksum a => Checksum [a] where | ||
92 | extendChecksum as = \pc -> foldl' (flip extendChecksum) pc as | ||
93 | {-# INLINE extendChecksum #-} | ||
94 | |||
95 | instance Checksum L.ByteString where | ||
96 | extendChecksum lbs = \pc -> extendChecksum (L.toChunks lbs) pc | ||
97 | {-# INLINE extendChecksum #-} | ||
98 | |||
99 | -- XXX this could be faster if we could mirror the structure of the instance for | ||
100 | -- S.ByteString | ||
101 | instance Checksum Sh.ShortByteString where | ||
102 | extendChecksum shb = \ pc -> extendChecksum (Sh.fromShort shb) pc | ||
103 | |||
104 | |||
105 | instance Checksum S.ByteString where | ||
106 | extendChecksum b pc | ||
107 | | S.null b = pc | ||
108 | | otherwise = case pcCarry pc of | ||
109 | Nothing -> result | ||
110 | Just prev -> extendChecksum (S.tail b) PartialChecksum | ||
111 | { pcCarry = Nothing | ||
112 | , pcAccum = stepChecksum (pcAccum pc) prev (S.unsafeIndex b 0) | ||
113 | } | ||
114 | where | ||
115 | |||
116 | n' = S.length b | ||
117 | n = clearBit n' 0 -- aligned to two | ||
118 | |||
119 | result = PartialChecksum | ||
120 | { pcAccum = loop (pcAccum pc) 0 | ||
121 | , pcCarry = carry | ||
122 | } | ||
123 | |||
124 | carry | ||
125 | | odd n' = Just $! S.unsafeIndex b n | ||
126 | | otherwise = Nothing | ||
127 | |||
128 | loop !acc off | ||
129 | | off < n = loop (stepChecksum acc hi lo) (off + 2) | ||
130 | | otherwise = acc | ||
131 | where hi = S.unsafeIndex b off | ||
132 | lo = S.unsafeIndex b (off+1) | ||
133 | |||
134 | stepChecksum :: Word32 -> Word8 -> Word8 -> Word32 | ||
135 | stepChecksum acc hi lo = acc + fromIntegral hi `shiftL` 8 + fromIntegral lo | ||
136 | {-# INLINE stepChecksum #-} | ||