Cabal-syntax-3.12.0.0: src/Distribution/Parsec/FieldLineStream.hs
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -Wall -Werror #-}
module Distribution.Parsec.FieldLineStream
( FieldLineStream (..)
, fieldLineStreamFromString
, fieldLineStreamFromBS
, fieldLineStreamEnd
) where
import Data.Bits
import Data.ByteString (ByteString)
import Distribution.Compat.Prelude
import Distribution.Utils.Generic (toUTF8BS)
import Prelude ()
import qualified Data.ByteString as BS
import qualified Text.Parsec as Parsec
-- | This is essentially a lazy bytestring, but chunks are glued with newline @\'\\n\'@.
data FieldLineStream
= FLSLast !ByteString
| FLSCons {-# UNPACK #-} !ByteString FieldLineStream
deriving (Show)
fieldLineStreamEnd :: FieldLineStream
fieldLineStreamEnd = FLSLast mempty
-- | Convert 'String' to 'FieldLineStream'.
--
-- /Note:/ inefficient!
fieldLineStreamFromString :: String -> FieldLineStream
fieldLineStreamFromString = FLSLast . toUTF8BS
fieldLineStreamFromBS :: ByteString -> FieldLineStream
fieldLineStreamFromBS = FLSLast
instance Monad m => Parsec.Stream FieldLineStream m Char where
uncons (FLSLast bs) = return $ case BS.uncons bs of
Nothing -> Nothing
Just (c, bs') -> Just (unconsChar c bs' (\bs'' -> FLSLast bs'') fieldLineStreamEnd)
uncons (FLSCons bs s) = return $ case BS.uncons bs of
-- as lines are glued with '\n', we return '\n' here!
Nothing -> Just ('\n', s)
Just (c, bs') -> Just (unconsChar c bs' (\bs'' -> FLSCons bs'' s) s)
-- Based on implementation 'decodeStringUtf8'
unconsChar :: forall a. Word8 -> ByteString -> (ByteString -> a) -> a -> (Char, a)
unconsChar c0 bs0 f next
| c0 <= 0x7F = (chr (fromIntegral c0), f bs0)
| c0 <= 0xBF = (replacementChar, f bs0)
| c0 <= 0xDF = twoBytes
| c0 <= 0xEF = moreBytes 3 0x800 bs0 (fromIntegral $ c0 .&. 0xF)
| c0 <= 0xF7 = moreBytes 4 0x10000 bs0 (fromIntegral $ c0 .&. 0x7)
| c0 <= 0xFB = moreBytes 5 0x200000 bs0 (fromIntegral $ c0 .&. 0x3)
| c0 <= 0xFD = moreBytes 6 0x4000000 bs0 (fromIntegral $ c0 .&. 0x1)
| otherwise = error $ "not implemented " ++ show c0
where
twoBytes = case BS.uncons bs0 of
Nothing -> (replacementChar, next)
Just (c1, bs1)
| c1 .&. 0xC0 == 0x80 ->
if d >= 0x80
then (chr d, f bs1)
else (replacementChar, f bs1)
| otherwise -> (replacementChar, f bs1)
where
d = (fromIntegral (c0 .&. 0x1F) `shiftL` 6) .|. fromIntegral (c1 .&. 0x3F)
moreBytes :: Int -> Int -> ByteString -> Int -> (Char, a)
moreBytes 1 overlong bs' acc
| overlong <= acc
, acc <= 0x10FFFF
, acc < 0xD800 || 0xDFFF < acc =
(chr acc, f bs')
| otherwise =
(replacementChar, f bs')
moreBytes byteCount overlong bs' acc = case BS.uncons bs' of
Nothing -> (replacementChar, f bs')
Just (cn, bs1)
| cn .&. 0xC0 == 0x80 ->
moreBytes
(byteCount - 1)
overlong
bs1
((acc `shiftL` 6) .|. fromIntegral cn .&. 0x3F)
| otherwise -> (replacementChar, f bs1)
replacementChar :: Char
replacementChar = '\xfffd'