on master help sign in
Fix decodeUtf8 use of freed memory
ffcfcacHarendra Kumar
src/Streamly/Internal/Memory/Array/Types.hs+72−18
@@ -67,6 +67,8 @@ module Streamly.Internal.Memory.Array.Types
67 , toArrayMinChunk
67 , toArrayMinChunk
68 , writeN
68 , writeN
69 , writeNUnsafe
69 , writeNUnsafe
70+ , writeNAligned
71+ , writeNAlignedUnmanaged
70 , write
72 , write
71 , writeAligned
73 , writeAligned
72
74
@@ -234,16 +236,13 @@ bytesToElemCount x n =
234-- Construction
236-- Construction
235-------------------------------------------------------------------------------
237-------------------------------------------------------------------------------
236
238
237−-- | Allocate an array that can hold 'count' items. The memory of the array is
239+-- | allocate a new array using the provided allocator function.
238−-- uninitialized.
240+{-# INLINE newArrayAlignedAllocWith #-}
239−--
240−-- Note that this is internal routine, the reference to this array cannot be
241−-- given out until the array has been written to and frozen.
242−{-# INLINE newArray #-}
243−newArray :: forall a. Storable a => Int -> IO (Array a)
241+newArrayAlignedAllocWith :: forall a. Storable a
244−newArray count = do
242+ => (Int -> Int -> IO (ForeignPtr a)) -> Int -> Int -> IO (Array a)
243+newArrayAlignedAllocWith alloc alignSize count = do
245 let size = count * sizeOf (undefined :: a)
244 let size = count * sizeOf (undefined :: a)
246− fptr <- Malloc.mallocForeignPtrAlignedBytes size (alignment (undefined :: a))
245+ fptr <- alloc size alignSize
247 let p = unsafeForeignPtrToPtr fptr
246 let p = unsafeForeignPtrToPtr fptr
248 return $ Array
247 return $ Array
249 { aStart = fptr
248 { aStart = fptr
@@ -251,6 +250,30 @@ newArray count = do
251 , aBound = p `plusPtr` size
250 , aBound = p `plusPtr` size
252 }
251 }
253
252
253+-- | Allocate a new array aligned to the specified alignmend and using
254+-- unmanaged pinned memory. The memory will not be automatically freed by GHC.
255+-- This could be useful in allocate once global data structures. Use carefully
256+-- as incorrect use can lead to memory leak.
257+{-# INLINE newArrayAlignedUnmanaged #-}
258+newArrayAlignedUnmanaged :: forall a. Storable a => Int -> Int -> IO (Array a)
259+newArrayAlignedUnmanaged =
260+ newArrayAlignedAllocWith Malloc.mallocForeignPtrAlignedUnmanagedBytes
261+
262+{-# INLINE newArrayAligned #-}
263+newArrayAligned :: forall a. Storable a => Int -> Int -> IO (Array a)
264+newArrayAligned = newArrayAlignedAllocWith Malloc.mallocForeignPtrAlignedBytes
265+
266+-- XXX can unaligned allocation be more efficient when alignment is not needed?
267+--
268+-- | Allocate an array that can hold 'count' items. The memory of the array is
269+-- uninitialized.
270+--
271+-- Note that this is internal routine, the reference to this array cannot be
272+-- given out until the array has been written to and frozen.
273+{-# INLINE newArray #-}
274+newArray :: forall a. Storable a => Int -> IO (Array a)
275+newArray = newArrayAligned (alignment (undefined :: a))
276+
254-- | Allocate an Array of the given size and run an IO action passing the array
277-- | Allocate an Array of the given size and run an IO action passing the array
255-- start pointer.
278-- start pointer.
256{-# INLINE withNewArray #-}
279{-# INLINE withNewArray #-}
@@ -315,6 +338,7 @@ reallocAligned alignSize newSize Array{..} = do
315 , aBound = pNew `plusPtr` newSize
338 , aBound = pNew `plusPtr` newSize
316 }
339 }
317
340
341+-- XXX can unaligned allocation be more efficient when alignment is not needed?
318{-# INLINABLE realloc #-}
342{-# INLINABLE realloc #-}
319realloc :: forall a. Storable a => Int -> Array a -> IO (Array a)
343realloc :: forall a. Storable a => Int -> Array a -> IO (Array a)
320realloc = reallocAligned (alignment (undefined :: a))
344realloc = reallocAligned (alignment (undefined :: a))
@@ -491,23 +515,53 @@ foldr f z arr = runIdentity $ D.foldr f z $ toStreamD arr
491-- Instances
515-- Instances
492-------------------------------------------------------------------------------
516-------------------------------------------------------------------------------
493
517
494−-- | @writeN n@ folds a maximum of @n@ elements from the input stream to an
495−-- 'Array'.
496−--
497−-- @since 0.7.0
498−{-# INLINE_NORMAL writeN #-}
518+{-# INLINE_NORMAL writeNAllocWith #-}
499−writeN :: forall m a. (MonadIO m, Storable a) => Int -> Fold m a (Array a)
519+writeNAllocWith :: forall m a. (MonadIO m, Storable a)
520+ => (Int -> IO (Array a)) -> Int -> Fold m a (Array a)
500−writeN n = Fold step initial extract
521+writeNAllocWith alloc n = Fold step initial extract
501
522
502 where
523 where
503
524
504− initial = liftIO $ newArray (max n 0)
525+ initial = liftIO $ alloc (max n 0)
505 step arr@(Array _ end bound) _ | end == bound = return arr
526 step arr@(Array _ end bound) _ | end == bound = return arr
506 step (Array start end bound) x = do
527 step (Array start end bound) x = do
507 liftIO $ poke end x
528 liftIO $ poke end x
508 return $ Array start (end `plusPtr` sizeOf (undefined :: a)) bound
529 return $ Array start (end `plusPtr` sizeOf (undefined :: a)) bound
530+ -- XXX note that shirkToFit does not maintain alignment, in case we are
531+ -- using aligned allocation.
509 extract = return -- liftIO . shrinkToFit
532 extract = return -- liftIO . shrinkToFit
510
533
534+-- | @writeN n@ folds a maximum of @n@ elements from the input stream to an
535+-- 'Array'.
536+--
537+-- @since 0.7.0
538+{-# INLINE_NORMAL writeN #-}
539+writeN :: forall m a. (MonadIO m, Storable a) => Int -> Fold m a (Array a)
540+writeN = writeNAllocWith newArray
541+
542+-- | @writeNAligned n@ folds a maximum of @n@ elements from the input
543+-- stream to an 'Array' aligned to the given size.
544+--
545+-- /Internal/
546+--
547+{-# INLINE_NORMAL writeNAligned #-}
548+writeNAligned :: forall m a. (MonadIO m, Storable a)
549+ => Int -> Int -> Fold m a (Array a)
550+writeNAligned alignSize = writeNAllocWith (newArrayAligned alignSize)
551+
552+-- | @writeNAlignedUnmanaged n@ folds a maximum of @n@ elements from the input
553+-- stream to an 'Array' aligned to the given size and using unmanaged memory.
554+-- This could be useful to allocate memory that we need to allocate only once
555+-- in the lifetime of the program.
556+--
557+-- /Internal/
558+--
559+{-# INLINE_NORMAL writeNAlignedUnmanaged #-}
560+writeNAlignedUnmanaged :: forall m a. (MonadIO m, Storable a)
561+ => Int -> Int -> Fold m a (Array a)
562+writeNAlignedUnmanaged alignSize =
563+ writeNAllocWith (newArrayAlignedUnmanaged alignSize)
564+
511data ArrayUnsafe a = ArrayUnsafe
565data ArrayUnsafe a = ArrayUnsafe
512 {-# UNPACK #-} !(ForeignPtr a) -- first address
566 {-# UNPACK #-} !(ForeignPtr a) -- first address
513 {-# UNPACK #-} !(Ptr a) -- first unused address
567 {-# UNPACK #-} !(Ptr a) -- first unused address
@@ -560,7 +614,7 @@ toArrayMinChunk alignSize elemCount = Fold step initial extract
560
614
561 initial = do
615 initial = do
562 when (elemCount < 0) $ error "toArrayMinChunk: elemCount is negative"
616 when (elemCount < 0) $ error "toArrayMinChunk: elemCount is negative"
563− liftIO $ newArray elemCount
617+ liftIO $ newArrayAligned alignSize elemCount
564 step arr@(Array start end bound) x | end == bound = do
618 step arr@(Array start end bound) x | end == bound = do
565 let p = unsafeForeignPtrToPtr start
619 let p = unsafeForeignPtrToPtr start
566 oldSize = end `minusPtr` p
620 oldSize = end `minusPtr` p
src/Streamly/Memory/Malloc.hs+13−1
@@ -22,14 +22,17 @@
22module Streamly.Memory.Malloc
22module Streamly.Memory.Malloc
23 (
23 (
24 mallocForeignPtrAlignedBytes
24 mallocForeignPtrAlignedBytes
25+ , mallocForeignPtrAlignedUnmanagedBytes
25 )
26 )
26where
27where
27
28
28#define USE_GHC_MALLOC
29#define USE_GHC_MALLOC
29
30
31+import Foreign.ForeignPtr (ForeignPtr, newForeignPtr_)
32+import Foreign.Marshal.Alloc (mallocBytes)
30#ifndef USE_GHC_MALLOC
33#ifndef USE_GHC_MALLOC
31import Foreign.ForeignPtr (newForeignPtr)
34import Foreign.ForeignPtr (newForeignPtr)
32−import Foreign.Marshal.Alloc (mallocBytes, finalizerFree)
35+import Foreign.Marshal.Alloc (finalizerFree)
33#endif
36#endif
34
37
35import qualified GHC.ForeignPtr as GHC
38import qualified GHC.ForeignPtr as GHC
@@ -44,3 +47,12 @@ mallocForeignPtrAlignedBytes size _alignment = do
44 p <- mallocBytes size
47 p <- mallocBytes size
45 newForeignPtr finalizerFree p
48 newForeignPtr finalizerFree p
46#endif
49#endif
50+
51+-- memalign alignment size
52+-- foreign import ccall unsafe "stdlib.h posix_memalign" _memalign :: CSize -> CSize -> IO (Ptr a)
53+
54+mallocForeignPtrAlignedUnmanagedBytes :: Int -> Int -> IO (ForeignPtr a)
55+mallocForeignPtrAlignedUnmanagedBytes size _alignment = do
56+ -- XXX use posix_memalign/aligned_alloc for aligned allocation
57+ p <- mallocBytes size
58+ newForeignPtr_ p
src/Streamly/Streams/StreamD.hs+11−4
@@ -3515,9 +3515,8 @@ runFold (Fold step begin done) = foldlMx' step begin done
3515
3515
3516-- XXX Use names decodeSuccess = 0, decodeFailure = 12
3516-- XXX Use names decodeSuccess = 0, decodeFailure = 12
3517
3517
3518−-- Aligning to cacheline makes a barely noticeable difference
3518+decodeTable :: [Word8]
3519−utf8d :: A.Array Word8
3519+decodeTable = [
3520−utf8d = unsafePerformIO $ runFold (A.writeAligned 64) $ fromList [
3521 -- The first part of the table maps bytes to character classes that
3520 -- The first part of the table maps bytes to character classes that
3522 -- to reduce the size of the transition table and create bitmasks.
3521 -- to reduce the size of the transition table and create bitmasks.
3523 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
3522 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@@ -3538,6 +3537,14 @@ utf8d = unsafePerformIO $ runFold (A.writeAligned 64) $ fromList [
3538 12,36,12,12,12,12,12,12,12,12,12,12
3537 12,36,12,12,12,12,12,12,12,12,12,12
3539 ]
3538 ]
3540
3539
3540+utf8d :: A.Array Word8
3541+utf8d =
3542+ unsafePerformIO
3543+ -- Aligning to cacheline makes a barely noticeable difference
3544+ -- XXX currently alignment is not implemented for unmanaged allocation
3545+ $ runFold (A.writeNAlignedUnmanaged 64 (length decodeTable))
3546+ (fromList decodeTable)
3547+
3541-- | Return element at the specified index without checking the bounds.
3548-- | Return element at the specified index without checking the bounds.
3542-- and without touching the foreign ptr.
3549-- and without touching the foreign ptr.
3543{-# INLINE_NORMAL unsafePeekElemOff #-}
3550{-# INLINE_NORMAL unsafePeekElemOff #-}
@@ -3665,7 +3672,7 @@ decodeUtf8With cfm (Stream step state) =
3665 12 ->
3672 12 ->
3666 Skip $
3673 Skip $
3667 transliterateOrError
3674 transliterateOrError
3668− "Streamly.Streams.StreamD.decodeUtf8ArraysWith: Invalid UTF8 codepoint encountered"
3675+ "Streamly.Streams.StreamD.decodeUtf8With: Invalid UTF8 codepoint encountered"
3669 (FreshPointDecodeInit st)
3676 (FreshPointDecodeInit st)
3670 0 -> error "unreachable state"
3677 0 -> error "unreachable state"
3671 _ -> Skip (FreshPointDecoding st sv cp)
3678 _ -> Skip (FreshPointDecoding st sv cp)