case-insensitive-1.1.0.2/0000755000000000000000000000000012250047620013306 5ustar0000000000000000case-insensitive-1.1.0.2/case-insensitive.cabal0000644000000000000000000000423112250047620017543 0ustar0000000000000000name: case-insensitive version: 1.1.0.2 cabal-version: >=1.8 build-type: Simple license: BSD3 license-file: LICENSE copyright: 2011 Bas van Dijk author: Bas van Dijk maintainer: Bas van Dijk homepage: https://github.com/basvandijk/case-insensitive bug-reports: https://github.com/basvandijk/case-insensitive/issues category: Data, Text synopsis: Case insensitive string comparison description: The module @Data.CaseInsensitive@ provides the 'CI' type constructor which can be parameterised by a string-like type like: 'String', 'ByteString', 'Text', etc.. Comparisons of values of the resulting type will be insensitive to cases. extra-source-files: README.markdown source-repository head Type: git Location: git://github.com/basvandijk/case-insensitive.git Library ghc-options: -Wall build-depends: base >= 3 && < 4.8 , bytestring >= 0.9 && < 0.11 , text >= 0.3 && < 1.1 , deepseq >= 1.1 && < 1.4 , hashable >= 1.0 && < 1.3 exposed-modules: Data.CaseInsensitive, Data.CaseInsensitive.Unsafe other-modules: Data.CaseInsensitive.Internal test-suite test-case-insensitive type: exitcode-stdio-1.0 main-is: test.hs hs-source-dirs: test build-depends: case-insensitive , base >= 3 && < 4.8 , bytestring >= 0.9 && < 0.11 , text >= 0.3 && < 1.1 , HUnit >= 1.2.2 && < 1.3 , test-framework >= 0.2.4 && < 0.9 , test-framework-hunit >= 0.2.4 && < 0.4 ghc-options: -Wall benchmark bench-case-insensitive type: exitcode-stdio-1.0 main-is: bench.hs hs-source-dirs: bench ghc-options: -Wall -O2 build-depends: case-insensitive , base >= 3 && < 4.8 , bytestring >= 0.9 && < 0.11 , criterion >= 0.6.1 && < 0.9 , deepseq >= 1.1 && < 1.4 case-insensitive-1.1.0.2/Setup.lhs0000644000000000000000000000011412250047620015112 0ustar0000000000000000#!/usr/bin/env runhaskell > import Distribution.Simple > main = defaultMain case-insensitive-1.1.0.2/README.markdown0000644000000000000000000000035412250047620016011 0ustar0000000000000000The module `Data.CaseInsensitive` provides the `CI` type constructor which can be parameterised by a string-like type like: `String`, `ByteString`, `Text`, etc.. Comparisons of values of the resulting type will be insensitive to cases. case-insensitive-1.1.0.2/LICENSE0000644000000000000000000000275412250047620014323 0ustar0000000000000000Copyright (c) 2011-2013 Bas van Dijk All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * The name of Bas van Dijk and the names of contributors may NOT be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. case-insensitive-1.1.0.2/test/0000755000000000000000000000000012250047620014265 5ustar0000000000000000case-insensitive-1.1.0.2/test/test.hs0000644000000000000000000000714212250047620015604 0ustar0000000000000000module Main ( main ) where import Data.ByteString ( ByteString ) import qualified Data.ByteString.Char8 as BC8 ( pack, map ) import qualified Data.ByteString.Lazy as Lazy ( ByteString ) import qualified Data.ByteString.Lazy.Char8 as BLC8 ( pack, map ) import qualified Data.CaseInsensitive as CI ( mk ) import Data.Char ( toUpper, chr ) import Data.Text ( Text ) import qualified Data.Text as T ( pack, toUpper ) import qualified Data.Text.Lazy as Lazy ( Text ) import qualified Data.Text.Lazy as TL ( pack, toUpper ) import Test.Framework ( defaultMain, testGroup ) import Test.Framework.Providers.HUnit ( testCase ) import Test.HUnit ( assertEqual ) main :: IO () main = defaultMain [ testGroup "ASCII" [ testCase "String" $ assertEqual "" (CI.mk asciiStr) (CI.mk ( map toUpper asciiStr)) , testCase "ByteString" $ assertEqual "" (CI.mk asciiBs) (CI.mk ( BC8.map toUpper asciiBs)) , testCase "Lazy.ByteString" $ assertEqual "" (CI.mk asciiLBs) (CI.mk (BLC8.map toUpper asciiLBs)) , testCase "Text" $ assertEqual "" (CI.mk asciiTxt) (CI.mk ( T.toUpper asciiTxt)) , testCase "Lazy.Text" $ assertEqual "" (CI.mk asciiLTxt) (CI.mk ( TL.toUpper asciiLTxt)) ] , testGroup "ISO-8859-1" [ testCase "String" $ assertEqual "" (CI.mk iso_8859_1Str) (CI.mk ( map toUpper iso_8859_1Str)) , testCase "ByteString" $ assertEqual "" (CI.mk iso_8859_1Bs) (CI.mk ( BC8.map toUpper' iso_8859_1Bs)) , testCase "Lazy.ByteString" $ assertEqual "" (CI.mk iso_8859_1LBs) (CI.mk (BLC8.map toUpper' iso_8859_1LBs)) , testCase "Text" $ assertEqual "" (CI.mk iso_8859_1Txt) (CI.mk ( T.toUpper iso_8859_1Txt)) , testCase "Lazy.Text" $ assertEqual "" (CI.mk iso_8859_1LTxt) (CI.mk ( TL.toUpper iso_8859_1LTxt)) ] ] asciiLTxt :: Lazy.Text asciiLTxt = TL.pack asciiStr asciiTxt :: Text asciiTxt = T.pack asciiStr asciiLBs :: Lazy.ByteString asciiLBs = BLC8.pack asciiStr asciiBs :: ByteString asciiBs = BC8.pack asciiStr asciiStr :: String asciiStr = map chr [0..127] iso_8859_1LTxt :: Lazy.Text iso_8859_1LTxt = TL.pack iso_8859_1Str iso_8859_1Txt :: Text iso_8859_1Txt = T.pack iso_8859_1Str iso_8859_1LBs :: Lazy.ByteString iso_8859_1LBs = BLC8.pack iso_8859_1Str iso_8859_1Bs :: ByteString iso_8859_1Bs = BC8.pack iso_8859_1Str iso_8859_1Str :: String iso_8859_1Str = asciiStr ++ map chr [128..255] -- | Upper-casing some characters in ISO 8859-1 move them outside the 0-255 range. toUpper' :: Char -> Char toUpper' 'µ' = 'µ' -- toUpper 'µ' (code point: 181) == 'Μ' (code point: 924) toUpper' 'ÿ' = 'ÿ' -- toUpper 'ÿ' (code point: 255) == 'Ÿ' (code point: 376) toUpper' c = toUpper c case-insensitive-1.1.0.2/Data/0000755000000000000000000000000012250047620014157 5ustar0000000000000000case-insensitive-1.1.0.2/Data/CaseInsensitive.hs0000644000000000000000000000205712250047620017613 0ustar0000000000000000{-# LANGUAGE CPP, NoImplicitPrelude #-} #if __GLASGOW_HASKELL__ >= 702 {-# LANGUAGE Trustworthy #-} #endif ----------------------------------------------------------------------------- -- | -- Module : Data.CaseInsensitive -- Copyright : (c) 2011-2013 Bas van Dijk -- License : BSD-style (see the file LICENSE) -- Maintainer : Bas van Dijk -- -- This module is intended to be imported qualified. May I suggest: -- -- @ -- import Data.CaseInsensitive ( CI ) -- import qualified Data.CaseInsensitive as CI -- @ -- -- /Note that the FoldCase instance for ByteStrings is only/ -- /guaranteed to be correct for ISO-8859-1 encoded strings!/ -- ----------------------------------------------------------------------------- module Data.CaseInsensitive ( CI , mk , original , foldedCase , map , FoldCase(foldCase) ) where import Data.CaseInsensitive.Internal case-insensitive-1.1.0.2/Data/CaseInsensitive/0000755000000000000000000000000012250047620017253 5ustar0000000000000000case-insensitive-1.1.0.2/Data/CaseInsensitive/Internal.hs0000644000000000000000000001466412250047620021376 0ustar0000000000000000{-# LANGUAGE CPP, NoImplicitPrelude, DeriveDataTypeable #-} #if __GLASGOW_HASKELL__ >= 702 {-# LANGUAGE Unsafe #-} #endif ----------------------------------------------------------------------------- -- | -- Module : Data.CaseInsensitive.Internal -- Copyright : (c) 2011-2013 Bas van Dijk -- License : BSD-style (see the file LICENSE) -- Maintainer : Bas van Dijk -- -- Internal module which exports the 'CI' type, constructor, -- associated instances and the 'FoldCase' class and instances. -- ----------------------------------------------------------------------------- module Data.CaseInsensitive.Internal ( CI , mk , unsafeMk , original , foldedCase , map , FoldCase(foldCase) ) where -------------------------------------------------------------------------------- -- Imports -------------------------------------------------------------------------------- -- from base: import Data.Bool ( (||) ) import Data.Char ( Char, toLower ) import Data.Eq ( Eq, (==) ) import Data.Function ( on ) import Data.Monoid ( Monoid, mempty, mappend ) import Data.Ord ( Ord, compare ) import Data.String ( IsString, fromString ) import Data.Typeable ( Typeable ) import Data.Word ( Word8 ) import Prelude ( String, (.), fmap, (&&), (+), (<=), (>=), otherwise ) import Text.Read ( Read, readPrec ) import Text.Show ( Show, showsPrec ) import qualified Data.List as L ( map ) #if __GLASGOW_HASKELL__ < 700 import Control.Monad ( (>>) ) import Prelude ( fromInteger ) #endif -- from bytestring: import qualified Data.ByteString as B ( ByteString, map ) import qualified Data.ByteString.Lazy as BL ( ByteString, map ) -- from text: import qualified Data.Text as T ( Text, toCaseFold ) import qualified Data.Text.Lazy as TL ( Text, toCaseFold, pack, unpack ) -- from deepseq: import Control.DeepSeq ( NFData, rnf, deepseq ) -- from hashable: import Data.Hashable ( Hashable, hashWithSalt ) -------------------------------------------------------------------------------- -- Case Insensitive Strings -------------------------------------------------------------------------------- {-| A @CI s@ provides /C/ase /I/nsensitive comparison for the string-like type @s@ (for example: 'String', 'T.Text', 'B.ByteString', etc.). Note that @CI s@ has an instance for 'IsString' which together with the @OverloadedStrings@ language extension allows you to write case insensitive string literals as in: @ \> (\"Content-Type\" :: 'CI' 'T.Text') == (\"CONTENT-TYPE\" :: 'CI' 'T.Text') True @ -} data CI s = CI { original :: !s -- ^ Retrieve the original string-like value. , foldedCase :: !s -- ^ Retrieve the case folded string-like value. -- (Also see 'foldCase'). } deriving Typeable -- | Make the given string-like value case insensitive. mk :: FoldCase s => s -> CI s mk s = CI s (foldCase s) -- | Constructs a 'CI' from an already case folded string-like -- value. The given string is used both as the 'original' as well as -- the 'foldedCase'. -- -- This function is unsafe since the compiler can't guarantee that the -- provided string is case folded. unsafeMk :: FoldCase s => s -> CI s unsafeMk s = CI s s -- | Transform the original string-like value but keep it case insensitive. map :: FoldCase s2 => (s1 -> s2) -> (CI s1 -> CI s2) map f = mk . f . original instance (IsString s, FoldCase s) => IsString (CI s) where fromString = mk . fromString instance Monoid s => Monoid (CI s) where mempty = CI mempty mempty CI o1 l1 `mappend` CI o2 l2 = CI (o1 `mappend` o2) (l1 `mappend` l2) instance Eq s => Eq (CI s) where (==) = (==) `on` foldedCase instance Ord s => Ord (CI s) where compare = compare `on` foldedCase instance (Read s, FoldCase s) => Read (CI s) where readPrec = fmap mk readPrec instance Show s => Show (CI s) where showsPrec prec = showsPrec prec . original instance Hashable s => Hashable (CI s) where hashWithSalt salt = hashWithSalt salt . foldedCase instance NFData s => NFData (CI s) where rnf (CI o f) = o `deepseq` f `deepseq` () -------------------------------------------------------------------------------- -- Folding (lowering) cases -------------------------------------------------------------------------------- -- | Class of string-like types that support folding cases. -- -- /Note/: In some languages, case conversion is a locale- and context-dependent -- operation. The @foldCase@ method is /not/ intended to be locale sensitive. -- Programs that require locale sensitivity should use appropriate versions of -- the case mapping functions from the @text-icu@ package: -- class FoldCase s where foldCase :: s -> s foldCaseList :: [s] -> [s] foldCaseList = L.map foldCase instance FoldCase a => FoldCase [a] where foldCase = foldCaseList -- | Note that @foldCase@ on @'B.ByteString's@ is only guaranteed to be correct for ISO-8859-1 encoded strings! instance FoldCase B.ByteString where foldCase = B.map toLower8 -- | Note that @foldCase@ on @'BL.ByteString's@ is only guaranteed to be correct for ISO-8859-1 encoded strings! instance FoldCase BL.ByteString where foldCase = BL.map toLower8 instance FoldCase Char where foldCase = toLower foldCaseList = TL.unpack . TL.toCaseFold . TL.pack instance FoldCase T.Text where foldCase = T.toCaseFold instance FoldCase TL.Text where foldCase = TL.toCaseFold instance FoldCase (CI s) where foldCase (CI _ l) = CI l l toLower8 :: Word8 -> Word8 toLower8 w | 65 <= w && w <= 90 || 192 <= w && w <= 214 || 216 <= w && w <= 222 = w + 32 | otherwise = w -------------------------------------------------------------------------------- -- Rewrite RULES -------------------------------------------------------------------------------- {-# RULES "mk/ByteString" forall (bs :: B.ByteString). mk bs = CI bs (foldCaseBS bs) #-} foldCaseBS :: B.ByteString -> B.ByteString foldCaseBS bs = B.map toLower8' bs where toLower8' :: Word8 -> Word8 toLower8' w | 65 <= w && w <= 90 || 192 <= w && w <= 214 || 216 <= w && w <= 222 = w + 32 | otherwise = w case-insensitive-1.1.0.2/Data/CaseInsensitive/Unsafe.hs0000644000000000000000000000121012250047620021022 0ustar0000000000000000{-# LANGUAGE CPP, NoImplicitPrelude #-} #if __GLASGOW_HASKELL__ >= 702 {-# LANGUAGE Unsafe #-} #endif ----------------------------------------------------------------------------- -- | -- Module : Data.CaseInsensitive.Unsafe -- Copyright : (c) 2011-2013 Bas van Dijk -- License : BSD-style (see the file LICENSE) -- Maintainer : Bas van Dijk -- -- Provides an unsafe way to create a case insensitive string-like value. -- ----------------------------------------------------------------------------- module Data.CaseInsensitive.Unsafe ( unsafeMk ) where import Data.CaseInsensitive.Internal ( unsafeMk ) case-insensitive-1.1.0.2/bench/0000755000000000000000000000000012250047620014365 5ustar0000000000000000case-insensitive-1.1.0.2/bench/bench.hs0000644000000000000000000000121312250047620015775 0ustar0000000000000000{-# LANGUAGE CPP #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Main ( main) where import Criterion.Main ( defaultMain, bcompare, bench, nf ) import qualified Data.ByteString as B ( readFile ) import qualified Data.CaseInsensitive as CI ( mk ) import qualified NoClass as NC ( mk ) #if !MIN_VERSION_bytestring(0,10,0) import Control.DeepSeq ( NFData ) instance NFData ByteString #endif main :: IO () main = do bs <- B.readFile "data/pg2189.txt" defaultMain [ bcompare [ bench "no-class" $ nf (\s -> NC.mk s) bs , bench "case-insensitive" $ nf (\s -> CI.mk s) bs ] ]