packages feed

shortbytestring 0.1.0.0 → 0.2.0.0

raw patch · 6 files changed

+141/−157 lines, 6 filesdep +QuickCheckdep ~randomPVP ok

version bump matches the API change (PVP)

Dependencies added: QuickCheck

Dependency ranges changed: random

API changes (from Hackage documentation)

- Data.ByteString.Short.Internal: packCString :: CString -> IO ShortByteString
- Data.ByteString.Short.Internal: packCStringLen :: CStringLen -> IO ShortByteString
- Data.ByteString.Short.Internal: useAsCString :: ShortByteString -> (CString -> IO a) -> IO a
- Data.ByteString.Short.Internal: useAsCStringLen :: ShortByteString -> (CStringLen -> IO a) -> IO a
+ Data.ByteString.Short.Internal: copyAddrToByteArray :: Ptr a -> MBA RealWorld -> Int -> Int -> ST RealWorld ()
+ Data.ByteString.Short.Internal: decodeWord16LE# :: (# Word#, Word# #) -> Word#
+ Data.ByteString.Short.Internal: encodeWord16LE# :: Word# -> (# Word#, Word# #)
+ Data.ByteString.Short.Internal: indexWord16Array :: BA -> Int -> Word16
+ Data.ByteString.Short.Internal: moduleErrorIO :: String -> String -> IO a
+ Data.ByteString.Short.Internal: moduleErrorMsg :: String -> String -> String
+ Data.ByteString.Short.Internal: packLenWord16 :: Int -> [Word16] -> ShortByteString
+ Data.ByteString.Short.Internal: packLenWord16Rev :: Int -> [Word16] -> ShortByteString
+ Data.ByteString.Short.Internal: packWord16 :: [Word16] -> ShortByteString
+ Data.ByteString.Short.Internal: packWord16Rev :: [Word16] -> ShortByteString
+ Data.ByteString.Short.Internal: unpackWord16 :: ShortByteString -> [Word16]
+ Data.ByteString.Short.Internal: writeWord16Array :: MBA s -> Int -> Word16 -> ST s ()
- Data.ByteString.Short.Internal: newCWString :: ShortByteString -> IO CWString
+ Data.ByteString.Short.Internal: newCWString :: ShortByteString -> IO (Ptr Word16)
- Data.ByteString.Short.Internal: packCWString :: CWString -> IO ShortByteString
+ Data.ByteString.Short.Internal: packCWString :: Ptr Word16 -> IO ShortByteString
- Data.ByteString.Short.Internal: packCWStringLen :: CWStringLen -> IO ShortByteString
+ Data.ByteString.Short.Internal: packCWStringLen :: (Ptr Word16, Int) -> IO ShortByteString
- Data.ByteString.Short.Internal: useAsCWString :: ShortByteString -> (CWString -> IO a) -> IO a
+ Data.ByteString.Short.Internal: useAsCWString :: ShortByteString -> (Ptr Word16 -> IO a) -> IO a
- Data.ByteString.Short.Internal: useAsCWStringLen :: ShortByteString -> (CWStringLen -> IO a) -> IO a
+ Data.ByteString.Short.Internal: useAsCWStringLen :: ShortByteString -> ((Ptr Word16, Int) -> IO a) -> IO a
- Data.ByteString.Short.Word16: newCWString :: ShortByteString -> IO CWString
+ Data.ByteString.Short.Word16: newCWString :: ShortByteString -> IO (Ptr Word16)
- Data.ByteString.Short.Word16: packCWString :: CWString -> IO ShortByteString
+ Data.ByteString.Short.Word16: packCWString :: Ptr Word16 -> IO ShortByteString
- Data.ByteString.Short.Word16: packCWStringLen :: CWStringLen -> IO ShortByteString
+ Data.ByteString.Short.Word16: packCWStringLen :: (Ptr Word16, Int) -> IO ShortByteString
- Data.ByteString.Short.Word16: useAsCWString :: ShortByteString -> (CWString -> IO a) -> IO a
+ Data.ByteString.Short.Word16: useAsCWString :: ShortByteString -> (Ptr Word16 -> IO a) -> IO a
- Data.ByteString.Short.Word16: useAsCWStringLen :: ShortByteString -> (CWStringLen -> IO a) -> IO a
+ Data.ByteString.Short.Word16: useAsCWStringLen :: ShortByteString -> ((Ptr Word16, Int) -> IO a) -> IO a

Files

CHANGELOG.md view
@@ -1,5 +1,9 @@ # Revision history for shortbytestring +## 0.2.0.0 -- 2021-10-31++* Fix issues with CWString conversion+ ## 0.1.0.0 -- YYYY-mm-dd  * First version. Released on an unsuspecting world.
lib/Data/ByteString/Short.hs view
@@ -147,26 +147,9 @@     , takeWhile     ) -import Data.ByteString.Short.Internal-import "bytestring" Data.ByteString.Short-    ( ShortByteString-    , empty-    , fromShort-    , index-#if MIN_VERSION_bytestring(0,11,0)-    , indexMaybe-    , (!?)-#endif-    , length-    , null-    , pack-    , toShort-    , unpack-    ) import "bytestring" Data.ByteString.Short.Internal-    ( createFromPtr )+import Data.ByteString.Short.Internal import Data.Word8-  import qualified "bytestring" Data.ByteString.Short as BS import qualified "bytestring" Data.ByteString.Short.Internal as BS
lib/Data/ByteString/Short/Internal.hs view
@@ -5,46 +5,28 @@ {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE UnboxedTuples #-}--module Data.ByteString.Short.Internal-  ( create-  , asBA-  , BA(..)-  , MBA(..)-  , newPinnedByteArray-  , newByteArray-  , copyByteArray-  , unsafeFreezeByteArray-  , useAsCString-  , useAsCStringLen-  , useAsCWString-  , useAsCWStringLen-  , packCString-  , packCStringLen-  , packCWString-  , packCWStringLen-  , newCWString-  )+{-# LANGUAGE BangPatterns #-} -where+module Data.ByteString.Short.Internal where  import Prelude hiding     ( length )+import qualified Data.Word16 as W16 import GHC.Exts+import GHC.Word import GHC.ST-    ( ST (ST), runST )-import Data.Word+    ( ST (ST) )+import qualified Data.List as List+#if !MIN_VERSION_base(4,13,0) import Foreign.C.String hiding (newCWString)-import Foreign.Marshal.Alloc (allocaBytes)-import Foreign.Marshal.Array (mallocArray0)-import Foreign.Storable (pokeByteOff)-import "bytestring" Data.ByteString.Short.Internal-import Control.Exception ( throwIO )-#if MIN_VERSION_bytestring(0,10,9)-import Data.ByteString.Internal (c_strlen)-#else import Foreign.C.Types+import Foreign.Storable+import Foreign.Marshal.Alloc #endif+import Foreign.Marshal.Array (withArray0, peekArray0, newArray0, withArrayLen, peekArray)+import "bytestring" Data.ByteString.Short.Internal+import Control.Exception ( throwIO )+import Control.Monad.ST   create :: Int -> (forall s. MBA s -> ST s ()) -> ShortByteString@@ -86,7 +68,12 @@     ST $ \s -> case unsafeFreezeByteArray# mba# s of                  (# s', ba# #) -> (# s', BA# ba# #) +copyAddrToByteArray :: Ptr a -> MBA RealWorld -> Int -> Int -> ST RealWorld ()+copyAddrToByteArray (Ptr src#) (MBA# dst#) (I# dst_off#) (I# len#) =+    ST $ \s -> case copyAddrToByteArray# src# dst# dst_off# len# s of+                 s' -> (# s', () #) + -- this is a copy-paste from bytestring #if !MIN_VERSION_bytestring(0,10,9) ------------------------------------------------------------------------@@ -164,10 +151,10 @@ -- @CWString@ must be null terminated. -- -- @since 0.10.10.0-packCWString :: CWString -> IO ShortByteString-packCWString cstr = do-  len <- c_strlen (coerce cstr)-  packCWStringLen (cstr, fromIntegral len)+packCWString :: Ptr Word16 -> IO ShortByteString+packCWString cwstr = do+  cs <- peekArray0 W16._nul cwstr+  return (packWord16 cs)  -- | /O(n)./ Construct a new @ShortByteString@ from a @CWStringLen@. The -- resulting @ShortByteString@ is an immutable copy of the original @CWStringLen@.@@ -175,10 +162,10 @@ -- Haskell heap. -- -- @since 0.10.10.0-packCWStringLen :: CWStringLen -> IO ShortByteString-packCWStringLen (cstr, len) | len >= 0 = createFromPtr cstr len-packCWStringLen (_, len) =-  moduleErrorIO "packCWStringLen" ("negative length: " ++ show len)+packCWStringLen :: (Ptr Word16, Int) -> IO ShortByteString+packCWStringLen (cp, len) = do+  cs <- peekArray len cp+  return (packWord16 cs)   -- | /O(n) construction./ Use a @ShortByteString@ with a function requiring a@@ -187,40 +174,28 @@ -- subcomputation finishes. -- -- @since 0.10.10.0-useAsCWString :: ShortByteString -> (CWString -> IO a) -> IO a-useAsCWString bs action =-  allocaBytes (l+1) $ \buf -> do-      copyToPtr bs 0 buf (fromIntegral l)-      pokeByteOff buf l (0::Word8)-      action buf-  where l = length bs+useAsCWString :: ShortByteString -> (Ptr Word16 -> IO a) -> IO a+useAsCWString = withArray0 W16._nul . unpackWord16  -- | /O(n) construction./ Use a @ShortByteString@ with a function requiring a @CWStringLen@. -- As for @useAsCWString@ this function makes a copy of the original @ShortByteString@. -- It must not be stored or used after the subcomputation finishes. -- -- @since 0.10.10.0-useAsCWStringLen :: ShortByteString -> (CWStringLen -> IO a) -> IO a-useAsCWStringLen bs action =-  allocaBytes l $ \buf -> do-      copyToPtr bs 0 buf (fromIntegral l)-      action (buf, l)-  where l = length bs+useAsCWStringLen :: ShortByteString -> ((Ptr Word16, Int) -> IO a) -> IO a+useAsCWStringLen bs action = withArrayLen (unpackWord16 bs) $ \ len ptr -> action (ptr, len)  -- | /O(n) construction./ Use a @ShortByteString@ with a function requiring a @CWStringLen@. -- As for @useAsCWString@ this function makes a copy of the original @ShortByteString@. -- It must not be stored or used after the subcomputation finishes. -- -- @since 0.10.10.0-newCWString :: ShortByteString -> IO CWString-newCWString bs = do-  ptr <- mallocArray0 l-  copyToPtr bs 0 ptr (fromIntegral l)-  pokeByteOff ptr l (0::Word8)-  return ptr-  where l = length bs+newCWString :: ShortByteString -> IO (Ptr Word16)+newCWString = newArray0 W16._nul . unpackWord16  ++  -- --------------------------------------------------------------------- -- Internal utilities @@ -230,3 +205,77 @@  moduleErrorMsg :: String -> String -> String moduleErrorMsg fun msg = "Data.ByteString.Short." ++ fun ++ ':':' ':msg++packWord16 :: [Word16] -> ShortByteString+packWord16 cs = packLenWord16 (List.length cs) cs++packLenWord16 :: Int -> [Word16] -> ShortByteString+packLenWord16 len ws0 =+    create (len * 2) (\mba -> go mba 0 ws0)+  where+    go :: MBA s -> Int -> [Word16] -> ST s ()+    go !_   !_ []     = return ()+    go !mba !i (w:ws) = do+      writeWord16Array mba i w+      go mba (i+2) ws+++unpackWord16 :: ShortByteString -> [Word16]+unpackWord16 sbs = go len []+  where+    len = length sbs+    go !i !acc+      | i < 1     = acc+      | otherwise = let !w = indexWord16Array (asBA sbs) (i - 2)+                    in go (i - 2) (w:acc)++packWord16Rev :: [Word16] -> ShortByteString+packWord16Rev cs = packLenWord16Rev ((List.length cs) * 2) cs++packLenWord16Rev :: Int -> [Word16] -> ShortByteString+packLenWord16Rev len ws0 =+    create len (\mba -> go mba len ws0)+  where+    go :: MBA s -> Int -> [Word16] -> ST s ()+    go !_   !_ []     = return ()+    go !mba !i (w:ws) = do+      writeWord16Array mba (i - 2) w+      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.+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', () #))++-- | 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.+indexWord16Array :: BA+                 -> Int      -- ^ Word8 index (not Word16)+                 -> Word16+indexWord16Array (BA# ba#) (I# i#) = +  case (# indexWord8Array# ba# i#, indexWord8Array# ba# (i# +# 1#) #) of+    (# lsb#, msb# #) -> W16# ((decodeWord16LE# (# lsb#, msb# #)))+++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#)++
lib/Data/ByteString/Short/Word16.hs view
@@ -179,9 +179,7 @@     , null     ) import GHC.Exts-import GHC.Word-import GHC.ST-    ( ST (ST) )+import GHC.ST ( ST )  import qualified "bytestring" Data.ByteString.Short.Internal as BS import qualified Data.List as List@@ -198,12 +196,12 @@  -- | /O(n)/. Convert a list into a 'ShortByteString' pack :: [Word16] -> ShortByteString-pack = packBytes+pack = packWord16   -- | /O(n)/. Convert a 'ShortByteString' into a list. unpack :: ShortByteString -> [Word16]-unpack = unpackBytes . assertEven+unpack = unpackWord16 . assertEven   -- ---------------------------------------------------------------------@@ -378,7 +376,7 @@ -- > == pack [0, 1, 2, 3, 4, 5] -- unfoldr :: (a -> Maybe (Word16, a)) -> a -> ShortByteString-unfoldr f x0 = packBytesRev $ go x0 mempty+unfoldr f x0 = packWord16Rev $ go x0 mempty  where    go x words' = case f x of                     Nothing -> words'@@ -398,7 +396,7 @@ -- > fst (unfoldrN n f s) == take n (unfoldr f s) -- unfoldrN :: Int -> (a -> Maybe (Word16, a)) -> a -> (ShortByteString, Maybe a)-unfoldrN i f x0 = first packBytesRev $ go (i - 1) x0 mempty+unfoldrN i f x0 = first packWord16Rev $ go (i - 1) x0 mempty  where    go i' x words'     | i' < 0     = (words', Just x)@@ -777,68 +775,8 @@ -- -------------------------------------------------------------------- -- Internal --- | 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.-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', () #)) --- | 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.-indexWord16Array :: BA-                 -> Int      -- ^ Word8 index (not Word16)-                 -> Word16-indexWord16Array (BA# ba#) (I# i#) = -  case (# indexWord8Array# ba# i#, indexWord8Array# ba# (i# +# 1#) #) of-    (# lsb#, msb# #) -> W16# ((decodeWord16LE# (# lsb#, msb# #))) --packBytes :: [Word16] -> ShortByteString-packBytes cs = packLenBytes (List.length cs) cs--packLenBytes :: Int -> [Word16] -> ShortByteString-packLenBytes len ws0 =-    create (len * 2) (\mba -> go mba 0 ws0)-  where-    go :: MBA s -> Int -> [Word16] -> ST s ()-    go !_   !_ []     = return ()-    go !mba !i (w:ws) = do-      writeWord16Array mba i w-      go mba (i+2) ws--packBytesRev :: [Word16] -> ShortByteString-packBytesRev cs = packLenBytesRev ((List.length cs) * 2) cs--packLenBytesRev :: Int -> [Word16] -> ShortByteString-packLenBytesRev len ws0 =-    create len (\mba -> go mba len ws0)-  where-    go :: MBA s -> Int -> [Word16] -> ST s ()-    go !_   !_ []     = return ()-    go !mba !i (w:ws) = do-      writeWord16Array mba (i - 2) w-      go mba (i - 2) ws---unpackBytes :: ShortByteString -> [Word16]-unpackBytes sbs = go len []-  where-    len = BS.length sbs-    go !i !acc-      | i < 1     = acc-      | otherwise = let !w = indexWord16Array (asBA sbs) (i - 2)-                    in go (i - 2) (w:acc)- -- Returns the index of the first match or the length of the whole -- bytestring if nothing matched. findIndexOrLength :: (Word16 -> Bool) -> ShortByteString -> Int@@ -872,13 +810,4 @@   | otherwise = error ("Uneven number of bytes: " <> show (BS.length sbs) <> ". This is not a Word16 bytestream.")  --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#) 
shortbytestring.cabal view
@@ -1,6 +1,6 @@ cabal-version:      3.0 name:               shortbytestring-version:            0.1.0.0+version:            0.2.0.0 license:            MIT license-file:       LICENSE maintainer:         hasufell@posteo.de@@ -33,6 +33,7 @@    hs-source-dirs:           lib   default-language:         Haskell2010+  ghc-options: -Wall -Wextra   build-depends:     , base              >=4.9.1.0  && <5     , bytestring        ^>=0.10.8.1@@ -61,7 +62,7 @@     , base             >=4.9.1.0  && <5     , bytestring       ^>=0.10.8.1     , deepseq          ^>=1.4-    , random           ^>=1.1+    , random           >=1.1      && <1.3     , shortbytestring     , tasty-bench      ^>=0.3 @@ -84,6 +85,7 @@     , tasty-quickcheck     , word16     , word8+    , QuickCheck    ghc-options:      -fwarn-unused-binds -threaded -rtsopts   default-language: Haskell2010
tests/Properties/ByteString/Common.hs view
@@ -20,11 +20,11 @@  #ifdef WORD8 module Properties.ByteString.Short (tests) where-import Data.Word8 (isSpace)+import Data.Word8 (isSpace, _nul) import qualified "shortbytestring" Data.ByteString.Short as B #else module Properties.ByteString.Short.Word16 (tests) where-import Data.Word16 (isSpace)+import Data.Word16 (isSpace, _nul) import qualified "shortbytestring" Data.ByteString.Short.Word16 as B #endif import "shortbytestring" Data.ByteString.Short (ShortByteString)@@ -38,6 +38,7 @@ import Data.Tuple import Test.Tasty import Test.Tasty.QuickCheck+import Test.QuickCheck.Monadic import Text.Show.Functions ()  #ifdef WORD16@@ -396,6 +397,22 @@   --, testProperty "unfoldr" $   --  \n f (toElem -> a) -> B.unpack (B.take (fromIntegral n) (B.unfoldr (fmap (first toElem) . f) a)) ===   --    take n (unfoldr (fmap (first toElem) . f) a)+  --+#ifdef WORD16+  , testProperty "useAsCWString str packCWString == str" $+    \x -> not (B.any (== _nul) x)+      ==> monadicIO $ run (B.useAsCWString x B.packCWString >>= \x' -> pure (x === x'))+  , testProperty "useAsCWStringLen str packCWStringLen == str" $+    \x -> not (B.any (== _nul) x)+      ==> monadicIO $ run (B.useAsCWStringLen x B.packCWStringLen >>= \x' -> pure (x === x'))+#else+  , testProperty "useAsCString str packCString == str" $+    \x -> not (B.any (== _nul) x)+      ==> monadicIO $ run (B.useAsCString x B.packCString >>= \x' -> pure (x === x'))+  , testProperty "useAsCStringLen str packCStringLen == str" $+    \x -> not (B.any (== _nul) x)+      ==> monadicIO $ run (B.useAsCStringLen x B.packCStringLen >>= \x' -> pure (x === x'))+#endif   ]  stripSuffix :: Eq a => [a] -> [a] -> Maybe [a]