@@ -24,6 +24,9 @@ module Streamly.Internal.FileSystem.Path.Common
2424 , toString
2525 , toChars
2626
27+ -- * Conversion
28+ , normalize
29+
2730 -- * Operations
2831 , primarySeparator
2932 , isSeparator
4548
4649import Control.Monad.Catch (MonadThrow (.. ))
4750import Data.Char (ord , isAlpha )
51+ import Data.Function ((&) )
4852import Data.Functor.Identity (Identity (.. ))
4953#ifdef DEBUG
5054import Data.Maybe (fromJust )
@@ -54,7 +58,7 @@ import GHC.Base (unsafeChr)
5458import Language.Haskell.TH (Q , Exp )
5559import Language.Haskell.TH.Quote (QuasiQuoter (.. ))
5660import Streamly.Internal.Data.Array (Array (.. ))
57- import Streamly.Internal.Data.MutByteArray (Unbox )
61+ import Streamly.Internal.Data.MutByteArray (Unbox ( .. ) )
5862import Streamly.Internal.Data.Path (PathException (.. ))
5963import Streamly.Internal.Data.Stream (Stream )
6064import System.IO.Unsafe (unsafePerformIO )
@@ -367,3 +371,52 @@ append :: (Unbox a, Integral a) =>
367371 OS -> (Array a -> String ) -> Array a -> Array a -> Array a
368372append os toStr a b =
369373 withAppendCheck os toStr b (doAppend os a b)
374+
375+ {-# INLINE normalize #-}
376+ normalize :: forall a . (Unbox a , Integral a ) => OS -> Array a -> Array a
377+ normalize os arr =
378+ if arrElemLen == 1
379+ then arr
380+ else Array. unsafeFreeze $ unsafePerformIO $ do
381+ let workSliceMut = Array. unsafeThaw workSlice
382+ workSliceStream = MutArray. read workSliceMut
383+ (mid :: MutArray. MutArray a ) <-
384+ Stream. indexOnSuffix (== sepElem) workSliceStream
385+ & Stream. filter (not . shouldFilterOut)
386+ & fmap (\ (i, len) -> getSliceWithSepSuffix i len workSliceMut)
387+ & Stream. fold (Fold. foldlM' MutArray. unsafeSplice initBufferM)
388+ if startsWithDotSlash && MutArray. length mid == 0
389+ then MutArray. fromListN 2 [fstElem, sndElem]
390+ else pure mid
391+
392+ where
393+
394+ sepElem = fromIntegral (ord (primarySeparator os))
395+ dotElem = fromIntegral (ord ' .' )
396+ arrElemLen = Array. length arr
397+
398+ fstElem = Array. getIndexUnsafe 0 arr
399+ sndElem = Array. getIndexUnsafe 1 arr
400+
401+ startsWithSep = fstElem == sepElem
402+ startsWithDotSlash = fstElem == dotElem && sndElem == sepElem
403+
404+ workSlice
405+ | startsWithSep = Array. getSliceUnsafe 1 (arrElemLen - 1 ) arr
406+ | startsWithDotSlash = Array. getSliceUnsafe 2 (arrElemLen - 2 ) arr
407+ | otherwise = arr
408+ workSliceElemLen = Array. length workSlice
409+
410+ shouldFilterOut (off, len) =
411+ len == 0 ||
412+ (len == 1 && Array. getIndexUnsafe off workSlice == dotElem)
413+
414+ getSliceWithSepSuffix i len
415+ | i + len == workSliceElemLen = MutArray. unsafeGetSlice i len
416+ getSliceWithSepSuffix i len = MutArray. unsafeGetSlice i (len + 1 )
417+
418+ initBufferM = do
419+ (newArr :: MutArray. MutArray a ) <- MutArray. emptyOf arrElemLen
420+ if startsWithSep
421+ then MutArray. unsafeSnoc newArr fstElem
422+ else pure newArr
0 commit comments