netserver/Codec/Binary/Base64.hs
author unc0rr
Tue, 07 Apr 2009 14:02:06 +0000
changeset 1948 1e0e1f03180d
parent 1747 44a6a9924c6d
permissions -rw-r--r--
Fix epic bug randomly teleporting hedgehogs
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
1747
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
     1
-- |
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
     2
-- Module    : Codec.Binary.Base64
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
     3
-- Copyright : (c) 2007 Magnus Therning
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
     4
-- License   : BSD3
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
     5
--
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
     6
-- Implemented as specified in RFC 4648
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
     7
-- (<http://tools.ietf.org/html/rfc4648>).
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
     8
--
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
     9
-- Further documentation and information can be found at
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    10
-- <http://www.haskell.org/haskellwiki/Library/Data_encoding>.
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    11
module Codec.Binary.Base64
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    12
    ( encode
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    13
    , decode
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    14
    , decode'
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    15
    , chop
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    16
    , unchop
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    17
    ) where
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    18
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    19
import Control.Monad
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    20
import Data.Array
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    21
import Data.Bits
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    22
import Data.Maybe
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    23
import Data.Word
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    24
import qualified Data.Map as M
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    25
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    26
-- {{{1 enc/dec map
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    27
_encMap =
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    28
    [ (0, 'A'), (1, 'B'), (2, 'C'), (3, 'D'), (4, 'E')
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    29
    , (5, 'F') , (6, 'G'), (7, 'H'), (8, 'I'), (9, 'J')
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    30
    , (10, 'K'), (11, 'L'), (12, 'M'), (13, 'N'), (14, 'O')
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    31
    , (15, 'P'), (16, 'Q'), (17, 'R'), (18, 'S'), (19, 'T')
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    32
    , (20, 'U'), (21, 'V'), (22, 'W'), (23, 'X'), (24, 'Y')
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    33
    , (25, 'Z'), (26, 'a'), (27, 'b'), (28, 'c'), (29, 'd')
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    34
    , (30, 'e'), (31, 'f'), (32, 'g'), (33, 'h'), (34, 'i')
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    35
    , (35, 'j'), (36, 'k'), (37, 'l'), (38, 'm'), (39, 'n')
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    36
    , (40, 'o'), (41, 'p'), (42, 'q'), (43, 'r'), (44, 's')
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    37
    , (45, 't'), (46, 'u'), (47, 'v'), (48, 'w'), (49, 'x')
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    38
    , (50, 'y'), (51, 'z'), (52, '0'), (53, '1'), (54, '2')
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    39
    , (55, '3'), (56, '4'), (57, '5'), (58, '6'), (59, '7')
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    40
    , (60, '8'), (61, '9'), (62, '+'), (63, '/') ]
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    41
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    42
-- {{{1 encodeArray
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    43
encodeArray :: Array Word8 Char
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    44
encodeArray = array (0, 64) _encMap
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    45
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    46
-- {{{1 decodeMap
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    47
decodeMap :: M.Map Char Word8
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    48
decodeMap  = M.fromList [(snd i, fst i) | i <- _encMap]
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    49
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    50
-- {{{1 encode
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    51
-- | Encode data.
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    52
encode :: [Word8]
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    53
    -> String
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    54
encode = let
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    55
        pad n = take n $ repeat 0
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    56
        enc [] = ""
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    57
        enc l@[o] = (++ "==") . take 2 .enc $ l ++ pad 2
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    58
        enc l@[o1, o2] = (++ "=") . take 3 . enc $ l ++ pad 1
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    59
        enc (o1:o2:o3:os) = let
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    60
                i1 = o1 `shiftR` 2
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    61
                i2 = (o1 `shiftL` 4 .|. o2 `shiftR` 4) .&. 0x3f
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    62
                i3 = (o2 `shiftL` 2 .|. o3 `shiftR` 6) .&. 0x3f
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    63
                i4 = o3 .&. 0x3f
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    64
            in (foldr (\ i s -> (encodeArray ! i) : s) "" [i1, i2, i3, i4]) ++ enc os
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    65
    in enc
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    66
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    67
-- {{{1 decode
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    68
-- | Decode data (lazy).
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    69
decode' :: String
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    70
    -> [Maybe Word8]
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    71
decode' = let
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    72
        pad n = take n $ repeat $ Just 0
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    73
        dec [] = []
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    74
        dec l@[Just eo1, Just eo2] = take 1 . dec $ l ++ pad 2
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    75
        dec l@[Just eo1, Just eo2, Just eo3] = take 2 . dec $ l ++ pad 1
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    76
        dec (Just eo1:Just eo2:Just eo3:Just eo4:eos) = let
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    77
                o1 = eo1 `shiftL` 2 .|. eo2 `shiftR` 4
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    78
                o2 = eo2 `shiftL` 4 .|. eo3 `shiftR` 2
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    79
                o3 = eo3 `shiftL` 6 .|. eo4
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    80
            in Just o1:Just o2:Just o3:(dec eos)
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    81
        dec _ = [Nothing]
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    82
    in
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    83
        dec . map (flip M.lookup decodeMap) . takeWhile (/= '=')
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    84
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    85
-- | Decode data (strict).
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    86
decode :: String
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    87
    -> Maybe [Word8]
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    88
decode = sequence . decode'
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    89
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    90
-- {{{1 chop
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    91
-- | Chop up a string in parts.
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    92
--
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    93
--   The length given is rounded down to the nearest multiple of 4.
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    94
--
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    95
--   /Notes:/
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    96
--
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    97
--   * PEM requires lines that are 64 characters long.
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    98
--
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
    99
--   * MIME requires lines that are at most 76 characters long.
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
   100
chop :: Int     -- ^ length of individual lines
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
   101
    -> String
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
   102
    -> [String]
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
   103
chop n "" = []
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
   104
chop n s = let
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
   105
        enc_len | n < 4 = 4
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
   106
                | otherwise = n `div` 4 * 4
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
   107
    in (take enc_len s) : chop n (drop enc_len s)
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
   108
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
   109
-- {{{1 unchop
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
   110
-- | Concatenate the strings into one long string.
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
   111
unchop :: [String]
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
   112
    -> String
44a6a9924c6d Add needed libraries
unc0rr
parents:
diff changeset
   113
unchop = foldr (++) ""