summaryrefslogtreecommitdiff
path: root/src/Codec/Binary/Base85.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Codec/Binary/Base85.hs')
-rw-r--r--src/Codec/Binary/Base85.hs142
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>.
10module 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
23import Codec.Binary.Util
24
25import Data.Array
26import Data.Bits
27import Data.Char
28import Data.Maybe
29import Data.Word
30import 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
37encodeArray :: Array Word8 Char
38encodeArray = array (33, 117) _encMap
39
40-- {{{1 decodeMap
41decodeMap :: M.Map Char Word8
42decodeMap = M.fromList [(snd i, fst i) | i <- _encMap]
43
44-- {{{1 encode
45-- | Incremental encoder function.
46encodeInc :: EncIncData -> EncIncRes String
47encodeInc 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 \<~ ~\>.
78encode :: [Word8] -> String
79encode = encoder encodeInc
80
81-- {{{1 decode
82-- | Incremental decoder function.
83decodeInc :: DecIncData String -> DecIncRes String
84decodeInc 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 \<~ ~\>.
122decode :: String -> Maybe [Word8]
123decode = 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.
129chop :: Int -- ^ length of individual lines
130 -> String
131 -> [String]
132chop _ "" = []
133chop 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.
140unchop :: [String]
141 -> String
142unchop = foldr (++) ""