diff --git a/CHANGELOG.md b/CHANGELOG.md index 45302b3..52f388a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ Changelog ========== +## Version 1.5.0 + +* Switch to faster map data type - all previous usage of + Data.Map.Strict and Data.Set is replaced with Data.HashMap.Strict + and Data.HashSet. + ## Version 1.4.0 * Expose runMinioRes and runMinioResWith (#129) diff --git a/docs/API.md b/docs/API.md index d8ef3f7..d90d60c 100644 --- a/docs/API.md +++ b/docs/API.md @@ -247,12 +247,13 @@ __Return Value__ __ObjectInfo record type__ -| Field | Type | Description | -|:------------|:----------------------------|:---------------------------------| -| `oiObject` | _Object_ (alias for `Text`) | Name of object | -| `oiModTime` | _UTCTime_ | Last modified time of the object | -| `oiETag` | _ETag_ (alias for `Text`) | ETag of the object | -| `oiSize` | _Int64_ | Size of the object in bytes | +| Field | Type | Description | +|:-------------|:----------------------------|:-------------------------------------| +| `oiObject` | _Object_ (alias for `Text`) | Name of object | +| `oiModTime` | _UTCTime_ | Last modified time of the object | +| `oiETag` | _ETag_ (alias for `Text`) | ETag of the object | +| `oiSize` | _Int64_ | Size of the object in bytes | +| `oiMetadata` | _HashMap Text Text_ | Map of key-value user-metadata pairs | __Example__ @@ -928,7 +929,7 @@ main = do ``` -### presignedPostPolicy :: PostPolicy -> Minio (ByteString, Map.Map Text ByteString) +### presignedPostPolicy :: PostPolicy -> Minio (ByteString, HashMap Text ByteString) Generate a presigned URL and POST policy to upload files via a POST request. This is intended for browser uploads and generates form data @@ -965,7 +966,7 @@ import Network.Minio import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as Char8 -import qualified Data.Map.Strict as Map +import qualified Data.HashMap.Strict as H import qualified Data.Text.Encoding as Enc import qualified Data.Time as Time @@ -1005,7 +1006,7 @@ main = do let formFn (k, v) = B.concat ["-F ", Enc.encodeUtf8 k, "=", "'", v, "'"] - formOptions = B.intercalate " " $ map formFn $ Map.toList formData + formOptions = B.intercalate " " $ map formFn $ H.toList formData return $ B.intercalate " " $ diff --git a/examples/PresignedPostPolicy.hs b/examples/PresignedPostPolicy.hs index c079eca..825118a 100755 --- a/examples/PresignedPostPolicy.hs +++ b/examples/PresignedPostPolicy.hs @@ -22,7 +22,7 @@ import Network.Minio import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as Char8 -import qualified Data.Map.Strict as Map +import qualified Data.HashMap.Strict as H import qualified Data.Text.Encoding as Enc import qualified Data.Time as Time @@ -69,7 +69,7 @@ main = do let formFn (k, v) = B.concat ["-F ", Enc.encodeUtf8 k, "=", "'", v, "'"] - formOptions = B.intercalate " " $ map formFn $ Map.toList formData + formOptions = B.intercalate " " $ map formFn $ H.toList formData return $ B.intercalate " " $ diff --git a/minio-hs.cabal b/minio-hs.cabal index 8a081f8..74793cb 100644 --- a/minio-hs.cabal +++ b/minio-hs.cabal @@ -1,5 +1,5 @@ name: minio-hs -version: 1.4.0 +version: 1.5.0 synopsis: A MinIO Haskell Library for Amazon S3 compatible cloud storage. description: The MinIO Haskell client library provides simple APIs to @@ -57,7 +57,6 @@ library , conduit >= 1.3 , conduit-extra >= 1.3 , connection - , containers >= 0.5 , cryptonite >= 0.25 , cryptonite-conduit >= 0.2 , digest >= 0.0.1 @@ -153,7 +152,6 @@ test-suite minio-hs-live-server-test , conduit , conduit-extra , connection - , containers , cryptonite , cryptonite-conduit , digest @@ -200,7 +198,6 @@ test-suite minio-hs-test , conduit , conduit-extra , connection - , containers , cryptonite , cryptonite-conduit , digest diff --git a/src/Network/Minio/API.hs b/src/Network/Minio/API.hs index 3d29354..7359bca 100644 --- a/src/Network/Minio/API.hs +++ b/src/Network/Minio/API.hs @@ -34,7 +34,7 @@ import Control.Retry (fullJitterBackoff, import qualified Data.ByteString as B import qualified Data.Char as C import qualified Data.Conduit as C -import qualified Data.Map as Map +import qualified Data.HashMap.Strict as H import qualified Data.Text as T import qualified Data.Time.Clock as Time import Network.HTTP.Conduit (Response) @@ -95,7 +95,7 @@ getRegionHost r = do if "amazonaws.com" `T.isSuffixOf` connectHost ci then maybe (throwIO $ MErrVRegionNotSupported r) - return (Map.lookup r awsRegionMap) + return (H.lookup r awsRegionMap) else return $ connectHost ci buildRequest :: S3ReqInfo -> Minio NC.Request diff --git a/src/Network/Minio/Data.hs b/src/Network/Minio/Data.hs index 6f89f55..2bd10f1 100644 --- a/src/Network/Minio/Data.hs +++ b/src/Network/Minio/Data.hs @@ -32,7 +32,6 @@ import qualified Data.ByteString as B import Data.CaseInsensitive (mk) import qualified Data.HashMap.Strict as H import qualified Data.Ini as Ini -import qualified Data.Map as Map import Data.String (IsString (..)) import qualified Data.Text as T import qualified Data.Text.Encoding as TE @@ -74,8 +73,8 @@ maxMultipartParts = 10000 -- type should have a IsString instance to infer the appropriate -- constant. -- | awsRegionMap - library constant -awsRegionMap :: Map.Map Text Text -awsRegionMap = Map.fromList [ +awsRegionMap :: H.HashMap Text Text +awsRegionMap = H.fromList [ ("us-east-1", "s3.amazonaws.com") , ("us-east-2", "s3-us-east-2.amazonaws.com") , ("us-west-1", "s3-us-west-1.amazonaws.com") @@ -440,8 +439,8 @@ data ObjectInfo = ObjectInfo , oiModTime :: UTCTime -- ^ Mdification time of the object , oiETag :: ETag -- ^ ETag of the object , oiSize :: Int64 -- ^ Size of the object in bytes - , oiMetadata :: Map.Map Text Text -- ^ A map of the metadata - -- key-value pairs + , oiMetadata :: H.HashMap Text Text -- ^ A map of the metadata + -- key-value pairs } deriving (Show, Eq) -- | Represents source object in server-side copy object @@ -928,7 +927,7 @@ getS3Path b o = -- seconds. The maximum duration that can be specified is 7 days. type UrlExpiry = Int -type RegionMap = Map.Map Bucket Region +type RegionMap = H.HashMap Bucket Region -- | The Minio Monad - all computations accessing object storage -- happens in it. @@ -991,7 +990,7 @@ runMinioWith conn m = runResourceT $ runMinioResWith conn m -- `MinioConn`. mkMinioConn :: ConnectInfo -> NC.Manager -> IO MinioConn mkMinioConn ci mgr = do - rMapMVar <- M.newMVar Map.empty + rMapMVar <- M.newMVar H.empty return $ MinioConn ci mgr rMapMVar -- | Run the Minio action and return the result or an error. diff --git a/src/Network/Minio/PresignedOperations.hs b/src/Network/Minio/PresignedOperations.hs index ab289c4..d929e7e 100644 --- a/src/Network/Minio/PresignedOperations.hs +++ b/src/Network/Minio/PresignedOperations.hs @@ -39,7 +39,7 @@ module Network.Minio.PresignedOperations import Data.Aeson ((.=)) import qualified Data.Aeson as Json import Data.ByteString.Builder (byteString, toLazyByteString) -import qualified Data.Map.Strict as Map +import qualified Data.HashMap.Strict as H import qualified Data.Text as T import qualified Data.Time as Time import qualified Network.HTTP.Conduit as NC @@ -252,7 +252,7 @@ showPostPolicy = toS . Json.encode -- browser. On success, this function returns a URL and POST -- form-data. presignedPostPolicy :: PostPolicy - -> Minio (ByteString, Map.Map Text ByteString) + -> Minio (ByteString, H.HashMap Text ByteString) presignedPostPolicy p = do ci <- asks mcConnInfo signTime <- liftIO $ Time.getCurrentTime @@ -277,12 +277,12 @@ presignedPostPolicy p = do mkPair (PPCStartsWith k v) = Just (k, v) mkPair (PPCEquals k v) = Just (k, v) mkPair _ = Nothing - formFromPolicy = Map.map toS $ Map.fromList $ catMaybes $ + formFromPolicy = H.map toS $ H.fromList $ catMaybes $ mkPair <$> conditions ppWithCreds - formData = formFromPolicy `Map.union` signData + formData = formFromPolicy `H.union` signData -- compute POST upload URL - bucket = Map.findWithDefault "" "bucket" formData + bucket = H.lookupDefault "" "bucket" formData scheme = byteString $ bool "http://" "https://" $ connectIsSecure ci region = connectRegion ci diff --git a/src/Network/Minio/Sign/V4.hs b/src/Network/Minio/Sign/V4.hs index 57f6824..d5836e2 100644 --- a/src/Network/Minio/Sign/V4.hs +++ b/src/Network/Minio/Sign/V4.hs @@ -22,8 +22,8 @@ import qualified Data.ByteString.Base64 as Base64 import qualified Data.ByteString.Char8 as B8 import Data.CaseInsensitive (mk) import qualified Data.CaseInsensitive as CI -import qualified Data.Map.Strict as Map -import qualified Data.Set as Set +import qualified Data.HashMap.Strict as Map +import qualified Data.HashSet as Set import qualified Data.Time as Time import qualified Network.HTTP.Conduit as NC import Network.HTTP.Types (Header, parseQuery) @@ -39,7 +39,7 @@ import Network.Minio.Errors -- these headers are not included in the string to sign when signing a -- request -ignoredHeaders :: Set ByteString +ignoredHeaders :: Set.HashSet ByteString ignoredHeaders = Set.fromList $ map CI.foldedCase [ H.hAuthorization , H.hContentType @@ -178,7 +178,7 @@ mkScope ts region = B.intercalate "/" getHeadersToSign :: [Header] -> [(ByteString, ByteString)] getHeadersToSign !h = - filter (flip Set.notMember ignoredHeaders . fst) $ + filter ((\hdr -> not $ Set.member hdr ignoredHeaders) . fst) $ map (\(x, y) -> (CI.foldedCase x, stripBS y)) h mkCanonicalRequest :: Bool -> SignParams -> NC.Request -> [(ByteString, ByteString)] @@ -234,7 +234,7 @@ computeSignature !toSign !key = digestToBase16 $ hmacSHA256 toSign key -- and ConnInfo and returns form-data for the POST upload containing -- just the signature and the encoded post-policy. signV4PostPolicy :: ByteString -> SignParams - -> Map.Map Text ByteString + -> Map.HashMap Text ByteString signV4PostPolicy !postPolicyJSON !sp = let stringToSign = Base64.encode postPolicyJSON diff --git a/src/Network/Minio/Utils.hs b/src/Network/Minio/Utils.hs index 8f2d67d..9b17f9b 100644 --- a/src/Network/Minio/Utils.hs +++ b/src/Network/Minio/Utils.hs @@ -23,8 +23,8 @@ import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as LB import Data.CaseInsensitive (mk, original) import qualified Data.Conduit.Binary as CB +import qualified Data.HashMap.Strict as H import qualified Data.List as List -import qualified Data.Map as Map import qualified Data.Text as T import Data.Text.Encoding.Error (lenientDecode) import Data.Text.Read (decimal) @@ -105,8 +105,8 @@ getETagHeader hs = decodeUtf8Lenient <$> lookupHeader Hdr.hETag hs getMetadata :: [HT.Header] -> [(Text, Text)] getMetadata = map ((\(x, y) -> (decodeUtf8Lenient $ original x, decodeUtf8Lenient $ stripBS y))) -getMetadataMap :: [HT.Header] -> Map Text Text -getMetadataMap hs = Map.fromList (getMetadata hs) +getMetadataMap :: [HT.Header] -> H.HashMap Text Text +getMetadataMap hs = H.fromList (getMetadata hs) getLastModifiedHeader :: [HT.Header] -> Maybe UTCTime getLastModifiedHeader hs = do @@ -245,14 +245,14 @@ lookupRegionCache :: Bucket -> Minio (Maybe Region) lookupRegionCache b = do rMVar <- asks mcRegionMap rMap <- UM.readMVar rMVar - return $ Map.lookup b rMap + return $ H.lookup b rMap addToRegionCache :: Bucket -> Region -> Minio () addToRegionCache b region = do rMVar <- asks mcRegionMap - UM.modifyMVar_ rMVar $ return . Map.insert b region + UM.modifyMVar_ rMVar $ return . H.insert b region deleteFromRegionCache :: Bucket -> Minio () deleteFromRegionCache b = do rMVar <- asks mcRegionMap - UM.modifyMVar_ rMVar $ return . Map.delete b + UM.modifyMVar_ rMVar $ return . H.delete b diff --git a/src/Network/Minio/XmlParser.hs b/src/Network/Minio/XmlParser.hs index 561a8bf..6217fba 100644 --- a/src/Network/Minio/XmlParser.hs +++ b/src/Network/Minio/XmlParser.hs @@ -30,8 +30,8 @@ module Network.Minio.XmlParser ) where import qualified Data.ByteString.Lazy as LB +import qualified Data.HashMap.Strict as H import Data.List (zip3, zip4, zip5) -import qualified Data.Map as Map import qualified Data.Text as T import Data.Text.Read (decimal) import Data.Time @@ -149,7 +149,7 @@ parseListObjectsV1Response xmldata = do sizes <- parseDecimals sizeStr let - objects = map (uncurry5 ObjectInfo) $ zip5 keys modTimes etags sizes (repeat Map.empty) + objects = map (uncurry5 ObjectInfo) $ zip5 keys modTimes etags sizes (repeat H.empty) return $ ListObjectsV1Result hasMore nextMarker objects prefixes @@ -178,7 +178,7 @@ parseListObjectsResponse xmldata = do sizes <- parseDecimals sizeStr let - objects = map (uncurry5 ObjectInfo) $ zip5 keys modTimes etags sizes (repeat Map.empty) + objects = map (uncurry5 ObjectInfo) $ zip5 keys modTimes etags sizes (repeat H.empty) return $ ListObjectsResult hasMore nextToken objects prefixes diff --git a/test/LiveServer.hs b/test/LiveServer.hs index ea5f40a..180bfe8 100644 --- a/test/LiveServer.hs +++ b/test/LiveServer.hs @@ -27,7 +27,7 @@ import Data.Conduit (yield) import qualified Data.Conduit as C import qualified Data.Conduit.Binary as CB import Data.Conduit.Combinators (sinkList) -import qualified Data.Map.Strict as Map +import qualified Data.HashMap.Strict as H import qualified Data.Text as T import Data.Time (fromGregorian) import qualified Data.Time as Time @@ -513,8 +513,8 @@ presignedUrlFunTest = funTestWithBucket "presigned Url tests" $ "presigned HEAD failed (presignedHeadObjectUrl)" -- check that header info is accurate - let h = Map.fromList $ NC.responseHeaders headResp - cLen = Map.findWithDefault "0" HT.hContentLength h + let h = H.fromList $ NC.responseHeaders headResp + cLen = H.lookupDefault "0" HT.hContentLength h liftIO $ (cLen == show size2) @? "Head req returned bad content length" step "GET object presigned URL - presignedGetObjectUrl" @@ -580,7 +580,7 @@ presignedPostPolicyFunTest = funTestWithBucket "Presigned Post Policy tests" $ postForm url formData inputFile = do req <- NC.parseRequest $ toS url let parts = map (\(x, y) -> Form.partBS x y) $ - Map.toList formData + H.toList formData parts' = parts ++ [Form.partFile "file" inputFile] req' <- Form.formDataBody parts' req mgr <- NC.newManager NC.tlsManagerSettings @@ -698,7 +698,7 @@ putObjectContentTypeTest = funTestWithBucket "putObject contentType tests" $ let m = oiMetadata oi step "Validate content-type" - liftIO $ assertEqual "Content-Type did not match" (Just "application/javascript") (Map.lookup "Content-Type" m) + liftIO $ assertEqual "Content-Type did not match" (Just "application/javascript") (H.lookup "Content-Type" m) step "upload object with content-encoding set to identity" fPutObject bucket object inputFile defaultPutObjectOptions { @@ -710,7 +710,7 @@ putObjectContentTypeTest = funTestWithBucket "putObject contentType tests" $ step "Validate content-encoding" liftIO $ assertEqual "Content-Encoding did not match" (Just "identity") - (Map.lookup "Content-Encoding" m') + (H.lookup "Content-Encoding" m') step "Cleanup actions" @@ -735,7 +735,7 @@ putObjectContentLanguageTest = funTestWithBucket "putObject contentLanguage test step "Validate content-language" liftIO $ assertEqual "content-language did not match" (Just "en-US") - (Map.lookup "Content-Language" m) + (H.lookup "Content-Language" m) step "Cleanup actions" removeObject bucket object @@ -771,7 +771,7 @@ putObjectStorageClassTest = funTestWithBucket "putObject storageClass tests" $ step "Validate x-amz-storage-class rrs" liftIO $ assertEqual "storageClass did not match" (Just "REDUCED_REDUNDANCY") - (Map.lookup "X-Amz-Storage-Class" m') + (H.lookup "X-Amz-Storage-Class" m') fpE <- try $ fPutObject bucket object'' inputFile'' defaultPutObjectOptions { pooStorageClass = Just "INVALID_STORAGE_CLASS" diff --git a/test/Network/Minio/XmlParser/Test.hs b/test/Network/Minio/XmlParser/Test.hs index 920a1c1..e32b28a 100644 --- a/test/Network/Minio/XmlParser/Test.hs +++ b/test/Network/Minio/XmlParser/Test.hs @@ -19,7 +19,7 @@ module Network.Minio.XmlParser.Test ( xmlParserTests ) where -import qualified Data.Map as Map +import qualified Data.HashMap.Strict as H import Data.Time (fromGregorian) import Test.Tasty import Test.Tasty.HUnit @@ -128,7 +128,7 @@ testParseListObjectsResult = do \" expectedListResult = ListObjectsResult True (Just "opaque") [object1] [] - object1 = ObjectInfo "my-image.jpg" modifiedTime1 "\"fba9dede5f27731c9771645a39863328\"" 434234 Map.empty + object1 = ObjectInfo "my-image.jpg" modifiedTime1 "\"fba9dede5f27731c9771645a39863328\"" 434234 H.empty modifiedTime1 = flip UTCTime 64230 $ fromGregorian 2009 10 12 parsedListObjectsResult <- tryValidationErr $ runTestNS $ parseListObjectsResponse xmldata @@ -155,7 +155,7 @@ testParseListObjectsV1Result = do \" expectedListResult = ListObjectsV1Result True (Just "my-image1.jpg") [object1] [] - object1 = ObjectInfo "my-image.jpg" modifiedTime1 "\"fba9dede5f27731c9771645a39863328\"" 434234 Map.empty + object1 = ObjectInfo "my-image.jpg" modifiedTime1 "\"fba9dede5f27731c9771645a39863328\"" 434234 H.empty modifiedTime1 = flip UTCTime 64230 $ fromGregorian 2009 10 12 parsedListObjectsV1Result <- tryValidationErr $ runTestNS $ parseListObjectsV1Response xmldata