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 +2/−0
- System/OsString/Common.hs +29/−11
- System/OsString/Data/ByteString/Short/Internal.hs +19/−43
- System/OsString/Internal.hs +22/−9
- changelog.md +11/−2
- os-string.cabal +26/−7
- tests/Arbitrary.hs +69/−0
- tests/encoding/EncodingSpec.hs +170/−0
- tests/encoding/Main.hs +10/−0
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)+