This repository has been archived by the owner on Jan 18, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 55
/
Base64.hsc
139 lines (116 loc) · 5.37 KB
/
Base64.hsc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
{- -*- haskell -*- -}
-- |An interface to Base64 codec.
module OpenSSL.EVP.Base64
( -- * Encoding
encodeBase64
, encodeBase64BS
, encodeBase64LBS
-- * Decoding
, decodeBase64
, decodeBase64BS
, decodeBase64LBS
)
where
import Control.Exception
import qualified Data.ByteString as B
import Data.ByteString.Base
import qualified Data.ByteString.Char8 as B8
import qualified Data.ByteString.Lazy.Char8 as L8
import Data.List
import Foreign
import Foreign.C
import OpenSSL.Utils
-- エンコード時: 最低 3 バイト以上になるまで次のブロックを取り出し續け
-- る。返された[ByteString] は B.concat してから、その文字列長より小さ
-- な最大の 3 の倍數の位置で分割し、殘りは次のブロックの一部と見做す。
--
-- デコード時: 分割のアルゴリズムは同じだが最低バイト数が 4。
nextBlock :: Int -> ([ByteString], LazyByteString) -> ([ByteString], LazyByteString)
nextBlock _ (xs, LPS [] ) = (xs, LPS [])
nextBlock minLen (xs, LPS src) = if foldl' (+) 0 (map B.length xs) >= minLen then
(xs, LPS src)
else
case src of
(y:ys) -> nextBlock minLen (xs ++ [y], LPS ys)
{- encode -------------------------------------------------------------------- -}
foreign import ccall unsafe "EVP_EncodeBlock"
_EncodeBlock :: Ptr CChar -> Ptr CChar -> Int -> IO Int
encodeBlock :: ByteString -> ByteString
encodeBlock inBS
= unsafePerformIO $
unsafeUseAsCStringLen inBS $ \ (inBuf, inLen) ->
createAndTrim maxOutLen $ \ outBuf ->
_EncodeBlock (unsafeCoercePtr outBuf) inBuf inLen
where
maxOutLen = (inputLen `div` 3 + 1) * 4 + 1 -- +1: '\0'
inputLen = B.length inBS
-- |@'encodeBase64' str@ lazilly encodes a stream of data to
-- Base64. The string doesn't have to be finite. Note that the string
-- must not contain any letters which aren't in the range of U+0000 -
-- U+00FF.
encodeBase64 :: String -> String
encodeBase64 = L8.unpack . encodeBase64LBS . L8.pack
-- |@'encodeBase64BS' bs@ strictly encodes a chunk of data to Base64.
encodeBase64BS :: ByteString -> ByteString
encodeBase64BS = encodeBlock
-- |@'encodeBase64LBS' lbs@ lazilly encodes a stream of data to
-- Base64. The string doesn't have to be finite.
encodeBase64LBS :: LazyByteString -> LazyByteString
encodeBase64LBS inLBS
| L8.null inLBS = L8.empty
| otherwise
= let (blockParts', remain' ) = nextBlock 3 ([], inLBS)
block' = B.concat blockParts'
blockLen' = B.length block'
(block , leftover) = if blockLen' < 3 then
-- 最後の半端
(block', B.empty)
else
B.splitAt (blockLen' - blockLen' `mod` 3) block'
remain = if B.null leftover then
remain'
else
case remain' of
LPS xs -> LPS (leftover:xs)
encodedBlock = encodeBlock block
LPS encodedRemain = encodeBase64LBS remain
in
LPS ([encodedBlock] ++ encodedRemain)
{- decode -------------------------------------------------------------------- -}
foreign import ccall unsafe "EVP_DecodeBlock"
_DecodeBlock :: Ptr CChar -> Ptr CChar -> Int -> IO Int
decodeBlock :: ByteString -> ByteString
decodeBlock inBS
= assert (B.length inBS `mod` 4 == 0) $
unsafePerformIO $
unsafeUseAsCStringLen inBS $ \ (inBuf, inLen) ->
createAndTrim (B.length inBS) $ \ outBuf ->
_DecodeBlock (unsafeCoercePtr outBuf) inBuf inLen
-- |@'decodeBase64' str@ lazilly decodes a stream of data from
-- Base64. The string doesn't have to be finite.
decodeBase64 :: String -> String
decodeBase64 = L8.unpack . decodeBase64LBS . L8.pack
-- |@'decodeBase64BS' bs@ strictly decodes a chunk of data from
-- Base64.
decodeBase64BS :: ByteString -> ByteString
decodeBase64BS = decodeBlock
-- |@'decodeBase64LBS' lbs@ lazilly decodes a stream of data from
-- Base64. The string doesn't have to be finite.
decodeBase64LBS :: LazyByteString -> LazyByteString
decodeBase64LBS inLBS
| L8.null inLBS = L8.empty
| otherwise
= let (blockParts', remain' ) = nextBlock 4 ([], inLBS)
block' = B.concat blockParts'
blockLen' = B.length block'
(block , leftover) = assert (blockLen' >= 4) $
B.splitAt (blockLen' - blockLen' `mod` 4) block'
remain = if B.null leftover then
remain'
else
case remain' of
LPS xs -> LPS (leftover:xs)
decodedBlock = decodeBlock block
LPS decodedRemain = decodeBase64LBS remain
in
LPS ([decodedBlock] ++ decodedRemain)