diff options
Diffstat (limited to 'src/Codec/Binary/Base85.hs')
-rw-r--r-- | src/Codec/Binary/Base85.hs | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/src/Codec/Binary/Base85.hs b/src/Codec/Binary/Base85.hs new file mode 100644 index 0000000..4aab150 --- /dev/null +++ b/src/Codec/Binary/Base85.hs | |||
@@ -0,0 +1,142 @@ | |||
1 | -- | | ||
2 | -- Module : Codec.Binary.Base85 | ||
3 | -- Copyright : (c) 2007 Magnus Therning | ||
4 | -- License : BSD3 | ||
5 | -- | ||
6 | -- Implemented as described at <http://en.wikipedia.org/wiki/Ascii85>. | ||
7 | -- | ||
8 | -- Further documentation and information can be found at | ||
9 | -- <http://www.haskell.org/haskellwiki/Library/Data_encoding>. | ||
10 | module Codec.Binary.Base85 | ||
11 | ( EncIncData(..) | ||
12 | , EncIncRes(..) | ||
13 | , encodeInc | ||
14 | , encode | ||
15 | , DecIncData(..) | ||
16 | , DecIncRes(..) | ||
17 | , decodeInc | ||
18 | , decode | ||
19 | , chop | ||
20 | , unchop | ||
21 | ) where | ||
22 | |||
23 | import Codec.Binary.Util | ||
24 | |||
25 | import Data.Array | ||
26 | import Data.Bits | ||
27 | import Data.Char | ||
28 | import Data.Maybe | ||
29 | import Data.Word | ||
30 | import qualified Data.Map as M | ||
31 | |||
32 | -- {{{1 enc/dec map | ||
33 | _encMap :: [(Word8, Char)] | ||
34 | _encMap = [(fromIntegral i, chr i) | i <- [33..117]] | ||
35 | |||
36 | -- {{{1 encodeArray | ||
37 | encodeArray :: Array Word8 Char | ||
38 | encodeArray = array (33, 117) _encMap | ||
39 | |||
40 | -- {{{1 decodeMap | ||
41 | decodeMap :: M.Map Char Word8 | ||
42 | decodeMap = M.fromList [(snd i, fst i) | i <- _encMap] | ||
43 | |||
44 | -- {{{1 encode | ||
45 | -- | Incremental encoder function. | ||
46 | encodeInc :: EncIncData -> EncIncRes String | ||
47 | encodeInc e = eI [] e | ||
48 | where | ||
49 | enc4 [0, 0, 0, 0] = "z" | ||
50 | enc4 [0x20, 0x20, 0x20, 0x20] = "y" | ||
51 | enc4 os@[o1, o2, o3, o4] = map (encodeArray !) group | ||
52 | where | ||
53 | group2Word32 = foldl (\ a b -> a `shiftL` 8 + fromIntegral b) 0 os | ||
54 | encodeWord32ToWord8s :: Word32 -> [Word8] | ||
55 | encodeWord32ToWord8s = | ||
56 | map (fromIntegral . (`mod` 85)) . take 5 . iterate (`div` 85) | ||
57 | adjustNReverse = reverse . map (+ 33) | ||
58 | group = (adjustNReverse .encodeWord32ToWord8s) group2Word32 | ||
59 | |||
60 | eI [] EDone = EFinal [] | ||
61 | eI [o1] EDone = EFinal (take 2 cs) | ||
62 | where | ||
63 | cs = enc4 [o1, 0, 0, 1] | ||
64 | eI [o1, o2] EDone = EFinal (take 3 cs) | ||
65 | where | ||
66 | cs = enc4 [o1, o2, 0, 1] | ||
67 | eI [o1, o2, o3] EDone = EFinal (take 4 cs) | ||
68 | where | ||
69 | cs = enc4 [o1, o2, o3, 1] | ||
70 | eI lo (EChunk bs) = doEnc [] (lo ++ bs) | ||
71 | where | ||
72 | doEnc acc (o1:o2:o3:o4:os) = doEnc (acc ++ enc4 [o1, o2, o3, o4]) os | ||
73 | doEnc acc os = EPart acc (eI os) | ||
74 | |||
75 | -- | Encode data. | ||
76 | -- | ||
77 | -- The result will not be enclosed in \<~ ~\>. | ||
78 | encode :: [Word8] -> String | ||
79 | encode = encoder encodeInc | ||
80 | |||
81 | -- {{{1 decode | ||
82 | -- | Incremental decoder function. | ||
83 | decodeInc :: DecIncData String -> DecIncRes String | ||
84 | decodeInc d = dI [] d | ||
85 | where | ||
86 | dec5 cs = let | ||
87 | ds = map (flip M.lookup decodeMap) cs | ||
88 | es@[e1, e2, e3, e4, e5] = map fromJust ds | ||
89 | adjRev = map (\ i -> i - 33) [e5, e4, e3, e2, e1] | ||
90 | group2Word32 = foldl1 (+) . zipWith (*) (map (85 ^) [0..4]) . map fromIntegral | ||
91 | word32ToGroup :: Word32 -> [Word8] | ||
92 | word32ToGroup = map fromIntegral . reverse . take 4 . iterate (`div` 256) | ||
93 | allJust = and . map isJust | ||
94 | in if allJust ds | ||
95 | then Just $ word32ToGroup $ group2Word32 adjRev | ||
96 | else Nothing | ||
97 | |||
98 | dI lo (DChunk s) = doDec [] (lo ++ s) | ||
99 | dI [] DDone = DFinal [] [] | ||
100 | dI cs@[c1, c2] DDone = case doDec [] (cs ++ "uuu") of | ||
101 | (DPart r _) -> DFinal (take 1 r) [] | ||
102 | f -> f | ||
103 | dI cs@[c1, c2, c3] DDone = case doDec [] (cs ++ "uu") of | ||
104 | (DPart r _) -> DFinal (take 2 r) [] | ||
105 | f -> f | ||
106 | dI cs@[c1, c2, c3, c4] DDone = case doDec [] (cs ++ "u") of | ||
107 | (DPart r _) -> DFinal (take 3 r) [] | ||
108 | f -> f | ||
109 | dI lo DDone = DFail [] lo | ||
110 | |||
111 | doDec acc ('z':cs) = doDec (acc ++ [0, 0, 0, 0]) cs | ||
112 | doDec acc ('y':cs) = doDec (acc ++ [0x20, 0x20, 0x20, 0x20]) cs | ||
113 | doDec acc s@(c1:c2:c3:c4:c5:cs) = maybe | ||
114 | (DFail acc s) | ||
115 | (\ bs -> doDec (acc ++ bs) cs) | ||
116 | (dec5 [c1, c2, c3, c4, c5]) | ||
117 | doDec acc cs = DPart acc (dI cs) | ||
118 | |||
119 | -- | Decode data. | ||
120 | -- | ||
121 | -- The input must not be enclosed in \<~ ~\>. | ||
122 | decode :: String -> Maybe [Word8] | ||
123 | decode = decoder decodeInc | ||
124 | |||
125 | -- {{{1 chop | ||
126 | -- | Chop up a string in parts. | ||
127 | -- | ||
128 | -- The length given is rounded down to the nearest multiple of 5. | ||
129 | chop :: Int -- ^ length of individual lines | ||
130 | -> String | ||
131 | -> [String] | ||
132 | chop _ "" = [] | ||
133 | chop n s = let | ||
134 | enc_len | n < 5 = 5 | ||
135 | | otherwise = n `div` 5 * 5 | ||
136 | in take enc_len s : chop n (drop enc_len s) | ||
137 | |||
138 | -- {{{1 unchop | ||
139 | -- | Concatenate the strings into one long string. | ||
140 | unchop :: [String] | ||
141 | -> String | ||
142 | unchop = foldr (++) "" | ||