packages feed

os-string 2.0.0 → 2.0.1

raw patch · 9 files changed

+358/−72 lines, 9 filesdep +quickcheck-classes-basedep ~basePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: quickcheck-classes-base

Dependency ranges changed: base

API changes (from Hackage documentation)

- System.OsString.Data.ByteString.Short.Internal: decodeWord16LE# :: (# Word8#, Word8# #) -> Word16#
- System.OsString.Data.ByteString.Short.Internal: encodeWord16LE# :: Word16# -> (# Word8#, Word8# #)
+ System.OsString: unsafeEncodeUtf :: HasCallStack => String -> OsString
+ System.OsString.Data.ByteString.Short.Internal: word16FromLE# :: Word16# -> Word16#
+ System.OsString.Data.ByteString.Short.Internal: word16ToLE# :: Word16# -> Word16#
+ System.OsString.Internal: unsafeEncodeUtf :: HasCallStack => String -> OsString
+ System.OsString.Posix: unsafeEncodeUtf :: HasCallStack => String -> PosixString
+ System.OsString.Windows: unsafeEncodeUtf :: HasCallStack => String -> WindowsString

Files

System/OsString.hs view
@@ -21,6 +21,7 @@    -- * OsString construction   , encodeUtf+  , unsafeEncodeUtf   , encodeWith   , encodeFS   , osstr@@ -130,6 +131,7 @@     ( unsafeFromChar     , toChar     , encodeUtf+    , unsafeEncodeUtf     , encodeWith     , encodeFS     , osstr
System/OsString/Common.hs view
@@ -3,6 +3,8 @@ {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskellQuotes #-}+{-# LANGUAGE ViewPatterns #-} {-# OPTIONS_GHC -Wno-unused-imports #-}  -- This template expects CPP definitions for:@@ -28,6 +30,7 @@    -- * String construction   , encodeUtf+  , unsafeEncodeUtf   , encodeWith   , encodeFS   , fromBytes@@ -175,7 +178,7 @@ import qualified System.OsString.Data.ByteString.Short as BSP #endif import GHC.Stack (HasCallStack)-import Prelude (Bool, Int, Maybe(..), IO, String, Either(..), fmap, ($), (.), mconcat, fromEnum, fromInteger, mempty, fromIntegral, fail, (<$>), show, either, pure, const, flip)+import Prelude (Bool(..), Int, Maybe(..), IO, String, Either(..), fmap, ($), (.), mconcat, fromEnum, fromInteger, mempty, fromIntegral, fail, (<$>), show, either, pure, const, flip, error, id) import Data.Bifunctor ( bimap ) import qualified System.OsString.Data.ByteString.Short.Word16 as BS16 import qualified System.OsString.Data.ByteString.Short as BS8@@ -187,13 +190,15 @@ -- -- This encodes as UTF16-LE (strictly), which is a pretty good guess. ----- Throws an 'EncodingException' if encoding fails.+-- Throws an 'EncodingException' if encoding fails. If the input does not+-- contain surrogate chars, you can use @unsafeEncodeUtf@. #else -- | Partial unicode friendly encoding. -- -- This encodes as UTF8 (strictly), which is a good guess. ----- Throws an 'EncodingException' if encoding fails.+-- Throws an 'EncodingException' if encoding fails. If the input does not+-- contain surrogate chars, you can use 'unsafeEncodeUtf'. #endif encodeUtf :: MonadThrow m => String -> m PLATFORM_STRING #ifdef WINDOWS@@ -202,6 +207,17 @@ encodeUtf = either throwM pure . encodeWith utf8 #endif +-- | Unsafe unicode friendly encoding.+--+-- Like 'encodeUtf', except it crashes when the input contains+-- surrogate chars. For sanitized input, this can be useful.+unsafeEncodeUtf :: HasCallStack => String -> PLATFORM_STRING+#ifdef WINDOWS+unsafeEncodeUtf = either (error . displayException) id . encodeWith utf16le+#else+unsafeEncodeUtf = either (error . displayException) id . encodeWith utf8+#endif+ -- | Encode a 'String' with the specified encoding. encodeWith :: TextEncoding            -> String@@ -345,23 +361,25 @@   { quoteExp = \s -> do       ps <- either (fail . show) pure $ encodeWith (mkUTF16le ErrorOnCodingFailure) s       lift ps-  , quotePat  = \_ ->-      fail "illegal QuasiQuote (allowed as expression only, used as a pattern)"+  , quotePat = \s -> do+      osp' <- either (fail . show) pure . encodeWith (mkUTF16le ErrorOnCodingFailure) $ s+      [p|((==) osp' -> True)|]   , quoteType = \_ ->-      fail "illegal QuasiQuote (allowed as expression only, used as a type)"+      fail "illegal QuasiQuote (allowed as expression or pattern only, used as a type)"   , quoteDec  = \_ ->-      fail "illegal QuasiQuote (allowed as expression only, used as a declaration)"+      fail "illegal QuasiQuote (allowed as expression or pattern only, used as a declaration)"   } #else   { quoteExp = \s -> do       ps <- either (fail . show) pure $ encodeWith (mkUTF8 ErrorOnCodingFailure) s       lift ps-  , quotePat  = \_ ->-      fail "illegal QuasiQuote (allowed as expression only, used as a pattern)"+  , quotePat = \s -> do+      osp' <- either (fail . show) pure . encodeWith (mkUTF8 ErrorOnCodingFailure) $ s+      [p|((==) osp' -> True)|]   , quoteType = \_ ->-      fail "illegal QuasiQuote (allowed as expression only, used as a type)"+      fail "illegal QuasiQuote (allowed as expression or pattern only, used as a type)"   , quoteDec  = \_ ->-      fail "illegal QuasiQuote (allowed as expression only, used as a declaration)"+      fail "illegal QuasiQuote (allowed as expression or pattern only, used as a declaration)"   } #endif 
System/OsString/Data/ByteString/Short/Internal.hs view
@@ -6,6 +6,9 @@ {-# LANGUAGE ViewPatterns #-} {-# LANGUAGE UnliftedFFITypes         #-} +-- Required for WORDS_BIGENDIAN+#include <ghcautoconf.h>+ -- | -- Module      :  System.OsString.Data.ByteString.Short.Internal -- Copyright   :  © 2022 Julian Ospald@@ -21,7 +24,6 @@  import Control.Monad.ST import Control.Exception (assert, throwIO)-import Data.Bits (Bits(..)) import Data.ByteString.Short.Internal (ShortByteString(..), length) #if !MIN_VERSION_base(4,11,0) import Data.Semigroup@@ -282,64 +284,38 @@       go mba (i - 2) ws  --- | This isn't strictly Word16 array write. Instead it's two consecutive Word8 array--- writes to avoid endianness issues due to primops doing automatic alignment based--- on host platform. We want to always write LE to the byte array.+-- | Encode Word16 as little-endian. writeWord16Array :: MBA s                  -> Int      -- ^ Word8 index (not Word16)                  -> Word16                  -> ST s ()-writeWord16Array (MBA# mba#) (I# i#) (W16# w#) =-  case encodeWord16LE# w# of-    (# lsb#, msb# #) ->-      ST (\s -> case writeWord8Array# mba# i# lsb# s of-          s' -> (# s', () #)) >>-      ST (\s -> case writeWord8Array# mba# (i# +# 1#) msb# s of-          s' -> (# s', () #))+writeWord16Array (MBA# mba#) (I# i#) (W16# w#) = ST $ \s ->+  case writeWord8ArrayAsWord16# mba# i# (word16ToLE# w#) s of+    s' -> (# s', () #)  indexWord8Array :: BA                 -> Int      -- ^ Word8 index                 -> Word8 indexWord8Array (BA# ba#) (I# i#) = W8# (indexWord8Array# ba# i#) --- | This isn't strictly Word16 array read. Instead it's two Word8 array reads--- to avoid endianness issues due to primops doing automatic alignment based--- on host platform. We expect the byte array to be LE always.+-- | Decode Word16 from little-endian. indexWord16Array :: BA                  -> Int      -- ^ Word8 index (not Word16)                  -> Word16-indexWord16Array ba i = fromIntegral lsb .|. (fromIntegral msb `shiftL` 8)-  where-    lsb = indexWord8Array ba i-    msb = indexWord8Array ba (i + 1)--#if !MIN_VERSION_base(4,16,0)--encodeWord16LE# :: Word#              -- ^ Word16-                -> (# Word#, Word# #) -- ^ Word8 (LSB, MSB)-encodeWord16LE# x# = (# x# `and#` int2Word# 0xff#-                     ,  x# `and#` int2Word# 0xff00# `shiftRL#` 8# #)--decodeWord16LE# :: (# Word#, Word# #) -- ^ Word8 (LSB, MSB)-                -> Word#              -- ^ Word16-decodeWord16LE# (# lsb#, msb# #) = msb# `shiftL#` 8# `or#` lsb#+indexWord16Array (BA# ba#) (I# i#) =+  W16# (word16FromLE# (indexWord8ArrayAsWord16# ba# i#)) +#if MIN_VERSION_base(4,16,0)+word16ToLE#, word16FromLE# :: Word16# -> Word16# #else--encodeWord16LE# :: Word16#              -- ^ Word16-                -> (# Word8#, Word8# #) -- ^ Word8 (LSB, MSB)-encodeWord16LE# x# = (# word16ToWord8# x#-                     ,  word16ToWord8# (x# `uncheckedShiftRLWord16#` 8#) #)-  where-    word16ToWord8# y = wordToWord8# (word16ToWord# y)--decodeWord16LE# :: (# Word8#, Word8# #) -- ^ Word8 (LSB, MSB)-                -> Word16#              -- ^ Word16-decodeWord16LE# (# lsb#, msb# #) = ((word8ToWord16# msb# `uncheckedShiftLWord16#` 8#) `orWord16#` word8ToWord16# lsb#)-  where-    word8ToWord16# y = wordToWord16# (word8ToWord# y)-+word16ToLE#, word16FromLE# :: Word# -> Word# #endif+#ifdef WORDS_BIGENDIAN+word16ToLE# = byteSwap16#+#else+word16ToLE# w# = w#+#endif+word16FromLE# = word16ToLE#  setByteArray :: MBA s -> Int -> Int -> Int -> ST s () setByteArray (MBA# dst#) (I# off#) (I# len#) (I# c#) =
System/OsString/Internal.hs view
@@ -3,6 +3,8 @@ {-# LANGUAGE UnliftedFFITypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TemplateHaskellQuotes #-}+{-# LANGUAGE ViewPatterns #-}  -- needed to quote a view pattern  module System.OsString.Internal where @@ -40,10 +42,18 @@ -- On windows this encodes as UTF16-LE (strictly), which is a pretty good guess. -- On unix this encodes as UTF8 (strictly), which is a good guess. ----- Throws a 'EncodingException' if encoding fails.+-- Throws an 'EncodingException' if encoding fails. If the input does not+-- contain surrogate chars, you can use 'unsafeEncodeUtf'. encodeUtf :: MonadThrow m => String -> m OsString encodeUtf = fmap OsString . PF.encodeUtf +-- | Unsafe unicode friendly encoding.+--+-- Like 'encodeUtf', except it crashes when the input contains+-- surrogate chars. For sanitized input, this can be useful.+unsafeEncodeUtf :: HasCallStack => String -> OsString+unsafeEncodeUtf = OsString . PF.unsafeEncodeUtf+ -- | Encode an 'OsString' given the platform specific encodings. encodeWith :: TextEncoding  -- ^ unix text encoding            -> TextEncoding  -- ^ windows text encoding@@ -122,6 +132,7 @@  -- | QuasiQuote an 'OsString'. This accepts Unicode characters -- and encodes as UTF-8 on unix and UTF-16 on windows.+-- If used as pattern, requires turning on the @ViewPatterns@ extension. osstr :: QuasiQuoter osstr =   QuasiQuoter@@ -129,23 +140,25 @@   { quoteExp = \s -> do       osp <- either (fail . show) (pure . OsString) . PF.encodeWith (mkUTF16le ErrorOnCodingFailure) $ s       lift osp-  , quotePat  = \_ ->-      fail "illegal QuasiQuote (allowed as expression only, used as a pattern)"+  , quotePat = \s -> do+      osp' <- either (fail . show) (pure . OsString) . PF.encodeWith (mkUTF16le ErrorOnCodingFailure) $ s+      [p|((==) osp' -> True)|]   , quoteType = \_ ->-      fail "illegal QuasiQuote (allowed as expression only, used as a type)"+      fail "illegal QuasiQuote (allowed as expression or pattern only, used as a type)"   , quoteDec  = \_ ->-      fail "illegal QuasiQuote (allowed as expression only, used as a declaration)"+      fail "illegal QuasiQuote (allowed as expression or pattern only, used as a declaration)"   } #else   { quoteExp = \s -> do       osp <- either (fail . show) (pure . OsString) . PF.encodeWith (mkUTF8 ErrorOnCodingFailure) $ s       lift osp-  , quotePat  = \_ ->-      fail "illegal QuasiQuote (allowed as expression only, used as a pattern)"+  , quotePat = \s -> do+      osp' <- either (fail . show) (pure . OsString) . PF.encodeWith (mkUTF8 ErrorOnCodingFailure) $ s+      [p|((==) osp' -> True)|]   , quoteType = \_ ->-      fail "illegal QuasiQuote (allowed as expression only, used as a type)"+      fail "illegal QuasiQuote (allowed as expression or pattern only, used as a type)"   , quoteDec  = \_ ->-      fail "illegal QuasiQuote (allowed as expression only, used as a declaration)"+      fail "illegal QuasiQuote (allowed as expression or pattern only, used as a declaration)"   } #endif 
changelog.md view
@@ -1,6 +1,15 @@ # Changelog for [`os-string` package](http://hackage.haskell.org/package/os-string) -## 1.0.0 *??? 2023*+## 2.0.1 *Dec 2023* -* Split out `OsString` from filepath package+* add `unsafeEncodeUtf`, fixes #5++## 2.0.0 *Nov 2023*++* Split out `OsString` modules from filepath library+* add more bytestring like functions (index/search/etc.)++## 1.0.0 *Nov 2023*++* dummy release to avoid name clashes with filepath <1.5 
os-string.cabal view
@@ -1,6 +1,6 @@ cabal-version:      2.2 name:               os-string-version:            2.0.0+version:            2.0.1  -- NOTE: Don't forget to update ./changelog.md license:            BSD-3-Clause@@ -16,14 +16,14 @@ build-type:         Simple synopsis:           Library for manipulating Operating system strings. tested-with:-  GHC ==8.0.2-   || ==8.2.2-   || ==8.4.4-   || ==8.6.5+  GHC ==8.6.5    || ==8.8.4    || ==8.10.7    || ==9.0.2-   || ==9.2.3+   || ==9.2.8+   || ==9.4.8+   || ==9.6.3+   || ==9.8.1  description:   This package provides functionality for manipulating @OsString@ values, and is shipped with <https://www.haskell.org/ghc/ GHC>.@@ -63,7 +63,7 @@    default-language: Haskell2010   build-depends:-    , base              >=4.9      && <4.20+    , base              >=4.12.0.0      && <4.20     , bytestring        >=0.11.3.0     , deepseq     , exceptions@@ -90,6 +90,25 @@     , bytestring  >=0.11.3.0     , os-string     , QuickCheck  >=2.7      && <2.15++test-suite encoding-tests+  default-language: Haskell2010+  ghc-options:      -Wall+  type:             exitcode-stdio-1.0+  main-is:          Main.hs+  hs-source-dirs:   tests tests/encoding+  other-modules:+    Arbitrary+    EncodingSpec+    TestUtil++  build-depends:+    , base+    , bytestring  >=0.11.3.0+    , deepseq+    , os-string+    , QuickCheck  >=2.7      && <2.15+    , quickcheck-classes-base ^>=0.6.2  benchmark bench   main-is:          Bench.hs
+ tests/Arbitrary.hs view
@@ -0,0 +1,69 @@+{-# OPTIONS_GHC -Wno-orphans #-}++module Arbitrary where++import Data.Char+import Data.Maybe+import System.OsString+import System.OsString.Internal.Types+import qualified System.OsString.Posix as Posix+import qualified System.OsString.Windows as Windows+import Data.ByteString ( ByteString )+import qualified Data.ByteString as ByteString+import Test.QuickCheck+++instance Arbitrary OsString where+  arbitrary = fmap fromJust $ encodeUtf <$> listOf filepathChar++instance Arbitrary PosixString where+  arbitrary = fmap fromJust $ Posix.encodeUtf <$> listOf filepathChar++instance Arbitrary WindowsString where+  arbitrary = fmap fromJust $ Windows.encodeUtf <$> listOf filepathChar+++newtype NonNullString = NonNullString { nonNullString :: String }+  deriving Show++instance Arbitrary NonNullString where+  arbitrary = NonNullString <$> listOf filepathChar++filepathChar :: Gen Char+filepathChar = arbitraryUnicodeChar `suchThat` (\c -> not (isNull c) && isValidUnicode c)+ where+  isNull = (== '\NUL')+  isValidUnicode c = case generalCategory c of+      Surrogate -> False+      NotAssigned -> False+      _ -> True+++newtype NonNullAsciiString = NonNullAsciiString { nonNullAsciiString :: String }+  deriving Show++instance Arbitrary NonNullAsciiString where+  arbitrary = NonNullAsciiString <$> listOf filepathAsciiChar++filepathAsciiChar :: Gen Char+filepathAsciiChar = arbitraryASCIIChar `suchThat` (\c -> not (isNull c))+ where+  isNull = (== '\NUL')++newtype NonNullSurrogateString = NonNullSurrogateString { nonNullSurrogateString :: String }+  deriving Show++instance Arbitrary NonNullSurrogateString where+  arbitrary = NonNullSurrogateString <$> listOf filepathWithSurrogates++filepathWithSurrogates :: Gen Char+filepathWithSurrogates =+  frequency+    [(3, arbitraryASCIIChar),+     (1, arbitraryUnicodeChar),+     (1, arbitraryBoundedEnum)+    ]+++instance Arbitrary ByteString where arbitrary = ByteString.pack <$> arbitrary+instance CoArbitrary ByteString where coarbitrary = coarbitrary . ByteString.unpack
+ tests/encoding/EncodingSpec.hs view
@@ -0,0 +1,170 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE TypeApplications #-}++module EncodingSpec where++import Data.ByteString ( ByteString )+import qualified Data.ByteString as BS++import Arbitrary+import Test.QuickCheck++import Data.Either ( isRight )+import qualified System.OsString.Data.ByteString.Short as BS8+import qualified System.OsString.Data.ByteString.Short.Word16 as BS16+import System.OsString.Encoding.Internal+import GHC.IO (unsafePerformIO)+import GHC.IO.Encoding ( setFileSystemEncoding )+import System.IO+    ( utf16le )+import Control.Exception+import Control.DeepSeq+import Data.Bifunctor ( first )+import GHC.IO.Encoding.Failure ( CodingFailureMode(..) )+import GHC.IO.Encoding.UTF16 ( mkUTF16le )+import GHC.IO.Encoding.UTF8 ( mkUTF8 )+++tests :: [(String, Property)]+tests =+  [ ("ucs2le_decode . ucs2le_encode == id",+    property $ \(padEven -> ba) ->+      let decoded = decodeWithTE ucs2le (BS8.toShort ba)+          encoded = encodeWithTE ucs2le =<< decoded+      in (BS8.fromShort <$> encoded) === Right ba)+  , ("utf16 doesn't handle invalid surrogate pairs",+     property $+      let str = [toEnum 55296, toEnum 55297]+          encoded = encodeWithTE utf16le str+          decoded = decodeWithTE utf16le =<< encoded+#if __GLASGOW_HASKELL__ >= 904+      in decoded === Left (EncodingError ("recoverEncode: invalid argument (cannot encode character " <> show (head str) <> ")") Nothing))+#else+      in decoded === Left (EncodingError "recoverEncode: invalid argument (invalid character)" Nothing))+#endif+  , ("ucs2 handles invalid surrogate pairs",+     property $+      let str = [toEnum 55296, toEnum 55297]+          encoded = encodeWithTE ucs2le str+          decoded = decodeWithTE ucs2le =<< encoded+      in decoded === Right str)+  , ("can roundtrip arbitrary bytes through utf-8 (with RoundtripFailure)",+     property $+      \bs ->+        let decoded = decodeWithTE (mkUTF8 RoundtripFailure) (BS8.toShort bs)+            encoded = encodeWithTE (mkUTF8 RoundtripFailure) =<< decoded+        in (either (const 0) BS8.length encoded, encoded) === (BS8.length (BS8.toShort bs), Right (BS8.toShort bs)))++  , ("can decode arbitrary strings through utf-8 (with RoundtripFailure)",+     property $+      \(NonNullSurrogateString str) ->+        let encoded = encodeWithTE (mkUTF8 RoundtripFailure) str+            decoded = decodeWithTE (mkUTF8 RoundtripFailure) =<< encoded+        in expectFailure $ (either (const 0) length decoded, decoded) === (length str, Right str))++  , ("utf-8 roundtrip encode cannot deal with some surrogates",+     property $+      let str = [toEnum 0xDFF0, toEnum 0xDFF2]+          encoded = encodeWithTE (mkUTF8 RoundtripFailure) str+          decoded = decodeWithTE (mkUTF8 RoundtripFailure) =<< encoded+#if __GLASGOW_HASKELL__ >= 904+      in decoded === Left (EncodingError ("recoverEncode: invalid argument (cannot encode character " <> show (head str) <> ")") Nothing))+#else+      in decoded === Left (EncodingError "recoverEncode: invalid argument (invalid character)" Nothing))+#endif++  , ("cannot roundtrip arbitrary bytes through utf-16 (with RoundtripFailure)",+     property $+      \(padEven -> bs) ->+        let decoded = decodeWithTE (mkUTF16le RoundtripFailure) (BS8.toShort bs)+            encoded = encodeWithTE (mkUTF16le RoundtripFailure) =<< decoded+        in expectFailure $ (either (const 0) BS8.length encoded, encoded) === (BS8.length (BS8.toShort bs), Right (BS8.toShort bs)))+  , ("encodeWithTE/decodeWithTE ErrorOnCodingFailure fails (utf16le)",+     property $+      \(padEven -> bs) ->+        let decoded = decodeWithTE (mkUTF16le ErrorOnCodingFailure) (BS8.toShort bs)+            encoded = encodeWithTE (mkUTF16le ErrorOnCodingFailure) =<< decoded+        in expectFailure $ (isRight encoded, isRight decoded) === (True, True))+  , ("encodeWithTE/decodeWithTE ErrorOnCodingFailure fails (utf8)",+     property $+      \bs ->+        let decoded = decodeWithTE (mkUTF8 ErrorOnCodingFailure) (BS8.toShort bs)+            encoded = encodeWithTE (mkUTF8 ErrorOnCodingFailure) =<< decoded+        in expectFailure $ (isRight encoded, isRight decoded) === (True, True))+  , ("encodeWithTE/decodeWithTE TransliterateCodingFailure never fails (utf16le)",+     property $+      \(padEven -> bs) ->+        let decoded = decodeWithTE (mkUTF16le TransliterateCodingFailure) (BS8.toShort bs)+            encoded = encodeWithTE (mkUTF16le TransliterateCodingFailure) =<< decoded+        in (isRight encoded, isRight decoded) === (True, True))+  , ("encodeWithTE/decodeWithTE TransliterateCodingFailure never fails (utf8)",+     property $+      \bs ->+        let decoded = decodeWithTE (mkUTF8 TransliterateCodingFailure) (BS8.toShort bs)+            encoded = encodeWithTE (mkUTF8 TransliterateCodingFailure) =<< decoded+        in (isRight encoded, isRight decoded) === (True, True))+  , ("encodeWithBaseWindows/decodeWithBaseWindows never fails (utf16le)",+     property $+      \(padEven -> bs) ->+        let decoded = decodeW' (BS8.toShort bs)+            encoded = encodeW' =<< decoded+        in (isRight encoded, isRight decoded) === (True, True))+  , ("encodeWithBasePosix/decodeWithBasePosix never fails (utf8b)",+     property $+      \bs -> ioProperty $ do+        setFileSystemEncoding (mkUTF8 TransliterateCodingFailure)+        let decoded = decodeP' (BS8.toShort bs)+            encoded = encodeP' =<< decoded+        pure $ (isRight encoded, isRight decoded) === (True, True))++  , ("decodeWithBaseWindows == utf16le_b",+     property $+      \(BS8.toShort . padEven -> bs) ->+        let decoded  = decodeW' bs+            decoded' = first displayException $ decodeWithTE (mkUTF16le_b ErrorOnCodingFailure) bs+        in decoded === decoded')++  , ("encodeWithBaseWindows == utf16le_b",+     property $+      \(NonNullSurrogateString str) ->+        let decoded  = encodeW' str+            decoded' = first displayException $ encodeWithTE (mkUTF16le_b ErrorOnCodingFailure) str+        in decoded === decoded')++  , ("encodeWithTE/decodeWithTE never fails (utf16le_b)",+     property $+      \(padEven -> bs) ->+        let decoded = decodeWithTE (mkUTF16le_b ErrorOnCodingFailure) (BS8.toShort bs)+            encoded = encodeWithTE (mkUTF16le_b ErrorOnCodingFailure) =<< decoded+        in (isRight encoded, isRight decoded) === (True, True))+  ]+++padEven :: ByteString -> ByteString+padEven bs+  | even (BS.length bs) = bs+  | otherwise = bs `BS.append` BS.pack [70]+++decodeP' :: BS8.ShortByteString -> Either String String+decodeP' ba = unsafePerformIO $ do+  r <- try @SomeException $ decodeWithBasePosix ba+  evaluate $ force $ first displayException r++encodeP' :: String -> Either String BS8.ShortByteString+encodeP' str = unsafePerformIO $ do+  r <- try @SomeException $ encodeWithBasePosix str+  evaluate $ force $ first displayException r++decodeW' :: BS16.ShortByteString -> Either String String+decodeW' ba = unsafePerformIO $ do+  r <- try @SomeException $ decodeWithBaseWindows ba+  evaluate $ force $ first displayException r++encodeW' :: String -> Either String BS8.ShortByteString+encodeW' str = unsafePerformIO $ do+  r <- try @SomeException $ encodeWithBaseWindows str+  evaluate $ force $ first displayException r+
+ tests/encoding/Main.hs view
@@ -0,0 +1,10 @@+{-# LANGUAGE TypeApplications #-}++module Main (main) where++import qualified EncodingSpec as Spec+import TestUtil++main :: IO ()+main = runTests (Spec.tests)+