Skip to content

Commit 353ee18

Browse files
committed
New API to convert directly between Lazy and Short
1 parent 188512c commit 353ee18

File tree

4 files changed

+47
-1
lines changed

4 files changed

+47
-1
lines changed

Changelog.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44
* [`Data.Data.dataTypeOf` for `StrictByteString` and `LazyByteString` now returns a `DataType` that uses `AlgRep` instead of `NoRep`.](https://github.com/haskell/bytestring/pull/614)
55
* This allows utilities like `syb:Data.Generics.Text.gread` to be meaningfully used at these types containing `ByteString`s.
66
* [`fromListN` in `instance IsList ByteString` truncates input list if it's longer than the size hint](https://github.com/haskell/bytestring/pull/672)
7+
8+
* API additions and behavior changes:
9+
* Data.ByteString.Short now provides `lazyToShort` and `lazyFromShort`.
10+
711
<!--
812
* Bug fixes:
9-
* API additions and behavior changes:
1013
* Deprecations:
1114
* Performance improvements:
1215
* Miscellaneous:

Data/ByteString/Short.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ module Data.ByteString.Short (
7474
unpack,
7575
fromShort,
7676
toShort,
77+
lazyFromShort,
78+
lazyToShort,
7779

7880
-- * Basic interface
7981
snoc,

Data/ByteString/Short/Internal.hs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ module Data.ByteString.Short.Internal (
3333
unpack,
3434
fromShort,
3535
toShort,
36+
lazyFromShort,
37+
lazyToShort,
3638

3739
-- * Basic interface
3840
snoc,
@@ -165,6 +167,7 @@ import Data.Data
165167
( Data(..) )
166168
import Data.Monoid
167169
( Monoid(..) )
170+
import Data.Int (Int64)
168171
import Data.Semigroup
169172
( Semigroup(..), stimesMonoid )
170173
import Data.List.NonEmpty
@@ -183,6 +186,8 @@ import Foreign.C.String
183186
( CString
184187
, CStringLen
185188
)
189+
import Foreign.ForeignPtr ( touchForeignPtr )
190+
import Foreign.ForeignPtr.Unsafe ( unsafeForeignPtrToPtr )
186191
import Foreign.Marshal.Alloc
187192
( allocaBytes )
188193
import Foreign.Storable
@@ -242,6 +247,7 @@ import Prelude
242247
)
243248

244249
import qualified Data.ByteString.Internal.Type as BS
250+
import qualified Data.ByteString.Lazy.Internal as LBS
245251

246252
import qualified Data.List as List
247253
import qualified GHC.Exts
@@ -450,6 +456,38 @@ toShortIO (BS fptr len) = do
450456
stToIO (copyAddrToByteArray ptr mba 0 len)
451457
ShortByteString <$> stToIO (unsafeFreezeByteArray mba)
452458

459+
-- | A simple wrapper around 'fromShort' that wraps the strict 'ByteString' as
460+
-- a one-chunk lazy 'LBS.ByteString'.
461+
lazyFromShort :: ShortByteString -> LBS.ByteString
462+
lazyFromShort = LBS.fromStrict . fromShort
463+
464+
-- | /O(n)/. Convert a lazy 'LBS.ByteString' into a 'ShortByteString'.
465+
--
466+
-- This makes a copy, so does not retain the input string. Naturally, best
467+
-- used only with sufficiently short lazy ByteStrings. The entire lazy
468+
-- ByteString is brought into memory before a copy is made.
469+
--
470+
lazyToShort :: LBS.ByteString -> ShortByteString
471+
lazyToShort LBS.Empty = empty
472+
lazyToShort lbs
473+
| tot64 /= fromIntegral total = error "lazyToShort: input too long"
474+
| otherwise = unsafeDupablePerformIO $ do
475+
mba <- stToIO (newByteArray total)
476+
copy mba lbs
477+
ShortByteString <$> stToIO (unsafeFreezeByteArray mba)
478+
where
479+
!tot64 = LBS.foldlChunks (\n (BS _ l) -> n + fromIntegral l) (0 :: Int64) lbs
480+
!total = fromIntegral tot64
481+
482+
copy :: MutableByteArray RealWorld -> LBS.ByteString -> IO ()
483+
copy mba = go 0
484+
where
485+
go off (LBS.Chunk (BS fp len) cs) = do
486+
stToIO $ copyAddrToByteArray (unsafeForeignPtrToPtr fp) mba off len
487+
touchForeignPtr fp
488+
go (off + len) cs
489+
go !_ LBS.Empty = pure ()
490+
453491
-- | /O(n)/. Convert a 'ShortByteString' into a 'ByteString'.
454492
--
455493
fromShort :: ShortByteString -> ByteString

tests/Properties.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,8 @@ prop_short_pack_unpack xs =
530530
(Short.unpack . Short.pack) xs == xs
531531
prop_short_toShort_fromShort bs =
532532
(Short.fromShort . Short.toShort) bs == bs
533+
prop_short_lazyToShort_fromShort lbs =
534+
(Short.lazyFromShort . Short.lazyToShort) lbs == lbs
533535

534536
prop_short_toShort_unpack bs =
535537
(Short.unpack . Short.toShort) bs == P.unpack bs
@@ -596,6 +598,7 @@ prop_short_pinned (NonNegative (I# len#)) = runST $ ST $ \s ->
596598
short_tests =
597599
[ testProperty "pack/unpack" prop_short_pack_unpack
598600
, testProperty "toShort/fromShort" prop_short_toShort_fromShort
601+
, testProperty "lazyToShort/fromShort" prop_short_lazyToShort_fromShort
599602
, testProperty "toShort/unpack" prop_short_toShort_unpack
600603
, testProperty "pack/fromShort" prop_short_pack_fromShort
601604
, testProperty "empty" prop_short_empty

0 commit comments

Comments
 (0)