summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Truzjan <pxqr.sta@gmail.com>2013-11-29 12:06:42 +0400
committerSam Truzjan <pxqr.sta@gmail.com>2013-11-29 12:06:42 +0400
commit9a2ad80fbf5564295842a0833a7cc4a6f2e73b81 (patch)
treea41029abe9fd8fd96efcad318e25b4c3a387d8ba
parent68bd351026d562f4784f6095bbb3c8e8dae46009 (diff)
Return Either instead of error
-rw-r--r--base32-bytestring.cabal2
-rw-r--r--src/Data/ByteString/Base32.hs4
-rw-r--r--src/Data/ByteString/Base32/Hex.hs4
-rw-r--r--src/Data/ByteString/Base32/Internal.hs19
-rw-r--r--tests/Data/ByteString/Base32/HexSpec.hs41
-rw-r--r--tests/Data/ByteString/Base32Spec.hs41
6 files changed, 61 insertions, 50 deletions
diff --git a/base32-bytestring.cabal b/base32-bytestring.cabal
index 1925b62..d314bbd 100644
--- a/base32-bytestring.cabal
+++ b/base32-bytestring.cabal
@@ -46,7 +46,7 @@ library
46 46
47test-suite spec 47test-suite spec
48 default-language: Haskell2010 48 default-language: Haskell2010
49 default-extensions: OverloadedStrings 49 default-extensions:
50 type: exitcode-stdio-1.0 50 type: exitcode-stdio-1.0
51 hs-source-dirs: tests 51 hs-source-dirs: tests
52 main-is: Spec.hs 52 main-is: Spec.hs
diff --git a/src/Data/ByteString/Base32.hs b/src/Data/ByteString/Base32.hs
index efc5951..79939d1 100644
--- a/src/Data/ByteString/Base32.hs
+++ b/src/Data/ByteString/Base32.hs
@@ -57,10 +57,10 @@ decTable = BS.pack $ L.map decW5 [minBound .. maxBound]
57 57
58-- | Decode a base32 encoded bytestring. This functions is 58-- | Decode a base32 encoded bytestring. This functions is
59-- case-insensitive and do not require correct padding. 59-- case-insensitive and do not require correct padding.
60decode :: Base32 -> ByteString 60decode :: Base32 -> Either String ByteString
61decode = pack5 decTable 61decode = pack5 decTable
62 62
63-- | The same as 'decode' but with additional leniency: decodeLenient 63-- | The same as 'decode' but with additional leniency: decodeLenient
64-- will skip non-alphabet characters. 64-- will skip non-alphabet characters.
65decodeLenient :: Base32 -> ByteString 65decodeLenient :: Base32 -> Either String ByteString
66decodeLenient = pack5Lenient decTable \ No newline at end of file 66decodeLenient = pack5Lenient decTable \ No newline at end of file
diff --git a/src/Data/ByteString/Base32/Hex.hs b/src/Data/ByteString/Base32/Hex.hs
index c38c2da..d866570 100644
--- a/src/Data/ByteString/Base32/Hex.hs
+++ b/src/Data/ByteString/Base32/Hex.hs
@@ -55,10 +55,10 @@ decTable = BS.pack $ L.map decW5 [minBound .. maxBound]
55 55
56-- | Decode a base32hex encoded bytestring. This functions is 56-- | Decode a base32hex encoded bytestring. This functions is
57-- case-insensitive and do not requires correct padding. 57-- case-insensitive and do not requires correct padding.
58decode :: Base32Hex -> ByteString 58decode :: Base32Hex -> Either String ByteString
59decode = pack5 decTable 59decode = pack5 decTable
60 60
61-- | The same as 'decode' but with additional leniency: decodeLenient 61-- | The same as 'decode' but with additional leniency: decodeLenient
62-- will skip non-alphabet characters. 62-- will skip non-alphabet characters.
63decodeLenient :: Base32Hex -> ByteString 63decodeLenient :: Base32Hex -> Either String ByteString
64decodeLenient = pack5Lenient decTable \ No newline at end of file 64decodeLenient = pack5Lenient decTable \ No newline at end of file
diff --git a/src/Data/ByteString/Base32/Internal.hs b/src/Data/ByteString/Base32/Internal.hs
index 5902f60..78b1146 100644
--- a/src/Data/ByteString/Base32/Internal.hs
+++ b/src/Data/ByteString/Base32/Internal.hs
@@ -22,6 +22,7 @@ module Data.ByteString.Base32.Internal
22 , invIx 22 , invIx
23 ) where 23 ) where
24 24
25import Control.Exception hiding (mask)
25import Data.Bits.Extras 26import Data.Bits.Extras
26import Data.ByteString as BS 27import Data.ByteString as BS
27import Data.ByteString.Internal as BS 28import Data.ByteString.Internal as BS
@@ -151,9 +152,17 @@ unpack5 (PS fptr off len) bs
151invIx :: Word5 152invIx :: Word5
152invIx = 255 153invIx = 255
153 154
154pack5Ptr :: Ptr Word5 -> ByteString -> ByteString 155type Result = Either String
156
157cleanup :: IO a -> Result a
158cleanup io = unsafePerformIO $
159 catch (io >>= evaluate >>= return . Right) handler
160 where
161 handler (ErrorCall msg) = return (Left msg)
162
163pack5Ptr :: Ptr Word5 -> ByteString -> Result ByteString
155pack5Ptr !tbl bs @ (PS fptr off sz) = 164pack5Ptr !tbl bs @ (PS fptr off sz) =
156 unsafePerformIO $ do 165 cleanup $ do
157 let packedSize = dstSize $ BS.length bs 166 let packedSize = dstSize $ BS.length bs
158 BS.createAndTrim packedSize $ \ dst -> do 167 BS.createAndTrim packedSize $ \ dst -> do
159 withForeignPtr fptr $ \ ptr -> do 168 withForeignPtr fptr $ \ ptr -> do
@@ -162,7 +171,7 @@ pack5Ptr !tbl bs @ (PS fptr off sz) =
162 where 171 where
163 lookupTable :: Word8 -> Word5 172 lookupTable :: Word8 -> Word5
164 lookupTable ix 173 lookupTable ix
165 | x == invIx = error $ "base32: decode: invalid character" ++ show ix 174 | x == invIx = error $ show (w2c ix) ++ " is not base32 character"
166 | otherwise = x 175 | otherwise = x
167 where x = inlinePerformIO (peekByteOff tbl (fromIntegral ix)) 176 where x = inlinePerformIO (peekByteOff tbl (fromIntegral ix))
168 {-# INLINE lookupTable #-} 177 {-# INLINE lookupTable #-}
@@ -221,7 +230,7 @@ pack5Ptr !tbl bs @ (PS fptr off sz) =
221 230
222type DecTable = ByteString 231type DecTable = ByteString
223 232
224pack5 :: DecTable -> ByteString -> ByteString 233pack5 :: DecTable -> ByteString -> Result ByteString
225pack5 (PS fptr off len) bs 234pack5 (PS fptr off len) bs
226 | len /= 256 235 | len /= 256
227 = error $ "base32: pack5: invalid lookup table size " ++ show len 236 = error $ "base32: pack5: invalid lookup table size " ++ show len
@@ -238,7 +247,7 @@ isInAlphabet :: Ptr Word5 -> Word8 -> Bool
238isInAlphabet !tbl !ix = 247isInAlphabet !tbl !ix =
239 inlinePerformIO (peekByteOff tbl (fromIntegral ix)) /= invIx 248 inlinePerformIO (peekByteOff tbl (fromIntegral ix)) /= invIx
240 249
241pack5Lenient :: DecTable -> ByteString -> ByteString 250pack5Lenient :: DecTable -> ByteString -> Either String ByteString
242pack5Lenient tbl @ (PS fptr _ _) bs = 251pack5Lenient tbl @ (PS fptr _ _) bs =
243 unsafePerformIO $ do 252 unsafePerformIO $ do
244 withForeignPtr fptr $ \ !tbl_ptr -> do 253 withForeignPtr fptr $ \ !tbl_ptr -> do
diff --git a/tests/Data/ByteString/Base32/HexSpec.hs b/tests/Data/ByteString/Base32/HexSpec.hs
index 2cdadef..7cddb69 100644
--- a/tests/Data/ByteString/Base32/HexSpec.hs
+++ b/tests/Data/ByteString/Base32/HexSpec.hs
@@ -1,3 +1,4 @@
1{-# LANGUAGE OverloadedStrings #-}
1{-# OPTIONS -fno-warn-orphans #-} 2{-# OPTIONS -fno-warn-orphans #-}
2module Data.ByteString.Base32.HexSpec ( spec ) where 3module Data.ByteString.Base32.HexSpec ( spec ) where
3 4
@@ -28,19 +29,19 @@ spec = do
28 29
29 describe "decode" $ do 30 describe "decode" $ do
30 it "conform rfc examples" $ do 31 it "conform rfc examples" $ do
31 decode "" `shouldBe` "" 32 decode "" `shouldBe` Right ""
32 decode "CO======" `shouldBe` "f" 33 decode "CO======" `shouldBe` Right "f"
33 decode "CPNG====" `shouldBe` "fo" 34 decode "CPNG====" `shouldBe` Right "fo"
34 decode "CPNMU===" `shouldBe` "foo" 35 decode "CPNMU===" `shouldBe` Right "foo"
35 decode "CPNMUOG=" `shouldBe` "foob" 36 decode "CPNMUOG=" `shouldBe` Right "foob"
36 decode "CPNMUOJ1" `shouldBe` "fooba" 37 decode "CPNMUOJ1" `shouldBe` Right "fooba"
37 decode "CPNMUOJ1E8======" `shouldBe` "foobar" 38 decode "CPNMUOJ1E8======" `shouldBe` Right "foobar"
38 39
39 it "inverse for encode" $ property $ \bs -> 40 it "inverse for encode" $ property $ \bs ->
40 decode (encode bs) == bs 41 decode (encode bs) == Right bs
41 42
42 it "case insensitive" $ property $ \bs -> 43 it "case insensitive" $ property $ \bs ->
43 decode (BC.map toLower (encode bs)) == bs 44 decode (BC.map toLower (encode bs)) == Right bs
44 45
45 it "fail gracefully if encoded data contains non alphabet chars" $ do 46 it "fail gracefully if encoded data contains non alphabet chars" $ do
46 evaluate (decode "#=======") `shouldThrow` anyErrorCall 47 evaluate (decode "#=======") `shouldThrow` anyErrorCall
@@ -48,20 +49,20 @@ spec = do
48 49
49 describe "decodeLenient" $ do 50 describe "decodeLenient" $ do
50 it "conform RFC examples" $ do 51 it "conform RFC examples" $ do
51 decode "" `shouldBe` "" 52 decode "" `shouldBe` Right ""
52 decode "CO======" `shouldBe` "f" 53 decode "CO======" `shouldBe` Right "f"
53 decode "CPNG====" `shouldBe` "fo" 54 decode "CPNG====" `shouldBe` Right "fo"
54 decode "CPNMU===" `shouldBe` "foo" 55 decode "CPNMU===" `shouldBe` Right "foo"
55 decode "CPNMUOG=" `shouldBe` "foob" 56 decode "CPNMUOG=" `shouldBe` Right "foob"
56 decode "CPNMUOJ1" `shouldBe` "fooba" 57 decode "CPNMUOJ1" `shouldBe` Right "fooba"
57 decode "CPNMUOJ1E8======" `shouldBe` "foobar" 58 decode "CPNMUOJ1E8======" `shouldBe` Right "foobar"
58 59
59 it "inverse for encode" $ property $ \bs -> 60 it "inverse for encode" $ property $ \bs ->
60 decodeLenient (encode bs) == bs 61 decodeLenient (encode bs) == Right bs
61 62
62 it "case insensitive" $ property $ \bs -> 63 it "case insensitive" $ property $ \bs ->
63 decodeLenient (BC.map toLower (encode bs)) == bs 64 decodeLenient (BC.map toLower (encode bs)) == Right bs
64 65
65 it "skip non alphabet chars" $ do 66 it "skip non alphabet chars" $ do
66 decodeLenient "|" `shouldBe` "" 67 decodeLenient "|" `shouldBe` Right ""
67 decodeLenient "C|O" `shouldBe` "f" 68 decodeLenient "C|O" `shouldBe` Right "f"
diff --git a/tests/Data/ByteString/Base32Spec.hs b/tests/Data/ByteString/Base32Spec.hs
index 148539a..4cec8f9 100644
--- a/tests/Data/ByteString/Base32Spec.hs
+++ b/tests/Data/ByteString/Base32Spec.hs
@@ -1,3 +1,4 @@
1{-# LANGUAGE OverloadedStrings #-}
1{-# OPTIONS -fno-warn-orphans #-} 2{-# OPTIONS -fno-warn-orphans #-}
2module Data.ByteString.Base32Spec (spec) where 3module Data.ByteString.Base32Spec (spec) where
3 4
@@ -34,19 +35,19 @@ spec = do
34 35
35 describe "decode" $ do 36 describe "decode" $ do
36 it "conform RFC examples" $ do 37 it "conform RFC examples" $ do
37 decode "" `shouldBe` "" 38 decode "" `shouldBe` Right ""
38 decode "MY======" `shouldBe` "f" 39 decode "MY======" `shouldBe` Right "f"
39 decode "MZXQ====" `shouldBe` "fo" 40 decode "MZXQ====" `shouldBe` Right "fo"
40 decode "MZXW6===" `shouldBe` "foo" 41 decode "MZXW6===" `shouldBe` Right "foo"
41 decode "MZXW6YQ=" `shouldBe` "foob" 42 decode "MZXW6YQ=" `shouldBe` Right "foob"
42 decode "MZXW6YTB" `shouldBe` "fooba" 43 decode "MZXW6YTB" `shouldBe` Right "fooba"
43 decode "MZXW6YTBOI======" `shouldBe` "foobar" 44 decode "MZXW6YTBOI======" `shouldBe` Right "foobar"
44 45
45 it "inverse for encode" $ property $ \bs -> 46 it "inverse for encode" $ property $ \bs ->
46 decode (encode bs) == bs 47 decode (encode bs) == Right bs
47 48
48 it "case insensitive" $ property $ \bs -> 49 it "case insensitive" $ property $ \bs ->
49 decode (BC.map toLower (encode bs)) == bs 50 decode (BC.map toLower (encode bs)) == Right bs
50 51
51 it "fail gracefully if encoded data contains non alphabet chars" $ do 52 it "fail gracefully if encoded data contains non alphabet chars" $ do
52 evaluate (decode "0=======") `shouldThrow` anyErrorCall 53 evaluate (decode "0=======") `shouldThrow` anyErrorCall
@@ -54,20 +55,20 @@ spec = do
54 55
55 describe "decodeLenient" $ do 56 describe "decodeLenient" $ do
56 it "conform RFC examples" $ do 57 it "conform RFC examples" $ do
57 decodeLenient "" `shouldBe` "" 58 decodeLenient "" `shouldBe` Right ""
58 decodeLenient "MY======" `shouldBe` "f" 59 decodeLenient "MY======" `shouldBe` Right "f"
59 decodeLenient "MZXQ====" `shouldBe` "fo" 60 decodeLenient "MZXQ====" `shouldBe` Right "fo"
60 decodeLenient "MZXW6===" `shouldBe` "foo" 61 decodeLenient "MZXW6===" `shouldBe` Right "foo"
61 decodeLenient "MZXW6YQ=" `shouldBe` "foob" 62 decodeLenient "MZXW6YQ=" `shouldBe` Right "foob"
62 decodeLenient "MZXW6YTB" `shouldBe` "fooba" 63 decodeLenient "MZXW6YTB" `shouldBe` Right "fooba"
63 decodeLenient "MZXW6YTBOI======" `shouldBe` "foobar" 64 decodeLenient "MZXW6YTBOI======" `shouldBe` Right "foobar"
64 65
65 it "inverse for encode" $ property $ \bs -> 66 it "inverse for encode" $ property $ \bs ->
66 decodeLenient (encode bs) == bs 67 decodeLenient (encode bs) == Right bs
67 68
68 it "case insensitive" $ property $ \bs -> 69 it "case insensitive" $ property $ \bs ->
69 decodeLenient (BC.map toLower (encode bs)) == bs 70 decodeLenient (BC.map toLower (encode bs)) == Right bs
70 71
71 it "skip non alphabet chars" $ do 72 it "skip non alphabet chars" $ do
72 decodeLenient "|" `shouldBe` "" 73 decodeLenient "|" `shouldBe` Right ""
73 decodeLenient "M|Y" `shouldBe` "f" 74 decodeLenient "M|Y" `shouldBe` Right "f"