on 3ca519b help sign in
.gitignore+4
since splitsnapshots
unifiedsplit
@@ -1,3 +1,7 @@
1dist
1dist
2+dist-boot
3+dist-install
2cabal-dev
4cabal-dev
3cabal.sandbox.config
5cabal.sandbox.config
6+ghc.mk
7+GNUmakefile
.hgtags+1
@@ -64,3 +64,4 @@ f8a60279237c1c28e094868740d9188dd94fcc9e 1.2.0.0
64631fd055e27fa10983e17e9ad36d1b285b85c661 1.2.1.3
64631fd055e27fa10983e17e9ad36d1b285b85c661 1.2.1.3
658221a68ed5ec37028357df9c6c25f52320e54ad9 1.2.2.0
658221a68ed5ec37028357df9c6c25f52320e54ad9 1.2.2.0
66ce1c5ac65eb9f5580114b4e380092b05a485e95d 1.2.2.1
66ce1c5ac65eb9f5580114b4e380092b05a485e95d 1.2.2.1
67+d72c634a91ffe27ec5cd03921dd53efc8129d8f6 1.2.2.2
.travis.ymlnew file+111
@@ -0,0 +1,111 @@
1+# This Travis job script has been generated by a script via
2+#
3+# make_travis_yml_2.hs '-o' '.travis.yml' 'text.cabal'
4+#
5+# For more information, see https://github.com/hvr/multi-ghc-travis
6+#
7+language: c
8+sudo: false
9+
10+# Bryan threatens to turn off Travis-CI if he gets any email :-)
11+notifications:
12+ email: false
13+
14+cache:
15+ directories:
16+ - $HOME/.cabal/packages
17+ - $HOME/.cabal/store
18+
19+before_cache:
20+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
21+ # remove files that are regenerated by 'cabal update'
22+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.*
23+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/*.json
24+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.cache
25+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar
26+ - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar.idx
27+
28+matrix:
29+ include:
30+ - compiler: "ghc-7.0.4"
31+ # env: TEST=--disable-tests BENCH=--disable-benchmarks
32+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-7.0.4], sources: [hvr-ghc]}}
33+ - compiler: "ghc-7.2.2"
34+ # env: TEST=--disable-tests BENCH=--disable-benchmarks
35+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-7.2.2], sources: [hvr-ghc]}}
36+ - compiler: "ghc-7.4.2"
37+ # env: TEST=--disable-tests BENCH=--disable-benchmarks
38+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-7.4.2], sources: [hvr-ghc]}}
39+ - compiler: "ghc-7.6.3"
40+ # env: TEST=--disable-tests BENCH=--disable-benchmarks
41+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-7.6.3], sources: [hvr-ghc]}}
42+ - compiler: "ghc-7.8.4"
43+ # env: TEST=--disable-tests BENCH=--disable-benchmarks
44+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-7.8.4], sources: [hvr-ghc]}}
45+ - compiler: "ghc-7.10.3"
46+ # env: TEST=--disable-tests BENCH=--disable-benchmarks
47+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-7.10.3], sources: [hvr-ghc]}}
48+ - compiler: "ghc-8.0.2"
49+ # env: TEST=--disable-tests BENCH=--disable-benchmarks
50+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.0.2], sources: [hvr-ghc]}}
51+ - compiler: "ghc-8.2.2"
52+ # env: TEST=--disable-tests BENCH=--disable-benchmarks
53+ addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.2.2], sources: [hvr-ghc]}}
54+
55+before_install:
56+ - HC=${CC}
57+ - HCPKG=${HC/ghc/ghc-pkg}
58+ - unset CC
59+ - PATH=/opt/ghc/bin:/opt/ghc-ppa-tools/bin:$PATH
60+ - PKGNAME='text'
61+
62+install:
63+ - cabal --version
64+ - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
65+ - BENCH=${BENCH---enable-benchmarks}
66+ - TEST=${TEST---enable-tests}
67+ - HADDOCK=${HADDOCK-true}
68+ - INSTALLED=${INSTALLED-true}
69+ - travis_retry cabal update -v
70+ - sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config
71+ - rm -fv cabal.project.local
72+ - "echo 'packages: .' > cabal.project"
73+ - rm -f cabal.project.freeze
74+ - cabal new-build -w ${HC} ${TEST} ${BENCH} --dep -j2 all
75+ - cabal new-build -w ${HC} --disable-tests --disable-benchmarks --dep -j2 all
76+
77+# Here starts the actual work to be performed for the package under test;
78+# any command which exits with a non-zero exit code causes the build to fail.
79+script:
80+ - if [ -f configure.ac ]; then autoreconf -i; fi
81+ - rm -rf .ghc.environment.* dist/
82+ - cabal sdist # test that a source-distribution can be generated
83+ - cd dist/
84+ - SRCTAR=(${PKGNAME}-*.tar.gz)
85+ - SRC_BASENAME="${SRCTAR/%.tar.gz}"
86+ - tar -xvf "./$SRC_BASENAME.tar.gz"
87+ - cd "$SRC_BASENAME/"
88+## from here on, CWD is inside the extracted source-tarball
89+ - rm -fv cabal.project.local
90+ - "echo 'packages: .' > cabal.project"
91+ # this builds all libraries and executables (without tests/benchmarks)
92+ - rm -f cabal.project.freeze
93+ - cabal new-build -w ${HC} --disable-tests --disable-benchmarks all
94+ # this builds all libraries and executables (including tests/benchmarks)
95+ # - rm -rf ./dist-newstyle
96+
97+ # Build with installed constraints for packages in global-db
98+ - if $INSTALLED; then
99+ echo cabal new-build -w ${HC} --disable-tests --disable-benchmarks $(${HCPKG} list --global --simple-output --names-only | sed 's/\([a-zA-Z0-9-]\{1,\}\) */--constraint="\1 installed" /g') all | sh;
100+ else echo "Not building with installed constraints"; fi
101+
102+ # build & run tests, build benchmarks
103+ - cabal new-build -w ${HC} ${TEST} ${BENCH} all
104+ - if [ "x$TEST" = "x--enable-tests" ]; then cabal new-test -w ${HC} ${TEST} all; fi
105+
106+ # haddock
107+ - rm -rf ./dist-newstyle
108+ - if $HADDOCK; then cabal new-haddock -w ${HC} --disable-tests --disable-benchmarks all; else echo "Skipping haddock generation";fi
109+
110+# REGENDATA ["-o",".travis.yml","text.cabal"]
111+# EOF
Data/Text.hs+32−1
@@ -34,6 +34,7 @@
34-- with Unicode text (including normalization, regular expressions,
34-- with Unicode text (including normalization, regular expressions,
35-- non-standard encodings, text breaking, and locales), see
35-- non-standard encodings, text breaking, and locales), see
36-- <http://hackage.haskell.org/package/text-icu the text-icu package >.
36-- <http://hackage.haskell.org/package/text-icu the text-icu package >.
37+--
37
38
38module Data.Text
39module Data.Text
39 (
40 (
@@ -43,6 +44,9 @@ module Data.Text
43 -- * Acceptable data
44 -- * Acceptable data
44 -- $replacement
45 -- $replacement
45
46
47+ -- * Definition of character
48+ -- $character_definition
49+
46 -- * Fusion
50 -- * Fusion
47 -- $fusion
51 -- $fusion
48
52
@@ -60,6 +64,7 @@ module Data.Text
60 , snoc
64 , snoc
61 , append
65 , append
62 , uncons
66 , uncons
67+ , unsnoc
63 , head
68 , head
64 , last
69 , last
65 , tail
70 , tail
@@ -246,6 +251,17 @@ import qualified GHC.Exts as Exts
246import Text.Printf (PrintfArg, formatArg, formatString)
251import Text.Printf (PrintfArg, formatArg, formatString)
247#endif
252#endif
248
253
254+-- $character-definition
255+--
256+-- This package uses the word /character/ to denote Unicode code points.
257+--
258+-- Note that this is not the same thing as a grapheme (e.g. a composition of
259+-- code points that form one visual symbol). For instance, consider the grapheme
260+-- @ä@. This symbol has two Unicode representations: a single code-point
261+-- representation @U+00E4@ (the @LATIN SMALL LETTER A WITH DIAERESIS@ code
262+-- point), and a two code point representation @U+0061@ (the @A@ code point) and
263+-- @U+0308@ (the @COMBINING DIAERESIS@ code point).
264+
249-- $strict
265-- $strict
250--
266--
251-- This package provides both strict and lazy 'Text' types. The
267-- This package provides both strict and lazy 'Text' types. The
@@ -545,6 +561,19 @@ init (Text arr off len) | len <= 0 = emptyError "init"
545 unstream (S.init (stream t)) = init t
561 unstream (S.init (stream t)) = init t
546 #-}
562 #-}
547
563
564+-- | /O(1)/ Returns all but the last character and the last character of a
565+-- 'Text', or 'Nothing' if empty.
566+--
567+-- @since 1.2.3.0
568+unsnoc :: Text -> Maybe (Text, Char)
569+unsnoc (Text arr off len)
570+ | len <= 0 = Nothing
571+ | n < 0xDC00 || n > 0xDFFF = Just (text arr off (len-1), unsafeChr n)
572+ | otherwise = Just (text arr off (len-2), U16.chr2 n0 n)
573+ where n = A.unsafeIndex arr (off+len-1)
574+ n0 = A.unsafeIndex arr (off+len-2)
575+{-# INLINE [1] unsnoc #-}
576+
548-- | /O(1)/ Tests whether a 'Text' is empty or not. Subject to
577-- | /O(1)/ Tests whether a 'Text' is empty or not. Subject to
549-- fusion.
578-- fusion.
550null :: Text -> Bool
579null :: Text -> Bool
@@ -782,6 +811,8 @@ toUpper t = unstream (S.toUpper (stream t))
782-- guides disagree on whether the book name \"The Hill of the Red
811-- guides disagree on whether the book name \"The Hill of the Red
783-- Fox\" is correctly title cased&#x2014;but this function will
812-- Fox\" is correctly title cased&#x2014;but this function will
784-- capitalize /every/ word.
813-- capitalize /every/ word.
814+--
815+-- @since 1.0.0.0
785toTitle :: Text -> Text
816toTitle :: Text -> Text
786toTitle t = unstream (S.toTitle (stream t))
817toTitle t = unstream (S.toTitle (stream t))
787{-# INLINE toTitle #-}
818{-# INLINE toTitle #-}
@@ -1099,8 +1130,8 @@ takeEnd n t@(Text arr off len)
1099iterNEnd :: Int -> Text -> Int
1130iterNEnd :: Int -> Text -> Int
1100iterNEnd n t@(Text _arr _off len) = loop (len-1) n
1131iterNEnd n t@(Text _arr _off len) = loop (len-1) n
1101 where loop i !m
1132 where loop i !m
1133+ | m <= 0 = i+1
1102 | i <= 0 = 0
1134 | i <= 0 = 0
1103− | m <= 1 = i
1104 | otherwise = loop (i+d) (m-1)
1135 | otherwise = loop (i+d) (m-1)
1105 where d = reverseIter_ t i
1136 where d = reverseIter_ t i
1106
1137
Data/Text/Encoding.hs+12−2
@@ -79,7 +79,11 @@ import Data.Text.Internal.Unsafe.Shift (shiftR)
79import Data.Text.Show ()
79import Data.Text.Show ()
80import Data.Text.Unsafe (unsafeDupablePerformIO)
80import Data.Text.Unsafe (unsafeDupablePerformIO)
81import Data.Word (Word8, Word32)
81import Data.Word (Word8, Word32)
82+#if __GLASGOW_HASKELL__ >= 703
82−import Foreign.C.Types (CSize(..))
83+import Foreign.C.Types (CSize(CSize))
84+#else
85+import Foreign.C.Types (CSize)
86+#endif
83import Foreign.ForeignPtr (withForeignPtr)
87import Foreign.ForeignPtr (withForeignPtr)
84import Foreign.Marshal.Utils (with)
88import Foreign.Marshal.Utils (with)
85import Foreign.Ptr (Ptr, minusPtr, nullPtr, plusPtr)
89import Foreign.Ptr (Ptr, minusPtr, nullPtr, plusPtr)
@@ -209,6 +213,8 @@ decodeUtf8With onErr (PS fp off len) = runText $ \done -> do
209-- or continuation where it is encountered.
213-- or continuation where it is encountered.
210
214
211-- | A stream oriented decoding result.
215-- | A stream oriented decoding result.
216+--
217+-- @since 1.0.0.0
212data Decoding = Some Text ByteString (ByteString -> Decoding)
218data Decoding = Some Text ByteString (ByteString -> Decoding)
213
219
214instance Show Decoding where
220instance Show Decoding where
@@ -228,11 +234,15 @@ newtype DecoderState = DecoderState Word32 deriving (Eq, Show, Num, Storable)
228-- thrown (either by this function or a continuation) that cannot be
234-- thrown (either by this function or a continuation) that cannot be
229-- caught in pure code. For more control over the handling of invalid
235-- caught in pure code. For more control over the handling of invalid
230-- data, use 'streamDecodeUtf8With'.
236-- data, use 'streamDecodeUtf8With'.
237+--
238+-- @since 1.0.0.0
231streamDecodeUtf8 :: ByteString -> Decoding
239streamDecodeUtf8 :: ByteString -> Decoding
232streamDecodeUtf8 = streamDecodeUtf8With strictDecode
240streamDecodeUtf8 = streamDecodeUtf8With strictDecode
233
241
234-- | Decode, in a stream oriented way, a 'ByteString' containing UTF-8
242-- | Decode, in a stream oriented way, a 'ByteString' containing UTF-8
235-- encoded text.
243-- encoded text.
244+--
245+-- @since 1.0.0.0
236streamDecodeUtf8With :: OnDecodeError -> ByteString -> Decoding
246streamDecodeUtf8With :: OnDecodeError -> ByteString -> Decoding
237streamDecodeUtf8With onErr = decodeChunk B.empty 0 0
247streamDecodeUtf8With onErr = decodeChunk B.empty 0 0
238 where
248 where
@@ -373,7 +383,7 @@ encodeUtf8 :: Text -> ByteString
373encodeUtf8 (Text arr off len)
383encodeUtf8 (Text arr off len)
374 | len == 0 = B.empty
384 | len == 0 = B.empty
375 | otherwise = unsafeDupablePerformIO $ do
385 | otherwise = unsafeDupablePerformIO $ do
376− fp <- mallocByteString (len*4)
386+ fp <- mallocByteString (len*3) -- see https://github.com/haskell/text/issues/194 for why len*3 is enough
377 withForeignPtr fp $ \ptr ->
387 withForeignPtr fp $ \ptr ->
378 with ptr $ \destPtr -> do
388 with ptr $ \destPtr -> do
379 c_encode_utf8 destPtr (A.aBA arr) (fromIntegral off) (fromIntegral len)
389 c_encode_utf8 destPtr (A.aBA arr) (fromIntegral off) (fromIntegral len)
Data/Text/Foreign.hs+5−1
@@ -108,7 +108,7 @@ takeWord16 (I16 n) t@(Text arr off len)
108 | n >= len || m >= len = t
108 | n >= len || m >= len = t
109 | otherwise = Text arr off m
109 | otherwise = Text arr off m
110 where
110 where
111− m | w < 0xDB00 || w > 0xD8FF = n
111+ m | w < 0xD800 || w > 0xDBFF = n
112 | otherwise = n+1
112 | otherwise = n+1
113 w = A.unsafeIndex arr (off+n-1)
113 w = A.unsafeIndex arr (off+n-1)
114
114
@@ -157,6 +157,8 @@ asForeignPtr t@(Text _arr _off len) = do
157-- | /O(n)/ Decode a C string with explicit length, which is assumed
157-- | /O(n)/ Decode a C string with explicit length, which is assumed
158-- to have been encoded as UTF-8. If decoding fails, a
158-- to have been encoded as UTF-8. If decoding fails, a
159-- 'UnicodeException' is thrown.
159-- 'UnicodeException' is thrown.
160+--
161+-- @since 1.0.0.0
160peekCStringLen :: CStringLen -> IO Text
162peekCStringLen :: CStringLen -> IO Text
161peekCStringLen cs = do
163peekCStringLen cs = do
162 bs <- unsafePackCStringLen cs
164 bs <- unsafePackCStringLen cs
@@ -169,5 +171,7 @@ peekCStringLen cs = do
169-- The temporary storage is freed when the subcomputation terminates
171-- The temporary storage is freed when the subcomputation terminates
170-- (either normally or via an exception), so the pointer to the
172-- (either normally or via an exception), so the pointer to the
171-- temporary storage must /not/ be used after this function returns.
173-- temporary storage must /not/ be used after this function returns.
174+--
175+-- @since 1.0.0.0
172withCStringLen :: Text -> (CStringLen -> IO a) -> IO a
176withCStringLen :: Text -> (CStringLen -> IO a) -> IO a
173withCStringLen t act = unsafeUseAsCStringLen (encodeUtf8 t) act
177withCStringLen t act = unsafeUseAsCStringLen (encodeUtf8 t) act
Data/Text/Internal/Fusion/CaseMapping.hs+164−4
@@ -1,10 +1,10 @@
1{-# LANGUAGE Rank2Types #-}
1{-# LANGUAGE Rank2Types #-}
2-- AUTOMATICALLY GENERATED - DO NOT EDIT
2-- AUTOMATICALLY GENERATED - DO NOT EDIT
3-- Generated by scripts/CaseMapping.hs
3-- Generated by scripts/CaseMapping.hs
4−-- CaseFolding-8.0.0.txt
4+-- CaseFolding-9.0.0.txt
5−-- Date: 2015-01-13, 18:16:36 GMT [MD]
5+-- Date: 2016-03-02, 18:54:54 GMT
6−-- SpecialCasing-8.0.0.txt
6+-- SpecialCasing-9.0.0.txt
7−-- Date: 2014-12-16, 23:08:04 GMT [MD]
7+-- Date: 2016-03-02, 18:55:13 GMT
8
8
9module Data.Text.Internal.Fusion.CaseMapping where
9module Data.Text.Internal.Fusion.CaseMapping where
10import Data.Char
10import Data.Char
@@ -371,6 +371,24 @@ foldMapping '\x13fb' s = Yield '\x13f3' (CC s '\x0000' '\x0000')
371foldMapping '\x13fc' s = Yield '\x13f4' (CC s '\x0000' '\x0000')
371foldMapping '\x13fc' s = Yield '\x13f4' (CC s '\x0000' '\x0000')
372-- CHEROKEE SMALL LETTER MV
372-- CHEROKEE SMALL LETTER MV
373foldMapping '\x13fd' s = Yield '\x13f5' (CC s '\x0000' '\x0000')
373foldMapping '\x13fd' s = Yield '\x13f5' (CC s '\x0000' '\x0000')
374+-- CYRILLIC SMALL LETTER ROUNDED VE
375+foldMapping '\x1c80' s = Yield '\x0432' (CC s '\x0000' '\x0000')
376+-- CYRILLIC SMALL LETTER LONG-LEGGED DE
377+foldMapping '\x1c81' s = Yield '\x0434' (CC s '\x0000' '\x0000')
378+-- CYRILLIC SMALL LETTER NARROW O
379+foldMapping '\x1c82' s = Yield '\x043e' (CC s '\x0000' '\x0000')
380+-- CYRILLIC SMALL LETTER WIDE ES
381+foldMapping '\x1c83' s = Yield '\x0441' (CC s '\x0000' '\x0000')
382+-- CYRILLIC SMALL LETTER TALL TE
383+foldMapping '\x1c84' s = Yield '\x0442' (CC s '\x0000' '\x0000')
384+-- CYRILLIC SMALL LETTER THREE-LEGGED TE
385+foldMapping '\x1c85' s = Yield '\x0442' (CC s '\x0000' '\x0000')
386+-- CYRILLIC SMALL LETTER TALL HARD SIGN
387+foldMapping '\x1c86' s = Yield '\x044a' (CC s '\x0000' '\x0000')
388+-- CYRILLIC SMALL LETTER TALL YAT
389+foldMapping '\x1c87' s = Yield '\x0463' (CC s '\x0000' '\x0000')
390+-- CYRILLIC SMALL LETTER UNBLENDED UK
391+foldMapping '\x1c88' s = Yield '\xa64b' (CC s '\x0000' '\x0000')
374-- LATIN SMALL LETTER H WITH LINE BELOW
392-- LATIN SMALL LETTER H WITH LINE BELOW
375foldMapping '\x1e96' s = Yield '\x0068' (CC s '\x0331' '\x0000')
393foldMapping '\x1e96' s = Yield '\x0068' (CC s '\x0331' '\x0000')
376-- LATIN SMALL LETTER T WITH DIAERESIS
394-- LATIN SMALL LETTER T WITH DIAERESIS
@@ -545,6 +563,8 @@ foldMapping '\x1ff6' s = Yield '\x03c9' (CC s '\x0342' '\x0000')
545foldMapping '\x1ff7' s = Yield '\x03c9' (CC s '\x0342' '\x03b9')
563foldMapping '\x1ff7' s = Yield '\x03c9' (CC s '\x0342' '\x03b9')
546-- GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI
564-- GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI
547foldMapping '\x1ffc' s = Yield '\x03c9' (CC s '\x03b9' '\x0000')
565foldMapping '\x1ffc' s = Yield '\x03c9' (CC s '\x03b9' '\x0000')
566+-- LATIN CAPITAL LETTER SMALL CAPITAL I
567+foldMapping '\xa7ae' s = Yield '\x026a' (CC s '\x0000' '\x0000')
548-- LATIN CAPITAL LETTER J WITH CROSSED-TAIL
568-- LATIN CAPITAL LETTER J WITH CROSSED-TAIL
549foldMapping '\xa7b2' s = Yield '\x029d' (CC s '\x0000' '\x0000')
569foldMapping '\xa7b2' s = Yield '\x029d' (CC s '\x0000' '\x0000')
550-- LATIN CAPITAL LETTER CHI
570-- LATIN CAPITAL LETTER CHI
@@ -737,6 +757,78 @@ foldMapping '\xfb15' s = Yield '\x0574' (CC s '\x056b' '\x0000')
737foldMapping '\xfb16' s = Yield '\x057e' (CC s '\x0576' '\x0000')
757foldMapping '\xfb16' s = Yield '\x057e' (CC s '\x0576' '\x0000')
738-- ARMENIAN SMALL LIGATURE MEN XEH
758-- ARMENIAN SMALL LIGATURE MEN XEH
739foldMapping '\xfb17' s = Yield '\x0574' (CC s '\x056d' '\x0000')
759foldMapping '\xfb17' s = Yield '\x0574' (CC s '\x056d' '\x0000')
760+-- OSAGE CAPITAL LETTER A
761+foldMapping '\x104b0' s = Yield '\x104d8' (CC s '\x0000' '\x0000')
762+-- OSAGE CAPITAL LETTER AI
763+foldMapping '\x104b1' s = Yield '\x104d9' (CC s '\x0000' '\x0000')
764+-- OSAGE CAPITAL LETTER AIN
765+foldMapping '\x104b2' s = Yield '\x104da' (CC s '\x0000' '\x0000')
766+-- OSAGE CAPITAL LETTER AH
767+foldMapping '\x104b3' s = Yield '\x104db' (CC s '\x0000' '\x0000')
768+-- OSAGE CAPITAL LETTER BRA
769+foldMapping '\x104b4' s = Yield '\x104dc' (CC s '\x0000' '\x0000')
770+-- OSAGE CAPITAL LETTER CHA
771+foldMapping '\x104b5' s = Yield '\x104dd' (CC s '\x0000' '\x0000')
772+-- OSAGE CAPITAL LETTER EHCHA
773+foldMapping '\x104b6' s = Yield '\x104de' (CC s '\x0000' '\x0000')
774+-- OSAGE CAPITAL LETTER E
775+foldMapping '\x104b7' s = Yield '\x104df' (CC s '\x0000' '\x0000')
776+-- OSAGE CAPITAL LETTER EIN
777+foldMapping '\x104b8' s = Yield '\x104e0' (CC s '\x0000' '\x0000')
778+-- OSAGE CAPITAL LETTER HA
779+foldMapping '\x104b9' s = Yield '\x104e1' (CC s '\x0000' '\x0000')
780+-- OSAGE CAPITAL LETTER HYA
781+foldMapping '\x104ba' s = Yield '\x104e2' (CC s '\x0000' '\x0000')
782+-- OSAGE CAPITAL LETTER I
783+foldMapping '\x104bb' s = Yield '\x104e3' (CC s '\x0000' '\x0000')
784+-- OSAGE CAPITAL LETTER KA
785+foldMapping '\x104bc' s = Yield '\x104e4' (CC s '\x0000' '\x0000')
786+-- OSAGE CAPITAL LETTER EHKA
787+foldMapping '\x104bd' s = Yield '\x104e5' (CC s '\x0000' '\x0000')
788+-- OSAGE CAPITAL LETTER KYA
789+foldMapping '\x104be' s = Yield '\x104e6' (CC s '\x0000' '\x0000')
790+-- OSAGE CAPITAL LETTER LA
791+foldMapping '\x104bf' s = Yield '\x104e7' (CC s '\x0000' '\x0000')
792+-- OSAGE CAPITAL LETTER MA
793+foldMapping '\x104c0' s = Yield '\x104e8' (CC s '\x0000' '\x0000')
794+-- OSAGE CAPITAL LETTER NA
795+foldMapping '\x104c1' s = Yield '\x104e9' (CC s '\x0000' '\x0000')
796+-- OSAGE CAPITAL LETTER O
797+foldMapping '\x104c2' s = Yield '\x104ea' (CC s '\x0000' '\x0000')
798+-- OSAGE CAPITAL LETTER OIN
799+foldMapping '\x104c3' s = Yield '\x104eb' (CC s '\x0000' '\x0000')
800+-- OSAGE CAPITAL LETTER PA
801+foldMapping '\x104c4' s = Yield '\x104ec' (CC s '\x0000' '\x0000')
802+-- OSAGE CAPITAL LETTER EHPA
803+foldMapping '\x104c5' s = Yield '\x104ed' (CC s '\x0000' '\x0000')
804+-- OSAGE CAPITAL LETTER SA
805+foldMapping '\x104c6' s = Yield '\x104ee' (CC s '\x0000' '\x0000')
806+-- OSAGE CAPITAL LETTER SHA
807+foldMapping '\x104c7' s = Yield '\x104ef' (CC s '\x0000' '\x0000')
808+-- OSAGE CAPITAL LETTER TA
809+foldMapping '\x104c8' s = Yield '\x104f0' (CC s '\x0000' '\x0000')
810+-- OSAGE CAPITAL LETTER EHTA
811+foldMapping '\x104c9' s = Yield '\x104f1' (CC s '\x0000' '\x0000')
812+-- OSAGE CAPITAL LETTER TSA
813+foldMapping '\x104ca' s = Yield '\x104f2' (CC s '\x0000' '\x0000')
814+-- OSAGE CAPITAL LETTER EHTSA
815+foldMapping '\x104cb' s = Yield '\x104f3' (CC s '\x0000' '\x0000')
816+-- OSAGE CAPITAL LETTER TSHA
817+foldMapping '\x104cc' s = Yield '\x104f4' (CC s '\x0000' '\x0000')
818+-- OSAGE CAPITAL LETTER DHA
819+foldMapping '\x104cd' s = Yield '\x104f5' (CC s '\x0000' '\x0000')
820+-- OSAGE CAPITAL LETTER U
821+foldMapping '\x104ce' s = Yield '\x104f6' (CC s '\x0000' '\x0000')
822+-- OSAGE CAPITAL LETTER WA
823+foldMapping '\x104cf' s = Yield '\x104f7' (CC s '\x0000' '\x0000')
824+-- OSAGE CAPITAL LETTER KHA
825+foldMapping '\x104d0' s = Yield '\x104f8' (CC s '\x0000' '\x0000')
826+-- OSAGE CAPITAL LETTER GHA
827+foldMapping '\x104d1' s = Yield '\x104f9' (CC s '\x0000' '\x0000')
828+-- OSAGE CAPITAL LETTER ZA
829+foldMapping '\x104d2' s = Yield '\x104fa' (CC s '\x0000' '\x0000')
830+-- OSAGE CAPITAL LETTER ZHA
831+foldMapping '\x104d3' s = Yield '\x104fb' (CC s '\x0000' '\x0000')
740-- OLD HUNGARIAN CAPITAL LETTER A
832-- OLD HUNGARIAN CAPITAL LETTER A
741foldMapping '\x10c80' s = Yield '\x10cc0' (CC s '\x0000' '\x0000')
833foldMapping '\x10c80' s = Yield '\x10cc0' (CC s '\x0000' '\x0000')
742-- OLD HUNGARIAN CAPITAL LETTER AA
834-- OLD HUNGARIAN CAPITAL LETTER AA
@@ -839,4 +931,72 @@ foldMapping '\x10cb0' s = Yield '\x10cf0' (CC s '\x0000' '\x0000')
839foldMapping '\x10cb1' s = Yield '\x10cf1' (CC s '\x0000' '\x0000')
931foldMapping '\x10cb1' s = Yield '\x10cf1' (CC s '\x0000' '\x0000')
840-- OLD HUNGARIAN CAPITAL LETTER US
932-- OLD HUNGARIAN CAPITAL LETTER US
841foldMapping '\x10cb2' s = Yield '\x10cf2' (CC s '\x0000' '\x0000')
933foldMapping '\x10cb2' s = Yield '\x10cf2' (CC s '\x0000' '\x0000')
934+-- ADLAM CAPITAL LETTER ALIF
935+foldMapping '\x1e900' s = Yield '\x1e922' (CC s '\x0000' '\x0000')
936+-- ADLAM CAPITAL LETTER DAALI
937+foldMapping '\x1e901' s = Yield '\x1e923' (CC s '\x0000' '\x0000')
938+-- ADLAM CAPITAL LETTER LAAM
939+foldMapping '\x1e902' s = Yield '\x1e924' (CC s '\x0000' '\x0000')
940+-- ADLAM CAPITAL LETTER MIIM
941+foldMapping '\x1e903' s = Yield '\x1e925' (CC s '\x0000' '\x0000')
942+-- ADLAM CAPITAL LETTER BA
943+foldMapping '\x1e904' s = Yield '\x1e926' (CC s '\x0000' '\x0000')
944+-- ADLAM CAPITAL LETTER SINNYIIYHE
945+foldMapping '\x1e905' s = Yield '\x1e927' (CC s '\x0000' '\x0000')
946+-- ADLAM CAPITAL LETTER PE
947+foldMapping '\x1e906' s = Yield '\x1e928' (CC s '\x0000' '\x0000')
948+-- ADLAM CAPITAL LETTER BHE
949+foldMapping '\x1e907' s = Yield '\x1e929' (CC s '\x0000' '\x0000')
950+-- ADLAM CAPITAL LETTER RA
951+foldMapping '\x1e908' s = Yield '\x1e92a' (CC s '\x0000' '\x0000')
952+-- ADLAM CAPITAL LETTER E
953+foldMapping '\x1e909' s = Yield '\x1e92b' (CC s '\x0000' '\x0000')
954+-- ADLAM CAPITAL LETTER FA
955+foldMapping '\x1e90a' s = Yield '\x1e92c' (CC s '\x0000' '\x0000')
956+-- ADLAM CAPITAL LETTER I
957+foldMapping '\x1e90b' s = Yield '\x1e92d' (CC s '\x0000' '\x0000')
958+-- ADLAM CAPITAL LETTER O
959+foldMapping '\x1e90c' s = Yield '\x1e92e' (CC s '\x0000' '\x0000')
960+-- ADLAM CAPITAL LETTER DHA
961+foldMapping '\x1e90d' s = Yield '\x1e92f' (CC s '\x0000' '\x0000')
962+-- ADLAM CAPITAL LETTER YHE
963+foldMapping '\x1e90e' s = Yield '\x1e930' (CC s '\x0000' '\x0000')
964+-- ADLAM CAPITAL LETTER WAW
965+foldMapping '\x1e90f' s = Yield '\x1e931' (CC s '\x0000' '\x0000')
966+-- ADLAM CAPITAL LETTER NUN
967+foldMapping '\x1e910' s = Yield '\x1e932' (CC s '\x0000' '\x0000')
968+-- ADLAM CAPITAL LETTER KAF
969+foldMapping '\x1e911' s = Yield '\x1e933' (CC s '\x0000' '\x0000')
970+-- ADLAM CAPITAL LETTER YA
971+foldMapping '\x1e912' s = Yield '\x1e934' (CC s '\x0000' '\x0000')
972+-- ADLAM CAPITAL LETTER U
973+foldMapping '\x1e913' s = Yield '\x1e935' (CC s '\x0000' '\x0000')
974+-- ADLAM CAPITAL LETTER JIIM
975+foldMapping '\x1e914' s = Yield '\x1e936' (CC s '\x0000' '\x0000')
976+-- ADLAM CAPITAL LETTER CHI
977+foldMapping '\x1e915' s = Yield '\x1e937' (CC s '\x0000' '\x0000')
978+-- ADLAM CAPITAL LETTER HA
979+foldMapping '\x1e916' s = Yield '\x1e938' (CC s '\x0000' '\x0000')
980+-- ADLAM CAPITAL LETTER QAAF
981+foldMapping '\x1e917' s = Yield '\x1e939' (CC s '\x0000' '\x0000')
982+-- ADLAM CAPITAL LETTER GA
983+foldMapping '\x1e918' s = Yield '\x1e93a' (CC s '\x0000' '\x0000')
984+-- ADLAM CAPITAL LETTER NYA
985+foldMapping '\x1e919' s = Yield '\x1e93b' (CC s '\x0000' '\x0000')
986+-- ADLAM CAPITAL LETTER TU
987+foldMapping '\x1e91a' s = Yield '\x1e93c' (CC s '\x0000' '\x0000')
988+-- ADLAM CAPITAL LETTER NHA
989+foldMapping '\x1e91b' s = Yield '\x1e93d' (CC s '\x0000' '\x0000')
990+-- ADLAM CAPITAL LETTER VA
991+foldMapping '\x1e91c' s = Yield '\x1e93e' (CC s '\x0000' '\x0000')
992+-- ADLAM CAPITAL LETTER KHA
993+foldMapping '\x1e91d' s = Yield '\x1e93f' (CC s '\x0000' '\x0000')
994+-- ADLAM CAPITAL LETTER GBE
995+foldMapping '\x1e91e' s = Yield '\x1e940' (CC s '\x0000' '\x0000')
996+-- ADLAM CAPITAL LETTER ZAL
997+foldMapping '\x1e91f' s = Yield '\x1e941' (CC s '\x0000' '\x0000')
998+-- ADLAM CAPITAL LETTER KPO
999+foldMapping '\x1e920' s = Yield '\x1e942' (CC s '\x0000' '\x0000')
1000+-- ADLAM CAPITAL LETTER SHA
1001+foldMapping '\x1e921' s = Yield '\x1e943' (CC s '\x0000' '\x0000')
842foldMapping c s = Yield (toLower c) (CC s '\0' '\0')
1002foldMapping c s = Yield (toLower c) (CC s '\0' '\0')
Data/Text/Internal/Fusion/Common.hs+44−37
@@ -1,4 +1,4 @@
1−{-# LANGUAGE BangPatterns, MagicHash, Rank2Types #-}
1+{-# LANGUAGE PatternGuards, BangPatterns, MagicHash, Rank2Types #-}
2-- |
2-- |
3-- Module : Data.Text.Internal.Fusion.Common
3-- Module : Data.Text.Internal.Fusion.Common
4-- Copyright : (c) Bryan O'Sullivan 2009, 2012
4-- Copyright : (c) Bryan O'Sullivan 2009, 2012
@@ -107,7 +107,7 @@ import Prelude (Bool(..), Char, Eq(..), Int, Integral, Maybe(..),
107import qualified Data.List as L
107import qualified Data.List as L
108import qualified Prelude as P
108import qualified Prelude as P
109import Data.Bits (shiftL)
109import Data.Bits (shiftL)
110−import Data.Char (isLetter)
110+import Data.Char (isLetter, isSpace)
111import Data.Int (Int64)
111import Data.Int (Int64)
112import Data.Text.Internal.Fusion.Types
112import Data.Text.Internal.Fusion.Types
113import Data.Text.Internal.Fusion.CaseMapping (foldMapping, lowerMapping, titleMapping,
113import Data.Text.Internal.Fusion.CaseMapping (foldMapping, lowerMapping, titleMapping,
@@ -117,7 +117,7 @@ import GHC.Prim (Addr#, chr#, indexCharOffAddr#, ord#)
117import GHC.Types (Char(..), Int(..))
117import GHC.Types (Char(..), Int(..))
118
118
119singleton :: Char -> Stream Char
119singleton :: Char -> Stream Char
120−singleton c = Stream next False 1
120+singleton c = Stream next False (codePointsSize 1)
121 where next False = Yield c True
121 where next False = Yield c True
122 next True = Done
122 next True = Done
123{-# INLINE [0] singleton #-}
123{-# INLINE [0] singleton #-}
@@ -175,7 +175,7 @@ data C s = C0 !s
175
175
176-- | /O(n)/ Adds a character to the front of a Stream Char.
176-- | /O(n)/ Adds a character to the front of a Stream Char.
177cons :: Char -> Stream Char -> Stream Char
177cons :: Char -> Stream Char -> Stream Char
178−cons !w (Stream next0 s0 len) = Stream next (C1 s0) (len+1)
178+cons !w (Stream next0 s0 len) = Stream next (C1 s0) (len + codePointsSize 1)
179 where
179 where
180 next (C1 s) = Yield w (C0 s)
180 next (C1 s) = Yield w (C0 s)
181 next (C0 s) = case next0 s of
181 next (C0 s) = case next0 s of
@@ -189,7 +189,7 @@ data Snoc a = N
189
189
190-- | /O(n)/ Adds a character to the end of a stream.
190-- | /O(n)/ Adds a character to the end of a stream.
191snoc :: Stream Char -> Char -> Stream Char
191snoc :: Stream Char -> Char -> Stream Char
192−snoc (Stream next0 xs0 len) w = Stream next (J xs0) (len+1)
192+snoc (Stream next0 xs0 len) w = Stream next (J xs0) (len + codePointsSize 1)
193 where
193 where
194 next (J xs) = case next0 xs of
194 next (J xs) = case next0 xs of
195 Done -> Yield w N
195 Done -> Yield w N
@@ -237,7 +237,7 @@ uncons :: Stream Char -> Maybe (Char, Stream Char)
237uncons (Stream next s0 len) = loop_uncons s0
237uncons (Stream next s0 len) = loop_uncons s0
238 where
238 where
239 loop_uncons !s = case next s of
239 loop_uncons !s = case next s of
240− Yield x s1 -> Just (x, Stream next s1 (len-1))
240+ Yield x s1 -> Just (x, Stream next s1 (len - codePointsSize 1))
241 Skip s' -> loop_uncons s'
241 Skip s' -> loop_uncons s'
242 Done -> Nothing
242 Done -> Nothing
243{-# INLINE [0] uncons #-}
243{-# INLINE [0] uncons #-}
@@ -260,7 +260,7 @@ last (Stream next s0 _len) = loop0_last s0
260-- | /O(1)/ Returns all characters after the head of a Stream Char, which must
260-- | /O(1)/ Returns all characters after the head of a Stream Char, which must
261-- be non-empty.
261-- be non-empty.
262tail :: Stream Char -> Stream Char
262tail :: Stream Char -> Stream Char
263−tail (Stream next0 s0 len) = Stream next (C0 s0) (len-1)
263+tail (Stream next0 s0 len) = Stream next (C0 s0) (len - codePointsSize 1)
264 where
264 where
265 next (C0 s) = case next0 s of
265 next (C0 s) = case next0 s of
266 Done -> emptyError "tail"
266 Done -> emptyError "tail"
@@ -278,7 +278,7 @@ data Init s = Init0 !s
278-- | /O(1)/ Returns all but the last character of a Stream Char, which
278-- | /O(1)/ Returns all but the last character of a Stream Char, which
279-- must be non-empty.
279-- must be non-empty.
280init :: Stream Char -> Stream Char
280init :: Stream Char -> Stream Char
281−init (Stream next0 s0 len) = Stream next (Init0 s0) (len-1)
281+init (Stream next0 s0 len) = Stream next (Init0 s0) (len - codePointsSize 1)
282 where
282 where
283 next (Init0 s) = case next0 s of
283 next (Init0 s) = case next0 s of
284 Done -> emptyError "init"
284 Done -> emptyError "init"
@@ -318,11 +318,14 @@ lengthI (Stream next s0 _len) = loop_length 0 s0
318-- greater than the number or if the stream can't possibly be as long
318-- greater than the number or if the stream can't possibly be as long
319-- as the number supplied, and hence be more efficient.
319-- as the number supplied, and hence be more efficient.
320compareLengthI :: Integral a => Stream Char -> a -> Ordering
320compareLengthI :: Integral a => Stream Char -> a -> Ordering
321−compareLengthI (Stream next s0 len) n =
321+compareLengthI (Stream next s0 len) n
322− case compareSize len (fromIntegral n) of
322+ -- Note that @len@ tracks code units whereas we want to compare the length
323− Just o -> o
323+ -- in code points. Specifically, a stream with hint @len@ may consist of
324+ -- anywhere from @len/2@ to @len@ code points.
325+ | Just r <- compareSize len n' = r
324− Nothing -> loop_cmp 0 s0
326+ | otherwise = loop_cmp 0 s0
325 where
327 where
328+ n' = codePointsSize $ fromIntegral n
326 loop_cmp !z s = case next s of
329 loop_cmp !z s = case next s of
327 Done -> compare z n
330 Done -> compare z n
328 Skip s' -> loop_cmp z s'
331 Skip s' -> loop_cmp z s'
@@ -368,7 +371,7 @@ data I s = I1 !s
368-- | /O(n)/ Take a character and place it between each of the
371-- | /O(n)/ Take a character and place it between each of the
369-- characters of a 'Stream Char'.
372-- characters of a 'Stream Char'.
370intersperse :: Char -> Stream Char -> Stream Char
373intersperse :: Char -> Stream Char -> Stream Char
371−intersperse c (Stream next0 s0 len) = Stream next (I1 s0) len
374+intersperse c (Stream next0 s0 len) = Stream next (I1 s0) (len + unknownSize)
372 where
375 where
373 next (I1 s) = case next0 s of
376 next (I1 s) = case next0 s of
374 Done -> Done
377 Done -> Done
@@ -393,9 +396,11 @@ intersperse c (Stream next0 s0 len) = Stream next (I1 s0) len
393-- functions may map one input character to two or three output
396-- functions may map one input character to two or three output
394-- characters.
397-- characters.
395
398
399+-- | Map a 'Stream' through the given case-mapping function.
396caseConvert :: (forall s. Char -> s -> Step (CC s) Char)
400caseConvert :: (forall s. Char -> s -> Step (CC s) Char)
397 -> Stream Char -> Stream Char
401 -> Stream Char -> Stream Char
398−caseConvert remap (Stream next0 s0 len) = Stream next (CC s0 '\0' '\0') len
402+caseConvert remap (Stream next0 s0 len) =
403+ Stream next (CC s0 '\0' '\0') (len `unionSize` 3*len)
399 where
404 where
400 next (CC s '\0' _) =
405 next (CC s '\0' _) =
401 case next0 s of
406 case next0 s of
@@ -458,19 +463,20 @@ toLower = caseConvert lowerMapping
458-- Fox\" is correctly title cased&#x2014;but this function will
463-- Fox\" is correctly title cased&#x2014;but this function will
459-- capitalize /every/ word.
464-- capitalize /every/ word.
460toTitle :: Stream Char -> Stream Char
465toTitle :: Stream Char -> Stream Char
461−toTitle (Stream next0 s0 len) = Stream next (CC (False :*: s0) '\0' '\0') len
466+toTitle (Stream next0 s0 len) = Stream next (CC (False :*: s0) '\0' '\0') (len + unknownSize)
462 where
467 where
463 next (CC (letter :*: s) '\0' _) =
468 next (CC (letter :*: s) '\0' _) =
464 case next0 s of
469 case next0 s of
465− Done -> Done
470+ Done -> Done
466− Skip s' -> Skip (CC (letter :*: s') '\0' '\0')
471+ Skip s' -> Skip (CC (letter :*: s') '\0' '\0')
467 Yield c s'
472 Yield c s'
468− | letter' -> if letter
473+ | nonSpace -> if letter
469− then lowerMapping c (letter' :*: s')
474+ then lowerMapping c (nonSpace :*: s')
470− else titleMapping c (letter' :*: s')
475+ else titleMapping c (letter' :*: s')
471− | otherwise -> Yield c (CC (letter' :*: s') '\0' '\0')
476+ | otherwise -> Yield c (CC (letter' :*: s') '\0' '\0')
477+ where nonSpace = P.not (isSpace c)
472− where letter' = isLetter c
478+ letter' = isLetter c
473− next (CC s a b) = Yield a (CC s b '\0')
479+ next (CC s a b) = Yield a (CC s b '\0')
474{-# INLINE [0] toTitle #-}
480{-# INLINE [0] toTitle #-}
475
481
476data Justify i s = Just1 !i !s
482data Justify i s = Just1 !i !s
@@ -478,7 +484,7 @@ data Justify i s = Just1 !i !s
478
484
479justifyLeftI :: Integral a => a -> Char -> Stream Char -> Stream Char
485justifyLeftI :: Integral a => a -> Char -> Stream Char -> Stream Char
480justifyLeftI k c (Stream next0 s0 len) =
486justifyLeftI k c (Stream next0 s0 len) =
481− Stream next (Just1 0 s0) (larger (fromIntegral k) len)
487+ Stream next (Just1 0 s0) (larger (fromIntegral k * charSize c + len) len)
482 where
488 where
483 next (Just1 n s) =
489 next (Just1 n s) =
484 case next0 s of
490 case next0 s of
@@ -698,7 +704,7 @@ replicateI n (Stream next0 s0 len) =
698-- (a,b), in which case, a is the next Char in the string, and b is
704-- (a,b), in which case, a is the next Char in the string, and b is
699-- the seed value for further production.
705-- the seed value for further production.
700unfoldr :: (a -> Maybe (Char,a)) -> a -> Stream Char
706unfoldr :: (a -> Maybe (Char,a)) -> a -> Stream Char
701−unfoldr f s0 = Stream next s0 1 -- HINT maybe too low
707+unfoldr f s0 = Stream next s0 unknownSize
702 where
708 where
703 {-# INLINE next #-}
709 {-# INLINE next #-}
704 next !s = case f s of
710 next !s = case f s of
@@ -712,7 +718,7 @@ unfoldr f s0 = Stream next s0 1 -- HINT maybe too low
712-- 'unfoldr' when the length of the result is known.
718-- 'unfoldr' when the length of the result is known.
713unfoldrNI :: Integral a => a -> (b -> Maybe (Char,b)) -> b -> Stream Char
719unfoldrNI :: Integral a => a -> (b -> Maybe (Char,b)) -> b -> Stream Char
714unfoldrNI n f s0 | n < 0 = empty
720unfoldrNI n f s0 | n < 0 = empty
715− | otherwise = Stream next (0 :*: s0) (fromIntegral (n*2)) -- HINT maybe too high
721+ | otherwise = Stream next (0 :*: s0) (maxSize $ fromIntegral (n*2))
716 where
722 where
717 {-# INLINE next #-}
723 {-# INLINE next #-}
718 next (z :*: s) = case f s of
724 next (z :*: s) = case f s of
@@ -724,12 +730,12 @@ unfoldrNI n f s0 | n < 0 = empty
724-------------------------------------------------------------------------------
730-------------------------------------------------------------------------------
725-- * Substreams
731-- * Substreams
726
732
727−-- | /O(n)/ take n, applied to a stream, returns the prefix of the
733+-- | /O(n)/ @'take' n@, applied to a stream, returns the prefix of the
728-- stream of length @n@, or the stream itself if @n@ is greater than the
734-- stream of length @n@, or the stream itself if @n@ is greater than the
729-- length of the stream.
735-- length of the stream.
730take :: Integral a => a -> Stream Char -> Stream Char
736take :: Integral a => a -> Stream Char -> Stream Char
731take n0 (Stream next0 s0 len) =
737take n0 (Stream next0 s0 len) =
732− Stream next (n0 :*: s0) (smaller len (fromIntegral (max 0 n0)))
738+ Stream next (n0 :*: s0) (smaller len (codePointsSize $ fromIntegral n0))
733 where
739 where
734 {-# INLINE next #-}
740 {-# INLINE next #-}
735 next (n :*: s) | n <= 0 = Done
741 next (n :*: s) | n <= 0 = Done
@@ -742,12 +748,12 @@ take n0 (Stream next0 s0 len) =
742data Drop a s = NS !s
748data Drop a s = NS !s
743 | JS !a !s
749 | JS !a !s
744
750
745−-- | /O(n)/ drop n, applied to a stream, returns the suffix of the
751+-- | /O(n)/ @'drop' n@, applied to a stream, returns the suffix of the
746-- stream after the first @n@ characters, or the empty stream if @n@
752-- stream after the first @n@ characters, or the empty stream if @n@
747-- is greater than the length of the stream.
753-- is greater than the length of the stream.
748drop :: Integral a => a -> Stream Char -> Stream Char
754drop :: Integral a => a -> Stream Char -> Stream Char
749drop n0 (Stream next0 s0 len) =
755drop n0 (Stream next0 s0 len) =
750− Stream next (JS n0 s0) (len - fromIntegral (max 0 n0))
756+ Stream next (JS n0 s0) (len - codePointsSize (fromIntegral n0))
751 where
757 where
752 {-# INLINE next #-}
758 {-# INLINE next #-}
753 next (JS n s)
759 next (JS n s)
@@ -762,10 +768,10 @@ drop n0 (Stream next0 s0 len) =
762 Yield x s' -> Yield x (NS s')
768 Yield x s' -> Yield x (NS s')
763{-# INLINE [0] drop #-}
769{-# INLINE [0] drop #-}
764
770
765−-- | takeWhile, applied to a predicate @p@ and a stream, returns the
771+-- | 'takeWhile', applied to a predicate @p@ and a stream, returns the
766−-- longest prefix (possibly empty) of elements that satisfy p.
772+-- longest prefix (possibly empty) of elements that satisfy @p@.
767takeWhile :: (Char -> Bool) -> Stream Char -> Stream Char
773takeWhile :: (Char -> Bool) -> Stream Char -> Stream Char
768−takeWhile p (Stream next0 s0 len) = Stream next s0 len -- HINT maybe too high
774+takeWhile p (Stream next0 s0 len) = Stream next s0 (len - unknownSize)
769 where
775 where
770 {-# INLINE next #-}
776 {-# INLINE next #-}
771 next !s = case next0 s of
777 next !s = case next0 s of
@@ -775,9 +781,9 @@ takeWhile p (Stream next0 s0 len) = Stream next s0 len -- HINT maybe too high
775 | otherwise -> Done
781 | otherwise -> Done
776{-# INLINE [0] takeWhile #-}
782{-# INLINE [0] takeWhile #-}
777
783
778−-- | dropWhile @p @xs returns the suffix remaining after takeWhile @p @xs.
784+-- | @'dropWhile' p xs@ returns the suffix remaining after @'takeWhile' p xs@.
779dropWhile :: (Char -> Bool) -> Stream Char -> Stream Char
785dropWhile :: (Char -> Bool) -> Stream Char -> Stream Char
780−dropWhile p (Stream next0 s0 len) = Stream next (L s0) len -- HINT maybe too high
786+dropWhile p (Stream next0 s0 len) = Stream next (L s0) (len - unknownSize)
781 where
787 where
782 {-# INLINE next #-}
788 {-# INLINE next #-}
783 next (L s) = case next0 s of
789 next (L s) = case next0 s of
@@ -811,7 +817,7 @@ isPrefixOf (Stream next1 s1 _) (Stream next2 s2 _) = loop (next1 s1) (next2 s2)
811-------------------------------------------------------------------------------
817-------------------------------------------------------------------------------
812-- ** Searching by equality
818-- ** Searching by equality
813
819
814−-- | /O(n)/ elem is the stream membership predicate.
820+-- | /O(n)/ 'elem' is the stream membership predicate.
815elem :: Char -> Stream Char -> Bool
821elem :: Char -> Stream Char -> Bool
816elem w (Stream next s0 _len) = loop_elem s0
822elem w (Stream next s0 _len) = loop_elem s0
817 where
823 where
@@ -856,7 +862,8 @@ indexI (Stream next s0 _len) n0
856-- returns a stream containing those characters that satisfy the
862-- returns a stream containing those characters that satisfy the
857-- predicate.
863-- predicate.
858filter :: (Char -> Bool) -> Stream Char -> Stream Char
864filter :: (Char -> Bool) -> Stream Char -> Stream Char
865+filter p (Stream next0 s0 len) =
859−filter p (Stream next0 s0 len) = Stream next s0 len -- HINT maybe too high
866+ Stream next s0 (len - unknownSize) -- HINT maybe too high
860 where
867 where
861 next !s = case next0 s of
868 next !s = case next0 s of
862 Done -> Done
869 Done -> Done
Data/Text/Internal/Fusion/Size.hs+33−7
@@ -1,4 +1,4 @@
1−{-# LANGUAGE CPP #-}
1+{-# LANGUAGE CPP, PatternGuards #-}
2{-# OPTIONS_GHC -fno-warn-missing-methods #-}
2{-# OPTIONS_GHC -fno-warn-missing-methods #-}
3-- |
3-- |
4-- Module : Data.Text.Internal.Fusion.Internal
4-- Module : Data.Text.Internal.Fusion.Internal
@@ -19,11 +19,16 @@
19module Data.Text.Internal.Fusion.Size
19module Data.Text.Internal.Fusion.Size
20 (
20 (
21 Size
21 Size
22− , exactly
22+ -- * Sizes
23 , exactSize
23 , exactSize
24 , maxSize
24 , maxSize
25 , betweenSize
25 , betweenSize
26 , unknownSize
26 , unknownSize
27+ , unionSize
28+ , charSize
29+ , codePointsSize
30+ -- * Querying sizes
31+ , exactly
27 , smaller
32 , smaller
28 , larger
33 , larger
29 , upperBound
34 , upperBound
@@ -32,11 +37,13 @@ module Data.Text.Internal.Fusion.Size
32 , isEmpty
37 , isEmpty
33 ) where
38 ) where
34
39
40+import Data.Char (ord)
35import Data.Text.Internal (mul)
41import Data.Text.Internal (mul)
36#if defined(ASSERTS)
42#if defined(ASSERTS)
37import Control.Exception (assert)
43import Control.Exception (assert)
38#endif
44#endif
39
45
46+-- | A size in UTF-16 code units.
40data Size = Between {-# UNPACK #-} !Int {-# UNPACK #-} !Int -- ^ Lower and upper bounds on size.
47data Size = Between {-# UNPACK #-} !Int {-# UNPACK #-} !Int -- ^ Lower and upper bounds on size.
41 | Unknown -- ^ Unknown size.
48 | Unknown -- ^ Unknown size.
42 deriving (Eq, Show)
49 deriving (Eq, Show)
@@ -46,6 +53,17 @@ exactly (Between na nb) | na == nb = Just na
46exactly _ = Nothing
53exactly _ = Nothing
47{-# INLINE exactly #-}
54{-# INLINE exactly #-}
48
55
56+-- | The 'Size' of the given code point.
57+charSize :: Char -> Size
58+charSize c
59+ | ord c < 0x10000 = exactSize 1
60+ | otherwise = exactSize 2
61+
62+-- | The 'Size' of @n@ code points.
63+codePointsSize :: Int -> Size
64+codePointsSize n = Between n (2*n)
65+{-# INLINE codePointsSize #-}
66+
49exactSize :: Int -> Size
67exactSize :: Int -> Size
50exactSize n =
68exactSize n =
51#if defined(ASSERTS)
69#if defined(ASSERTS)
@@ -71,6 +89,10 @@ betweenSize m n =
71 Between m n
89 Between m n
72{-# INLINE betweenSize #-}
90{-# INLINE betweenSize #-}
73
91
92+unionSize :: Size -> Size -> Size
93+unionSize (Between a b) (Between c d) = Between (min a c) (max b d)
94+unionSize _ _ = Unknown
95+
74unknownSize :: Size
96unknownSize :: Size
75unknownSize = Unknown
97unknownSize = Unknown
76{-# INLINE unknownSize #-}
98{-# INLINE unknownSize #-}
@@ -140,11 +162,15 @@ lowerBound _ (Between n _) = n
140lowerBound k _ = k
162lowerBound k _ = k
141{-# INLINE lowerBound #-}
163{-# INLINE lowerBound #-}
142
164
165+-- | Determine the ordering relationship between two 'Size's, or 'Nothing' in
166+-- the indeterminate case.
143−compareSize :: Size -> Int -> Maybe Ordering
167+compareSize :: Size -> Size -> Maybe Ordering
144−compareSize (Between ma mb) n
168+compareSize (Between ma mb) (Between na nb)
145− | mb < n = Just LT
169+ | mb < na = Just LT
146− | ma > n = Just GT
170+ | ma > nb = Just GT
171+ | ma == mb
172+ , ma == na
147− | ma == n && mb == n = Just EQ
173+ , ma == nb = Just EQ
148compareSize _ _ = Nothing
174compareSize _ _ = Nothing
149
175
150
176
Data/Text/Internal/Fusion/Types.hs+4−4
@@ -75,16 +75,16 @@ instance (Ord a) => Ord (Stream a) where
75-- unstreaming functions must be able to cope with the hint being too
75-- unstreaming functions must be able to cope with the hint being too
76-- small or too large.
76-- small or too large.
77--
77--
78−-- The size hint tries to track the UTF-16 code points in a stream,
78+-- The size hint tries to track the UTF-16 code units in a stream,
79−-- but often counts the number of characters instead. It can easily
79+-- but often counts the number of code points instead. It can easily
80-- undercount if, for instance, a transformed stream contains astral
80-- undercount if, for instance, a transformed stream contains astral
81−-- plane characters (those above 0x10000).
81+-- plane code points (those above 0x10000).
82
82
83data Stream a =
83data Stream a =
84 forall s. Stream
84 forall s. Stream
85 (s -> Step s a) -- stepper function
85 (s -> Step s a) -- stepper function
86 !s -- current state
86 !s -- current state
87− !Size -- size hint
87+ !Size -- size hint in code units
88
88
89-- | /O(n)/ Determines if two streams are equal.
89-- | /O(n)/ Determines if two streams are equal.
90eq :: (Eq a) => Stream a -> Stream a -> Bool
90eq :: (Eq a) => Stream a -> Stream a -> Bool
Data/Text/Lazy.hs+19−6
@@ -68,6 +68,7 @@ module Data.Text.Lazy
68 , snoc
68 , snoc
69 , append
69 , append
70 , uncons
70 , uncons
71+ , unsnoc
71 , head
72 , head
72 , last
73 , last
73 , tail
74 , tail
@@ -475,9 +476,7 @@ fromStrict t = chunk t Empty
475-- -----------------------------------------------------------------------------
476-- -----------------------------------------------------------------------------
476-- * Basic functions
477-- * Basic functions
477
478
478−-- | /O(n)/ Adds a character to the front of a 'Text'. This function
479+-- | /O(1)/ Adds a character to the front of a 'Text'. Subject to fusion.
479−-- is more costly than its 'List' counterpart because it requires
480−-- copying a new array. Subject to fusion.
481cons :: Char -> Text -> Text
480cons :: Char -> Text -> Text
482cons c t = Chunk (T.singleton c) t
481cons c t = Chunk (T.singleton c) t
483{-# INLINE [1] cons #-}
482{-# INLINE [1] cons #-}
@@ -561,6 +560,17 @@ init Empty = emptyError "init"
561 unstream (S.init (stream t)) = init t
560 unstream (S.init (stream t)) = init t
562 #-}
561 #-}
563
562
563+-- | /O(n\/c)/ Returns the 'init' and 'last' of a 'Text', or 'Nothing' if
564+-- empty.
565+--
566+-- * It is no faster than using 'init' and 'last'.
567+--
568+-- @since 1.2.3.0
569+unsnoc :: Text -> Maybe (Text, Char)
570+unsnoc Empty = Nothing
571+unsnoc ts@(Chunk _ _) = Just (init ts, last ts)
572+{-# INLINE unsnoc #-}
573+
564-- | /O(1)/ Tests whether a 'Text' is empty or not. Subject to
574-- | /O(1)/ Tests whether a 'Text' is empty or not. Subject to
565-- fusion.
575-- fusion.
566null :: Text -> Bool
576null :: Text -> Bool
@@ -820,6 +830,8 @@ toUpper t = unstream (S.toUpper (stream t))
820-- guides disagree on whether the book name \"The Hill of the Red
830-- guides disagree on whether the book name \"The Hill of the Red
821-- Fox\" is correctly title cased&#x2014;but this function will
831-- Fox\" is correctly title cased&#x2014;but this function will
822-- capitalize /every/ word.
832-- capitalize /every/ word.
833+--
834+-- @since 1.0.0.0
823toTitle :: Text -> Text
835toTitle :: Text -> Text
824toTitle t = unstream (S.toTitle (stream t))
836toTitle t = unstream (S.toTitle (stream t))
825{-# INLINE toTitle #-}
837{-# INLINE toTitle #-}
@@ -1153,9 +1165,10 @@ takeWhile p t0 = takeWhile' t0
1153takeWhileEnd :: (Char -> Bool) -> Text -> Text
1165takeWhileEnd :: (Char -> Bool) -> Text -> Text
1154takeWhileEnd p = takeChunk empty . L.reverse . toChunks
1166takeWhileEnd p = takeChunk empty . L.reverse . toChunks
1155 where takeChunk acc [] = acc
1167 where takeChunk acc [] = acc
1156− takeChunk acc (t:ts) = if T.length t' < T.length t
1168+ takeChunk acc (t:ts)
1157− then (Chunk t' acc)
1169+ | T.lengthWord16 t' < T.lengthWord16 t
1170+ = chunk t' acc
1158− else takeChunk (Chunk t' acc) ts
1171+ | otherwise = takeChunk (Chunk t' acc) ts
1159 where t' = T.takeWhileEnd p t
1172 where t' = T.takeWhileEnd p t
1160{-# INLINE takeWhileEnd #-}
1173{-# INLINE takeWhileEnd #-}
1161
1174
Data/Text/Lazy/Builder/Int.hs+3
@@ -32,6 +32,9 @@ import GHC.Base (quotInt, remInt)
32import GHC.Num (quotRemInteger)
32import GHC.Num (quotRemInteger)
33import GHC.Types (Int(..))
33import GHC.Types (Int(..))
34import Control.Monad.ST
34import Control.Monad.ST
35+#if MIN_VERSION_base(4,11,0)
36+import Prelude hiding ((<>))
37+#endif
35
38
36#ifdef __GLASGOW_HASKELL__
39#ifdef __GLASGOW_HASKELL__
37# if defined(INTEGER_GMP)
40# if defined(INTEGER_GMP)
Data/Text/Lazy/Builder/RealFloat.hs+3
@@ -24,6 +24,9 @@ import Data.Text.Lazy.Builder.Int (decimal)
24import Data.Text.Internal.Builder.RealFloat.Functions (roundTo)
24import Data.Text.Internal.Builder.RealFloat.Functions (roundTo)
25import Data.Text.Lazy.Builder
25import Data.Text.Lazy.Builder
26import qualified Data.Text as T
26import qualified Data.Text as T
27+#if MIN_VERSION_base(4,11,0)
28+import Prelude hiding ((<>))
29+#endif
27
30
28-- | Control the rendering of floating point numbers.
31-- | Control the rendering of floating point numbers.
29data FPFormat = Exponent
32data FPFormat = Exponent
Data/Text/Lazy/Read.hs+3−3
@@ -68,9 +68,9 @@ decimal txt
68 go n d = (n * 10 + fromIntegral (digitToInt d))
68 go n d = (n * 10 + fromIntegral (digitToInt d))
69
69
70-- | Read a hexadecimal integer, consisting of an optional leading
70-- | Read a hexadecimal integer, consisting of an optional leading
71−-- @\"0x\"@ followed by at least one decimal digit. Input is consumed
71+-- @\"0x\"@ followed by at least one hexadecimal digit. Input is
72−-- until a non-hex-digit or end of string is reached. This function
72+-- consumed until a non-hex-digit or end of string is reached.
73−-- is case insensitive.
73+-- This function is case insensitive.
74--
74--
75-- This function does not handle leading sign characters. If you need
75-- This function does not handle leading sign characters. If you need
76-- to handle signed input, use @'signed' 'hexadecimal'@.
76-- to handle signed input, use @'signed' 'hexadecimal'@.
Data/Text/Read.hs+3−3
@@ -67,9 +67,9 @@ decimal txt
67 go n d = (n * 10 + fromIntegral (digitToInt d))
67 go n d = (n * 10 + fromIntegral (digitToInt d))
68
68
69-- | Read a hexadecimal integer, consisting of an optional leading
69-- | Read a hexadecimal integer, consisting of an optional leading
70−-- @\"0x\"@ followed by at least one decimal digit. Input is consumed
70+-- @\"0x\"@ followed by at least one hexadecimal digit. Input is
71−-- until a non-hex-digit or end of string is reached. This function
71+-- consumed until a non-hex-digit or end of string is reached.
72−-- is case insensitive.
72+-- This function is case insensitive.
73--
73--
74-- This function does not handle leading sign characters. If you need
74-- This function does not handle leading sign characters. If you need
75-- to handle signed input, use @'signed' 'hexadecimal'@.
75-- to handle signed input, use @'signed' 'hexadecimal'@.
README.markdown+3−3
@@ -19,11 +19,11 @@ library to provide these facilities.
19# Get involved!
19# Get involved!
20
20
21Please report bugs via the
21Please report bugs via the
22−[github issue tracker](https://github.com/bos/text/issues).
22+[github issue tracker](https://github.com/haskell/text/issues).
23
23
24−Master [git repository](https://github.com/bos/text):
24+Master [git repository](https://github.com/haskell/text):
25
25
26−* `git clone git://github.com/bos/text.git`
26+* `git clone git://github.com/haskell/text.git`
27
27
28There's also a [Mercurial mirror](https://bitbucket.org/bos/text):
28There's also a [Mercurial mirror](https://bitbucket.org/bos/text):
29
29
benchmarks/haskell/Benchmarks.hs+2
@@ -10,6 +10,7 @@ import System.FilePath ((</>))
10import System.IO (IOMode (WriteMode), openFile, hSetEncoding, utf8)
10import System.IO (IOMode (WriteMode), openFile, hSetEncoding, utf8)
11
11
12import qualified Benchmarks.Builder as Builder
12import qualified Benchmarks.Builder as Builder
13+import qualified Benchmarks.Concat as Concat
13import qualified Benchmarks.DecodeUtf8 as DecodeUtf8
14import qualified Benchmarks.DecodeUtf8 as DecodeUtf8
14import qualified Benchmarks.EncodeUtf8 as EncodeUtf8
15import qualified Benchmarks.EncodeUtf8 as EncodeUtf8
15import qualified Benchmarks.Equality as Equality
16import qualified Benchmarks.Equality as Equality
@@ -41,6 +42,7 @@ benchmarks = do
41 -- Traditional benchmarks
42 -- Traditional benchmarks
42 bs <- sequence
43 bs <- sequence
43 [ Builder.benchmark
44 [ Builder.benchmark
45+ , Concat.benchmark
44 , DecodeUtf8.benchmark "html" (tf "libya-chinese.html")
46 , DecodeUtf8.benchmark "html" (tf "libya-chinese.html")
45 , DecodeUtf8.benchmark "xml" (tf "yiwiki.xml")
47 , DecodeUtf8.benchmark "xml" (tf "yiwiki.xml")
46 , DecodeUtf8.benchmark "ascii" (tf "ascii.txt")
48 , DecodeUtf8.benchmark "ascii" (tf "ascii.txt")
benchmarks/haskell/Benchmarks/Concat.hsnew file+25
@@ -0,0 +1,25 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+
3+module Benchmarks.Concat (benchmark) where
4+
5+import Control.Monad.Trans.Writer
6+import Criterion (Benchmark, bgroup, bench, whnf)
7+import Data.Text as T
8+
9+benchmark :: IO Benchmark
10+benchmark = return $ bgroup "Concat"
11+ [ bench "append" $ whnf (append4 "Text 1" "Text 2" "Text 3") "Text 4"
12+ , bench "concat" $ whnf (concat4 "Text 1" "Text 2" "Text 3") "Text 4"
13+ , bench "write" $ whnf (write4 "Text 1" "Text 2" "Text 3") "Text 4"
14+ ]
15+
16+append4, concat4, write4 :: Text -> Text -> Text -> Text -> Text
17+
18+{-# NOINLINE append4 #-}
19+append4 x1 x2 x3 x4 = x1 `append` x2 `append` x3 `append` x4
20+
21+{-# NOINLINE concat4 #-}
22+concat4 x1 x2 x3 x4 = T.concat [x1, x2, x3, x4]
23+
24+{-# NOINLINE write4 #-}
25+write4 x1 x2 x3 x4 = execWriter $ tell x1 >> tell x2 >> tell x3 >> tell x4
benchmarks/text-benchmarks.cabal+1
@@ -47,6 +47,7 @@ executable text-benchmarks
47 ghc-prim,
47 ghc-prim,
48 integer-gmp,
48 integer-gmp,
49 stringsearch,
49 stringsearch,
50+ transformers,
50 utf8-string,
51 utf8-string,
51 vector
52 vector
52
53
cabal.projectnew file+3
@@ -0,0 +1,3 @@
1+-- See http://cabal.readthedocs.io/en/latest/nix-local-build-overview.html
2+packages: .
3+tests: True
cbits/cbits.c+6−11
@@ -206,11 +206,9 @@ _hs_text_decode_utf8_state(uint16_t *const dest, size_t *destoff,
206 const uint8_t *srcend,
206 const uint8_t *srcend,
207 uint32_t *codepoint0, uint32_t *state0)
207 uint32_t *codepoint0, uint32_t *state0)
208{
208{
209− uint8_t const *ret = _hs_text_decode_utf8_int(dest, destoff, src, srcend,
209+ _hs_text_decode_utf8_int(dest, destoff, src, srcend, codepoint0, state0);
210− codepoint0, state0);
210+
211− if (*state0 == UTF8_REJECT)
212− ret -=1;
213− return ret;
211+ return *src;
214}
212}
215
213
216/*
214/*
@@ -222,12 +220,9 @@ _hs_text_decode_utf8(uint16_t *const dest, size_t *destoff,
222{
220{
223 uint32_t codepoint;
221 uint32_t codepoint;
224 uint32_t state = UTF8_ACCEPT;
222 uint32_t state = UTF8_ACCEPT;
225− uint8_t const *ret = _hs_text_decode_utf8_int(dest, destoff, &src, srcend,
223+ _hs_text_decode_utf8_int(dest, destoff, &src, srcend,
226− &codepoint, &state);
224+ &codepoint, &state);
227− /* Back up if we have an incomplete or invalid encoding */
228− if (state != UTF8_ACCEPT)
229− ret -= 1;
230− return ret;
225+ return src;
231}
226}
232
227
233void
228void
changelog.md+35−1
@@ -1,10 +1,44 @@
1+1.2.3.0
2+
3+* `unsnoc` implemented
4+
5+* Bug fix: the lazy `takeWhileEnd` function violated the
6+ [lazy text invariant](https://github.com/bos/text/blob/1.2.3.0/Data/Text/Internal/Lazy.hs#L51).
7+
8+* Spec compliance: `toCaseFold` now follows the Unicode 9.0 spec
9+ (updated from 8.0)
10+
11+* Bug fix: Fixed usage of size hints causing incorrect behavior (gh-197).
12+
13+* Improve UTF8 decoder error-recovery (gh-182).
14+
15+* Reduce memory overhead in `encodeUTF8` (gh-194).
16+
17+
18+1.2.2.2
19+
20+* The `toTitle` function now correctly handles letters that
21+ immediately follow punctuation. Before, `"there's"` would turn into
22+ `"There'S"`. Now, it becomes `"There's"`.
23+
24+* The implementation of unstreaming is faster, resulting in operations
25+ such as `map` and `intersperse` speeding up by up to 30%, with
26+ smaller code generated.
27+
28+* The optimised length comparison function is now more likely to be
29+ used after some rewrite rule tweaking.
30+
31+* Bug fix: an off-by-one bug in `takeEnd` is fixed.
32+
33+* Bug fix: a logic error in `takeWord16` is fixed.
34+
11.2.2.1
351.2.2.1
2
36
3* The switch to `integer-pure` in 1.2.2.0 was apparently mistaken.
37* The switch to `integer-pure` in 1.2.2.0 was apparently mistaken.
4 The build flag has been renamed accordingly. Your army of diligent
38 The build flag has been renamed accordingly. Your army of diligent
5 maintainers apologizes for the churn.
39 maintainers apologizes for the churn.
6
40
7−* Spec compliance: toCaseFold now follows the Unicode 8.0 spec
41+* Spec compliance: `toCaseFold` now follows the Unicode 8.0 spec
8 (updated from 7.0)
42 (updated from 7.0)
9
43
10* An STG lint error has been fixed
44* An STG lint error has been fixed
tests/Tests/Properties.hs+94−21
@@ -9,9 +9,9 @@ module Tests.Properties
9 ) where
9 ) where
10
10
11import Control.Applicative ((<$>), (<*>))
11import Control.Applicative ((<$>), (<*>))
12−import Control.Arrow ((***), second)
12+import Control.Arrow ((***), first, second)
13import Data.Bits ((.&.))
13import Data.Bits ((.&.))
14−import Data.Char (chr, isDigit, isHexDigit, isLower, isSpace, isUpper, ord)
14+import Data.Char (chr, isDigit, isHexDigit, isLower, isSpace, isLetter, isUpper, ord)
15import Data.Int (Int8, Int16, Int32, Int64)
15import Data.Int (Int8, Int16, Int32, Int64)
16import Data.Monoid (Monoid(..))
16import Data.Monoid (Monoid(..))
17import Data.String (IsString(fromString))
17import Data.String (IsString(fromString))
@@ -23,6 +23,7 @@ import Data.Text.Internal.Search (indices)
23import Data.Text.Lazy.Read as TL
23import Data.Text.Lazy.Read as TL
24import Data.Text.Read as T
24import Data.Text.Read as T
25import Data.Word (Word, Word8, Word16, Word32, Word64)
25import Data.Word (Word, Word8, Word16, Word32, Word64)
26+import Data.Maybe (mapMaybe)
26import Numeric (showEFloat, showFFloat, showGFloat, showHex)
27import Numeric (showEFloat, showFFloat, showGFloat, showHex)
27import Prelude hiding (replicate)
28import Prelude hiding (replicate)
28import Test.Framework (Test, testGroup)
29import Test.Framework (Test, testGroup)
@@ -30,6 +31,7 @@ import Test.Framework.Providers.QuickCheck2 (testProperty)
30import Test.QuickCheck hiding ((.&.))
31import Test.QuickCheck hiding ((.&.))
31import Test.QuickCheck.Monadic
32import Test.QuickCheck.Monadic
32import Test.QuickCheck.Property (Property(..))
33import Test.QuickCheck.Property (Property(..))
34+import Test.QuickCheck.Unicode (char)
33import Tests.QuickCheckUtils
35import Tests.QuickCheckUtils
34import Tests.Utils
36import Tests.Utils
35import Text.Show.Functions ()
37import Text.Show.Functions ()
@@ -37,6 +39,7 @@ import qualified Control.Exception as Exception
37import qualified Data.Bits as Bits (shiftL, shiftR)
39import qualified Data.Bits as Bits (shiftL, shiftR)
38import qualified Data.ByteString as B
40import qualified Data.ByteString as B
39import qualified Data.ByteString.Lazy as BL
41import qualified Data.ByteString.Lazy as BL
42+import qualified Data.Char as C
40import qualified Data.List as L
43import qualified Data.List as L
41import qualified Data.Text as T
44import qualified Data.Text as T
42import qualified Data.Text.Encoding as E
45import qualified Data.Text.Encoding as E
@@ -60,10 +63,10 @@ t_pack_unpack = (T.unpack . T.pack) `eq` id
60tl_pack_unpack = (TL.unpack . TL.pack) `eq` id
63tl_pack_unpack = (TL.unpack . TL.pack) `eq` id
61t_stream_unstream = (S.unstream . S.stream) `eq` id
64t_stream_unstream = (S.unstream . S.stream) `eq` id
62tl_stream_unstream = (SL.unstream . SL.stream) `eq` id
65tl_stream_unstream = (SL.unstream . SL.stream) `eq` id
63−t_reverse_stream t = (S.reverse . S.reverseStream) t == t
66+t_reverse_stream t = (S.reverse . S.reverseStream) t === t
64−t_singleton c = [c] == (T.unpack . T.singleton) c
67+t_singleton c = [c] === (T.unpack . T.singleton) c
65−tl_singleton c = [c] == (TL.unpack . TL.singleton) c
68+tl_singleton c = [c] === (TL.unpack . TL.singleton) c
66−tl_unstreamChunks x = f 11 x == f 1000 x
69+tl_unstreamChunks x = f 11 x === f 1000 x
67 where f n = SL.unstreamChunks n . S.streamList
70 where f n = SL.unstreamChunks n . S.streamList
68tl_chunk_unchunk = (TL.fromChunks . TL.toChunks) `eq` id
71tl_chunk_unchunk = (TL.fromChunks . TL.toChunks) `eq` id
69tl_from_to_strict = (TL.fromStrict . TL.toStrict) `eq` id
72tl_from_to_strict = (TL.fromStrict . TL.toStrict) `eq` id
@@ -74,13 +77,13 @@ encodeL1 = B.pack . map (fromIntegral . fromEnum) . T.unpack
74encodeLazyL1 :: TL.Text -> BL.ByteString
77encodeLazyL1 :: TL.Text -> BL.ByteString
75encodeLazyL1 = BL.fromChunks . map encodeL1 . TL.toChunks
78encodeLazyL1 = BL.fromChunks . map encodeL1 . TL.toChunks
76
79
77−t_ascii t = E.decodeASCII (E.encodeUtf8 a) == a
80+t_ascii t = E.decodeASCII (E.encodeUtf8 a) === a
78 where a = T.map (\c -> chr (ord c `mod` 128)) t
81 where a = T.map (\c -> chr (ord c `mod` 128)) t
79−tl_ascii t = EL.decodeASCII (EL.encodeUtf8 a) == a
82+tl_ascii t = EL.decodeASCII (EL.encodeUtf8 a) === a
80 where a = TL.map (\c -> chr (ord c `mod` 128)) t
83 where a = TL.map (\c -> chr (ord c `mod` 128)) t
81−t_latin1 t = E.decodeLatin1 (encodeL1 a) == a
84+t_latin1 t = E.decodeLatin1 (encodeL1 a) === a
82 where a = T.map (\c -> chr (ord c `mod` 256)) t
85 where a = T.map (\c -> chr (ord c `mod` 256)) t
83−tl_latin1 t = EL.decodeLatin1 (encodeLazyL1 a) == a
86+tl_latin1 t = EL.decodeLatin1 (encodeLazyL1 a) === a
84 where a = TL.map (\c -> chr (ord c `mod` 256)) t
87 where a = TL.map (\c -> chr (ord c `mod` 256)) t
85t_utf8 = forAll genUnicode $ (E.decodeUtf8 . E.encodeUtf8) `eq` id
88t_utf8 = forAll genUnicode $ (E.decodeUtf8 . E.encodeUtf8) `eq` id
86t_utf8' = forAll genUnicode $ (E.decodeUtf8' . E.encodeUtf8) `eq` (id . Right)
89t_utf8' = forAll genUnicode $ (E.decodeUtf8' . E.encodeUtf8) `eq` (id . Right)
@@ -111,7 +114,7 @@ t_utf8_undecoded = forAll genUnicode $ \t ->
111 let b = E.encodeUtf8 t
114 let b = E.encodeUtf8 t
112 ls = concatMap (leftover . E.encodeUtf8 . T.singleton) . T.unpack $ t
115 ls = concatMap (leftover . E.encodeUtf8 . T.singleton) . T.unpack $ t
113 leftover = (++ [B.empty]) . init . tail . B.inits
116 leftover = (++ [B.empty]) . init . tail . B.inits
114− in (map snd . feedChunksOf 1 E.streamDecodeUtf8) b == ls
117+ in (map snd . feedChunksOf 1 E.streamDecodeUtf8) b === ls
115
118
116data Badness = Solo | Leading | Trailing
119data Badness = Solo | Leading | Trailing
117 deriving (Eq, Show)
120 deriving (Eq, Show)
@@ -177,6 +180,33 @@ genInvalidUTF8 = B.pack <$> oneof [
177 k <- choose (0,n)
180 k <- choose (0,n)
178 vectorOf k gen
181 vectorOf k gen
179
182
183+-- See http://unicode.org/faq/utf_bom.html#gen8
184+-- A sequence such as <110xxxxx2 0xxxxxxx2> is illegal ...
185+-- When faced with this illegal byte sequence ... a UTF-8 conformant process
186+-- must treat the first byte 110xxxxx2 as an illegal termination error
187+-- (e.g. filter it out or replace by 0xFFFD) ...
188+-- ... and continue processing at the second byte 0xxxxxxx2
189+t_decode_with_error2 =
190+ E.decodeUtf8With (\_ _ -> Just 'x') (B.pack [0xC2, 97]) === "xa"
191+t_decode_with_error3 =
192+ E.decodeUtf8With (\_ _ -> Just 'x') (B.pack [0xE0, 97, 97]) === "xaa"
193+t_decode_with_error4 =
194+ E.decodeUtf8With (\_ _ -> Just 'x') (B.pack [0xF0, 97, 97, 97]) === "xaaa"
195+
196+t_decode_with_error2' =
197+ case E.streamDecodeUtf8With (\_ _ -> Just 'x') (B.pack [0xC2, 97]) of
198+ E.Some x _ _ -> x === "xa"
199+t_decode_with_error3' =
200+ case E.streamDecodeUtf8With (\_ _ -> Just 'x') (B.pack [0xC2, 97, 97]) of
201+ E.Some x _ _ -> x === "xaa"
202+t_decode_with_error4' =
203+ case E.streamDecodeUtf8With (\_ _ -> Just 'x') (B.pack [0xC2, 97, 97, 97]) of
204+ E.Some x _ _ -> x === "xaaa"
205+
206+t_infix_concat bs1 text bs2 rep =
207+ text `T.isInfixOf`
208+ E.decodeUtf8With (\_ _ -> rep) (B.concat [bs1, E.encodeUtf8 text, bs2])
209+
180s_Eq s = (s==) `eq` ((S.streamList s==) . S.streamList)
210s_Eq s = (s==) `eq` ((S.streamList s==) . S.streamList)
181 where _types = s :: String
211 where _types = s :: String
182sf_Eq p s =
212sf_Eq p s =
@@ -201,8 +231,8 @@ t_mconcat = unsquare $
201 mconcat `eq` (unpackS . mconcat . L.map T.pack)
231 mconcat `eq` (unpackS . mconcat . L.map T.pack)
202tl_mconcat = unsquare $
232tl_mconcat = unsquare $
203 mconcat `eq` (unpackS . mconcat . L.map TL.pack)
233 mconcat `eq` (unpackS . mconcat . L.map TL.pack)
204−t_mempty = mempty == (unpackS (mempty :: T.Text))
234+t_mempty = mempty === (unpackS (mempty :: T.Text))
205−tl_mempty = mempty == (unpackS (mempty :: TL.Text))
235+tl_mempty = mempty === (unpackS (mempty :: TL.Text))
206t_IsString = fromString `eqP` (T.unpack . fromString)
236t_IsString = fromString `eqP` (T.unpack . fromString)
207tl_IsString = fromString `eqP` (TL.unpack . fromString)
237tl_IsString = fromString `eqP` (TL.unpack . fromString)
208
238
@@ -229,6 +259,13 @@ sf_uncons p = (uncons . L.filter p) `eqP`
229 (fmap (second unpackS) . S.uncons . S.filter p)
259 (fmap (second unpackS) . S.uncons . S.filter p)
230t_uncons = uncons `eqP` (fmap (second unpackS) . T.uncons)
260t_uncons = uncons `eqP` (fmap (second unpackS) . T.uncons)
231tl_uncons = uncons `eqP` (fmap (second unpackS) . TL.uncons)
261tl_uncons = uncons `eqP` (fmap (second unpackS) . TL.uncons)
262+
263+unsnoc xs@(_:_) = Just (init xs, last xs)
264+unsnoc [] = Nothing
265+
266+t_unsnoc = unsnoc `eqP` (fmap (first unpackS) . T.unsnoc)
267+tl_unsnoc = unsnoc `eqP` (fmap (first unpackS) . TL.unsnoc)
268+
232s_head = head `eqP` S.head
269s_head = head `eqP` S.head
233sf_head p = (head . L.filter p) `eqP` (S.head . S.filter p)
270sf_head p = (head . L.filter p) `eqP` (S.head . S.filter p)
234t_head = head `eqP` T.head
271t_head = head `eqP` T.head
@@ -326,6 +363,19 @@ t_toUpper_upper t = p (T.toUpper t) >= p t
326 where p = T.length . T.filter isUpper
363 where p = T.length . T.filter isUpper
327tl_toUpper_upper t = p (TL.toUpper t) >= p t
364tl_toUpper_upper t = p (TL.toUpper t) >= p t
328 where p = TL.length . TL.filter isUpper
365 where p = TL.length . TL.filter isUpper
366+t_toTitle_title t = all (<= 1) (caps w)
367+ where caps = fmap (T.length . T.filter isUpper) . T.words . T.toTitle
368+ -- TIL: there exist uppercase-only letters
369+ w = T.filter (\c -> if C.isUpper c then C.toLower c /= c else True) t
370+t_toTitle_1stNotLower = and . notLow . T.toTitle . T.filter stable
371+ where notLow = mapMaybe (fmap (not . isLower) . (T.find isLetter)) . T.words
372+ -- Surprise! The Spanish/Portuguese ordinal indicators changed
373+ -- from category Ll (letter, lowercase) to Lo (letter, other)
374+ -- in Unicode 7.0
375+ -- Oh, and there exist lowercase-only letters (see previous test)
376+ stable c = if isLower c
377+ then C.toUpper c /= c
378+ else c /= '\170' && c /= '\186'
329
379
330justifyLeft k c xs = xs ++ L.replicate (k - length xs) c
380justifyLeft k c xs = xs ++ L.replicate (k - length xs) c
331justifyRight m n xs = L.replicate (m - length xs) n ++ xs
381justifyRight m n xs = L.replicate (m - length xs) n ++ xs
@@ -514,12 +564,20 @@ s_takeWhile p = L.takeWhile p `eqP` (unpackS . S.takeWhile p)
514s_takeWhile_s p = L.takeWhile p `eqP` (unpackS . S.unstream . S.takeWhile p)
564s_takeWhile_s p = L.takeWhile p `eqP` (unpackS . S.unstream . S.takeWhile p)
515sf_takeWhile q p = (L.takeWhile p . L.filter q) `eqP`
565sf_takeWhile q p = (L.takeWhile p . L.filter q) `eqP`
516 (unpackS . S.takeWhile p . S.filter q)
566 (unpackS . S.takeWhile p . S.filter q)
567+noMatch = do
568+ c <- char
569+ d <- suchThat char (/= c)
570+ return (c,d)
517t_takeWhile p = L.takeWhile p `eqP` (unpackS . T.takeWhile p)
571t_takeWhile p = L.takeWhile p `eqP` (unpackS . T.takeWhile p)
518tl_takeWhile p = L.takeWhile p `eqP` (unpackS . TL.takeWhile p)
572tl_takeWhile p = L.takeWhile p `eqP` (unpackS . TL.takeWhile p)
519t_takeWhileEnd p = (L.reverse . L.takeWhile p . L.reverse) `eqP`
573t_takeWhileEnd p = (L.reverse . L.takeWhile p . L.reverse) `eqP`
520 (unpackS . T.takeWhileEnd p)
574 (unpackS . T.takeWhileEnd p)
575+t_takeWhileEnd_null t = forAll noMatch $ \(c,d) -> T.null $
576+ T.takeWhileEnd (==d) (T.snoc t c)
521tl_takeWhileEnd p = (L.reverse . L.takeWhile p . L.reverse) `eqP`
577tl_takeWhileEnd p = (L.reverse . L.takeWhile p . L.reverse) `eqP`
522 (unpackS . TL.takeWhileEnd p)
578 (unpackS . TL.takeWhileEnd p)
579+tl_takeWhileEnd_null t = forAll noMatch $ \(c,d) -> TL.null $
580+ TL.takeWhileEnd (==d) (TL.snoc t c)
523s_dropWhile p = L.dropWhile p `eqP` (unpackS . S.dropWhile p)
581s_dropWhile p = L.dropWhile p `eqP` (unpackS . S.dropWhile p)
524s_dropWhile_s p = L.dropWhile p `eqP` (unpackS . S.unstream . S.dropWhile p)
582s_dropWhile_s p = L.dropWhile p `eqP` (unpackS . S.unstream . S.dropWhile p)
525sf_dropWhile q p = (L.dropWhile p . L.filter q) `eqP`
583sf_dropWhile q p = (L.dropWhile p . L.filter q) `eqP`
@@ -720,7 +778,7 @@ tl_indices (NotEmpty s) = lazyIndices s `eq` S.indices s
720 conc = T.concat . TL.toChunks
778 conc = T.concat . TL.toChunks
721t_indices_occurs = unsquare $ \(NotEmpty t) ts ->
779t_indices_occurs = unsquare $ \(NotEmpty t) ts ->
722 let s = T.intercalate t ts
780 let s = T.intercalate t ts
723− in Slow.indices t s == indices t s
781+ in Slow.indices t s === indices t s
724
782
725-- Bit shifts.
783-- Bit shifts.
726shiftL w = forAll (choose (0,width-1)) $ \k -> Bits.shiftL w k == U.shiftL w k
784shiftL w = forAll (choose (0,width-1)) $ \k -> Bits.shiftL w k == U.shiftL w k
@@ -812,18 +870,18 @@ tb_formatRealFloat_double (a::Double) = tb_formatRealFloat a
812-- Reading.
870-- Reading.
813
871
814t_decimal (n::Int) s =
872t_decimal (n::Int) s =
815− T.signed T.decimal (T.pack (show n) `T.append` t) == Right (n,t)
873+ T.signed T.decimal (T.pack (show n) `T.append` t) === Right (n,t)
816 where t = T.dropWhile isDigit s
874 where t = T.dropWhile isDigit s
817tl_decimal (n::Int) s =
875tl_decimal (n::Int) s =
818− TL.signed TL.decimal (TL.pack (show n) `TL.append` t) == Right (n,t)
876+ TL.signed TL.decimal (TL.pack (show n) `TL.append` t) === Right (n,t)
819 where t = TL.dropWhile isDigit s
877 where t = TL.dropWhile isDigit s
820t_hexadecimal m s ox =
878t_hexadecimal m s ox =
821− T.hexadecimal (T.concat [p, T.pack (showHex n ""), t]) == Right (n,t)
879+ T.hexadecimal (T.concat [p, T.pack (showHex n ""), t]) === Right (n,t)
822 where t = T.dropWhile isHexDigit s
880 where t = T.dropWhile isHexDigit s
823 p = if ox then "0x" else ""
881 p = if ox then "0x" else ""
824 n = getPositive m :: Int
882 n = getPositive m :: Int
825tl_hexadecimal m s ox =
883tl_hexadecimal m s ox =
826− TL.hexadecimal (TL.concat [p, TL.pack (showHex n ""), t]) == Right (n,t)
884+ TL.hexadecimal (TL.concat [p, TL.pack (showHex n ""), t]) === Right (n,t)
827 where t = TL.dropWhile isHexDigit s
885 where t = TL.dropWhile isHexDigit s
828 p = if ox then "0x" else ""
886 p = if ox then "0x" else ""
829 n = getPositive m :: Int
887 n = getPositive m :: Int
@@ -867,11 +925,11 @@ tl_write_read_line e m b t = write_read head TL.filter TL.hPutStrLn
867
925
868t_dropWord16 m t = dropWord16 m t `T.isSuffixOf` t
926t_dropWord16 m t = dropWord16 m t `T.isSuffixOf` t
869t_takeWord16 m t = takeWord16 m t `T.isPrefixOf` t
927t_takeWord16 m t = takeWord16 m t `T.isPrefixOf` t
870−t_take_drop_16 m t = T.append (takeWord16 n t) (dropWord16 n t) == t
928+t_take_drop_16 m t = T.append (takeWord16 n t) (dropWord16 n t) === t
871 where n = small m
929 where n = small m
872t_use_from t = monadicIO $ assert . (==t) =<< run (useAsPtr t fromPtr)
930t_use_from t = monadicIO $ assert . (==t) =<< run (useAsPtr t fromPtr)
873
931
874−t_copy t = T.copy t == t
932+t_copy t = T.copy t === t
875
933
876-- Regression tests.
934-- Regression tests.
877s_filter_eq s = S.filter p t == S.streamList (filter p s)
935s_filter_eq s = S.filter p t == S.streamList (filter p s)
@@ -924,6 +982,15 @@ tests =
924 testGroup "errors" [
982 testGroup "errors" [
925 testProperty "t_utf8_err" t_utf8_err,
983 testProperty "t_utf8_err" t_utf8_err,
926 testProperty "t_utf8_err'" t_utf8_err'
984 testProperty "t_utf8_err'" t_utf8_err'
985+ ],
986+ testGroup "error recovery" [
987+ testProperty "t_decode_with_error2" t_decode_with_error2,
988+ testProperty "t_decode_with_error3" t_decode_with_error3,
989+ testProperty "t_decode_with_error4" t_decode_with_error4,
990+ testProperty "t_decode_with_error2'" t_decode_with_error2',
991+ testProperty "t_decode_with_error3'" t_decode_with_error3',
992+ testProperty "t_decode_with_error4'" t_decode_with_error4',
993+ testProperty "t_infix_concat" t_infix_concat
927 ]
994 ]
928 ],
995 ],
929
996
@@ -967,6 +1034,8 @@ tests =
967 testProperty "sf_uncons" sf_uncons,
1034 testProperty "sf_uncons" sf_uncons,
968 testProperty "t_uncons" t_uncons,
1035 testProperty "t_uncons" t_uncons,
969 testProperty "tl_uncons" tl_uncons,
1036 testProperty "tl_uncons" tl_uncons,
1037+ testProperty "t_unsnoc" t_unsnoc,
1038+ testProperty "tl_unsnoc" tl_unsnoc,
970 testProperty "s_head" s_head,
1039 testProperty "s_head" s_head,
971 testProperty "sf_head" sf_head,
1040 testProperty "sf_head" sf_head,
972 testProperty "t_head" t_head,
1041 testProperty "t_head" t_head,
@@ -1030,7 +1099,9 @@ tests =
1030 testProperty "tl_toLower_lower" tl_toLower_lower,
1099 testProperty "tl_toLower_lower" tl_toLower_lower,
1031 testProperty "t_toUpper_length" t_toUpper_length,
1100 testProperty "t_toUpper_length" t_toUpper_length,
1032 testProperty "t_toUpper_upper" t_toUpper_upper,
1101 testProperty "t_toUpper_upper" t_toUpper_upper,
1033− testProperty "tl_toUpper_upper" tl_toUpper_upper
1102+ testProperty "tl_toUpper_upper" tl_toUpper_upper,
1103+ testProperty "t_toTitle_title" t_toTitle_title,
1104+ testProperty "t_toTitle_1stNotLower" t_toTitle_1stNotLower
1034 ],
1105 ],
1035
1106
1036 testGroup "justification" [
1107 testGroup "justification" [
@@ -1147,7 +1218,9 @@ tests =
1147 testProperty "t_takeWhile" t_takeWhile,
1218 testProperty "t_takeWhile" t_takeWhile,
1148 testProperty "tl_takeWhile" tl_takeWhile,
1219 testProperty "tl_takeWhile" tl_takeWhile,
1149 testProperty "t_takeWhileEnd" t_takeWhileEnd,
1220 testProperty "t_takeWhileEnd" t_takeWhileEnd,
1221+ testProperty "t_takeWhileEnd_null" t_takeWhileEnd_null,
1150 testProperty "tl_takeWhileEnd" tl_takeWhileEnd,
1222 testProperty "tl_takeWhileEnd" tl_takeWhileEnd,
1223+ testProperty "tl_takeWhileEnd_null" tl_takeWhileEnd_null,
1151 testProperty "sf_dropWhile" sf_dropWhile,
1224 testProperty "sf_dropWhile" sf_dropWhile,
1152 testProperty "s_dropWhile" s_dropWhile,
1225 testProperty "s_dropWhile" s_dropWhile,
1153 testProperty "s_dropWhile_s" s_dropWhile_s,
1226 testProperty "s_dropWhile_s" s_dropWhile_s,
tests/Tests/QuickCheckUtils.hs+1−1
@@ -106,7 +106,7 @@ smallArbitrary = sized $ \n -> resize (smallish n) arbitrary
106 where smallish = round . (sqrt :: Double -> Double) . fromIntegral . abs
106 where smallish = round . (sqrt :: Double -> Double) . fromIntegral . abs
107
107
108instance Arbitrary T.Text where
108instance Arbitrary T.Text where
109− arbitrary = T.pack `fmap` arbitrary
109+ arbitrary = T.pack `fmap` string
110 shrink = map T.pack . shrink . T.unpack
110 shrink = map T.pack . shrink . T.unpack
111
111
112instance Arbitrary TL.Text where
112instance Arbitrary TL.Text where
tests/Tests/Regressions.hs+11
@@ -72,6 +72,16 @@ mapAccumL_resize = do
72 assertEqual "mapAccumL should correctly size buffers for two-word results"
72 assertEqual "mapAccumL should correctly size buffers for two-word results"
73 (count * 2) (T.lengthWord16 (snd val))
73 (count * 2) (T.lengthWord16 (snd val))
74
74
75+-- See GitHub #197
76+t197 :: IO ()
77+t197 =
78+ assertBool "length (filter (==',') \"0,00\") should be 1" (currencyParser "0,00")
79+ where
80+ currencyParser x = cond == 1
81+ where
82+ cond = length fltr
83+ fltr = filter (== ',') x
84+
75tests :: F.Test
85tests :: F.Test
76tests = F.testGroup "Regressions"
86tests = F.testGroup "Regressions"
77 [ F.testCase "hGetContents_crash" hGetContents_crash
87 [ F.testCase "hGetContents_crash" hGetContents_crash
@@ -79,4 +89,5 @@ tests = F.testGroup "Regressions"
79 , F.testCase "mapAccumL_resize" mapAccumL_resize
89 , F.testCase "mapAccumL_resize" mapAccumL_resize
80 , F.testCase "replicate_crash" replicate_crash
90 , F.testCase "replicate_crash" replicate_crash
81 , F.testCase "utf8_decode_unsafe" utf8_decode_unsafe
91 , F.testCase "utf8_decode_unsafe" utf8_decode_unsafe
92+ , F.testCase "t197" t197
82 ]
93 ]
tests/text-tests.cabal+18−3
@@ -20,6 +20,11 @@ flag hpc
20 default: False
20 default: False
21 manual: True
21 manual: True
22
22
23+flag bytestring-builder
24+ description: Depend on the bytestring-builder package for backwards compatibility.
25+ default: False
26+ manual: False
27+
23executable text-tests
28executable text-tests
24 main-is: Tests.hs
29 main-is: Tests.hs
25
30
@@ -48,16 +53,21 @@ executable text-tests
48 HUnit >= 1.2,
53 HUnit >= 1.2,
49 QuickCheck >= 2.7,
54 QuickCheck >= 2.7,
50 base == 4.*,
55 base == 4.*,
51− bytestring,
52 deepseq,
56 deepseq,
53 directory,
57 directory,
54− quickcheck-unicode,
58+ quickcheck-unicode >= 1.0.1.0,
55 random,
59 random,
56 test-framework >= 0.4,
60 test-framework >= 0.4,
57 test-framework-hunit >= 0.2,
61 test-framework-hunit >= 0.2,
58 test-framework-quickcheck2 >= 0.2,
62 test-framework-quickcheck2 >= 0.2,
59 text-tests
63 text-tests
60
64
65+ if flag(bytestring-builder)
66+ build-depends: bytestring >= 0.9 && < 0.10.4,
67+ bytestring-builder >= 0.10.4
68+ else
69+ build-depends: bytestring >= 0.10.4
70+
61executable text-tests-stdio
71executable text-tests-stdio
62 main-is: Tests/IO.hs
72 main-is: Tests/IO.hs
63
73
@@ -137,7 +147,12 @@ library
137 array,
147 array,
138 base == 4.*,
148 base == 4.*,
139 binary,
149 binary,
140− bytestring,
141 deepseq,
150 deepseq,
142 ghc-prim,
151 ghc-prim,
143 integer-gmp
152 integer-gmp
153+
154+ if flag(bytestring-builder)
155+ build-depends: bytestring >= 0.9 && < 0.10.4,
156+ bytestring-builder >= 0.10.4
157+ else
158+ build-depends: bytestring >= 0.10.4
text.cabal+9−7
@@ -1,7 +1,7 @@
1name: text
1name: text
2−version: 1.2.2.1
2+version: 1.2.3.0
3−homepage: https://github.com/bos/text
3+homepage: https://github.com/haskell/text
4−bug-reports: https://github.com/bos/text/issues
4+bug-reports: https://github.com/haskell/text/issues
5synopsis: An efficient packed Unicode text type.
5synopsis: An efficient packed Unicode text type.
6description:
6description:
7 .
7 .
@@ -39,6 +39,8 @@ copyright: 2009-2011 Bryan O'Sullivan, 2008-2009 Tom Harper
39category: Data, Text
39category: Data, Text
40build-type: Simple
40build-type: Simple
41cabal-version: >= 1.8
41cabal-version: >= 1.8
42+tested-with: GHC==8.2.2, GHC==8.0.2, GHC==7.10.3, GHC==7.8.4,
43+ GHC==7.6.3, GHC==7.4.2, GHC==7.2.2, GHC==7.0.4
42extra-source-files:
44extra-source-files:
43 -- scripts/CaseFolding.txt
45 -- scripts/CaseFolding.txt
44 -- scripts/SpecialCasing.txt
46 -- scripts/SpecialCasing.txt
@@ -166,21 +168,21 @@ test-suite tests
166 include-dirs: include
168 include-dirs: include
167
169
168 ghc-options:
170 ghc-options:
169− -Wall -threaded -O0 -rtsopts
171+ -Wall -threaded -O1 -rtsopts
170
172
171 cpp-options:
173 cpp-options:
172 -DASSERTS -DHAVE_DEEPSEQ -DTEST_SUITE
174 -DASSERTS -DHAVE_DEEPSEQ -DTEST_SUITE
173
175
174 build-depends:
176 build-depends:
175 HUnit >= 1.2,
177 HUnit >= 1.2,
176− QuickCheck >= 2.7,
178+ QuickCheck >= 2.7 && < 2.10,
177 array,
179 array,
178 base,
180 base,
179 binary,
181 binary,
180 deepseq,
182 deepseq,
181 directory,
183 directory,
182 ghc-prim,
184 ghc-prim,
183− quickcheck-unicode,
185+ quickcheck-unicode >= 1.0.1.0,
184 random,
186 random,
185 test-framework >= 0.4,
187 test-framework >= 0.4,
186 test-framework-hunit >= 0.2,
188 test-framework-hunit >= 0.2,
@@ -201,7 +203,7 @@ test-suite tests
201
203
202source-repository head
204source-repository head
203 type: git
205 type: git
204− location: https://github.com/bos/text
206+ location: https://github.com/haskell/text
205
207
206source-repository head
208source-repository head
207 type: mercurial
209 type: mercurial