{-# LANGUAGE Trustworthy #-}
{-# LANGUAGE CPP, NoImplicitPrelude, NondecreasingIndentation,
             RecordWildCards, ScopedTypeVariables,
             UnboxedTuples #-}
{-# OPTIONS_GHC -Wno-name-shadowing #-}

module GHC.Internal.IO.Encoding.CodePage.API (
    mkCodePageEncoding
  ) where

-- Required for WORDS_BIGENDIAN
#include <ghcautoconf.h>

import GHC.Internal.Ptr
import GHC.Internal.Foreign.C.Types
import GHC.Internal.Foreign.Marshal.Alloc
import GHC.Internal.Foreign.Marshal.Array
import GHC.Internal.Foreign.Storable
import GHC.Internal.Data.Bits
import GHC.Internal.Data.Either
import GHC.Internal.Word

import GHC.Internal.Base
import GHC.Internal.List
import GHC.Internal.IO.Buffer
import GHC.Internal.IO.Encoding.Failure
import GHC.Internal.IO.Encoding.Types
import GHC.Internal.IO.Encoding.UTF16
import GHC.Internal.Num
import GHC.Internal.Show
import GHC.Internal.Real
import GHC.Internal.Windows hiding (LPCSTR)
import GHC.Internal.ForeignPtr (castForeignPtr)

import GHC.Internal.System.Posix.Internals

#if defined(javascript_HOST_ARCH)
mkCodePageEncoding :: String
mkCodePageEncoding = ""
#else

c_DEBUG_DUMP :: Bool
c_DEBUG_DUMP :: Bool
c_DEBUG_DUMP = Bool
False

debugIO :: String -> IO ()
debugIO :: String -> IO ()
debugIO String
s
 | Bool
c_DEBUG_DUMP = String -> IO ()
puts String
s
 | Bool
otherwise    = () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()

#include "windows_cconv.h"

type LPCSTR = Ptr Word8


mAX_DEFAULTCHAR :: Int
mAX_DEFAULTCHAR :: Int
mAX_DEFAULTCHAR = Int
2

mAX_LEADBYTES :: Int
mAX_LEADBYTES :: Int
mAX_LEADBYTES = Int
12

-- Don't really care about the contents of this, but we have to make sure the size is right
data CPINFO = CPINFO {
    CPINFO -> Word32
maxCharSize :: UINT,
    CPINFO -> [Word8]
defaultChar :: [BYTE], -- ^ Always of length mAX_DEFAULTCHAR
    CPINFO -> [Word8]
leadByte    :: [BYTE]  -- ^ Always of length mAX_LEADBYTES
  }

-- | @since base-4.7.0.0
instance Storable CPINFO where
    sizeOf :: CPINFO -> Int
sizeOf    CPINFO
_ = Word32 -> Int
forall a. Storable a => a -> Int
sizeOf (Word32
forall a. HasCallStack => a
undefined :: UINT) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ (Int
mAX_DEFAULTCHAR Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
mAX_LEADBYTES) Int -> Int -> Int
forall a. Num a => a -> a -> a
* Word8 -> Int
forall a. Storable a => a -> Int
sizeOf (Word8
forall a. HasCallStack => a
undefined :: BYTE)
    alignment :: CPINFO -> Int
alignment CPINFO
_ = CInt -> Int
forall a. Storable a => a -> Int
alignment (CInt
forall a. HasCallStack => a
undefined :: CInt)
    peek :: Ptr CPINFO -> IO CPINFO
peek Ptr CPINFO
ptr = do
      ptr <- Ptr Word32 -> IO (Ptr Word32)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Ptr Word32 -> IO (Ptr Word32)) -> Ptr Word32 -> IO (Ptr Word32)
forall a b. (a -> b) -> a -> b
$ Ptr CPINFO -> Ptr Word32
forall a b. Ptr a -> Ptr b
castPtr Ptr CPINFO
ptr
      a <- peek ptr
      ptr <- return $ castPtr $ advancePtr ptr 1
      b <- peekArray mAX_DEFAULTCHAR ptr
      c <- peekArray mAX_LEADBYTES   (advancePtr ptr mAX_DEFAULTCHAR)
      return $ CPINFO a b c
    poke :: Ptr CPINFO -> CPINFO -> IO ()
poke Ptr CPINFO
ptr CPINFO
val = do
      ptr <- Ptr Word32 -> IO (Ptr Word32)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Ptr Word32 -> IO (Ptr Word32)) -> Ptr Word32 -> IO (Ptr Word32)
forall a b. (a -> b) -> a -> b
$ Ptr CPINFO -> Ptr Word32
forall a b. Ptr a -> Ptr b
castPtr Ptr CPINFO
ptr
      poke ptr (maxCharSize val)
      ptr <- return $ castPtr $ advancePtr ptr 1
      pokeArray' "CPINFO.defaultChar" mAX_DEFAULTCHAR ptr                              (defaultChar val)
      pokeArray' "CPINFO.leadByte"    mAX_LEADBYTES   (advancePtr ptr mAX_DEFAULTCHAR) (leadByte val)

pokeArray' :: Storable a => String -> Int -> Ptr a -> [a] -> IO ()
pokeArray' :: forall a. Storable a => String -> Int -> Ptr a -> [a] -> IO ()
pokeArray' String
msg Int
sz Ptr a
ptr [a]
xs | [a] -> Int
forall a. [a] -> Int
length [a]
xs Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
sz = Ptr a -> [a] -> IO ()
forall a. Storable a => Ptr a -> [a] -> IO ()
pokeArray Ptr a
ptr [a]
xs
                         | Bool
otherwise       = String -> IO ()
forall a. String -> a
errorWithoutStackTrace (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
msg String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
": expected " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
sz String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" elements in list but got " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show ([a] -> Int
forall a. [a] -> Int
length [a]
xs)


foreign import WINDOWS_CCONV unsafe "windows.h GetCPInfo"
    c_GetCPInfo :: UINT       -- ^ CodePage
                -> Ptr CPINFO -- ^ lpCPInfo
                -> IO BOOL

foreign import WINDOWS_CCONV unsafe "windows.h MultiByteToWideChar"
    c_MultiByteToWideChar :: UINT   -- ^ CodePage
                          -> DWORD  -- ^ dwFlags
                          -> LPCSTR -- ^ lpMultiByteStr
                          -> CInt   -- ^ cbMultiByte
                          -> LPWSTR -- ^ lpWideCharStr
                          -> CInt   -- ^ cchWideChar
                          -> IO CInt

foreign import WINDOWS_CCONV unsafe "windows.h WideCharToMultiByte"
    c_WideCharToMultiByte :: UINT   -- ^ CodePage
                          -> DWORD  -- ^ dwFlags
                          -> LPWSTR -- ^ lpWideCharStr
                          -> CInt   -- ^ cchWideChar
                          -> LPCSTR -- ^ lpMultiByteStr
                          -> CInt   -- ^ cbMultiByte
                          -> LPCSTR -- ^ lpDefaultChar
                          -> LPBOOL -- ^ lpUsedDefaultChar
                          -> IO CInt

foreign import WINDOWS_CCONV unsafe "windows.h IsDBCSLeadByteEx"
    c_IsDBCSLeadByteEx :: UINT    -- ^ CodePage
                       -> BYTE    -- ^ TestChar
                       -> IO BOOL


-- | Returns a slow but correct implementation of TextEncoding using the Win32 API.
--
-- This is useful for supporting DBCS text encoding on the console without having to statically link
-- in huge code tables into all of our executables, or just as a fallback mechanism if a new code page
-- is introduced that we don't know how to deal with ourselves yet.
mkCodePageEncoding :: CodingFailureMode -> Word32 -> TextEncoding
mkCodePageEncoding :: CodingFailureMode -> Word32 -> TextEncoding
mkCodePageEncoding CodingFailureMode
cfm Word32
cp
  = TextEncoding {
        textEncodingName :: String
textEncodingName = String
"CP" String -> String -> String
forall a. [a] -> [a] -> [a]
++ Word32 -> String
forall a. Show a => a -> String
show Word32
cp,
        mkTextDecoder :: IO (TextDecoder ())
mkTextDecoder = (Buffer Word8
 -> Buffer CharBufElem -> IO (Buffer Word8, Buffer CharBufElem))
-> (Word32 -> Int -> CodeBuffer Word8 CharBufElem)
-> Word32
-> IO (TextDecoder ())
forall from to.
(Buffer from -> Buffer to -> IO (Buffer from, Buffer to))
-> (Word32 -> Int -> CodeBuffer from to)
-> Word32
-> IO (BufferCodec from to ())
newCP (CodingFailureMode
-> Buffer Word8
-> Buffer CharBufElem
-> IO (Buffer Word8, Buffer CharBufElem)
recoverDecode CodingFailureMode
cfm) Word32 -> Int -> CodeBuffer Word8 CharBufElem
cpDecode Word32
cp,
        mkTextEncoder :: IO (TextEncoder ())
mkTextEncoder = (Buffer CharBufElem
 -> Buffer Word8 -> IO (Buffer CharBufElem, Buffer Word8))
-> (Word32 -> Int -> CodeBuffer CharBufElem Word8)
-> Word32
-> IO (TextEncoder ())
forall from to.
(Buffer from -> Buffer to -> IO (Buffer from, Buffer to))
-> (Word32 -> Int -> CodeBuffer from to)
-> Word32
-> IO (BufferCodec from to ())
newCP (CodingFailureMode
-> Buffer CharBufElem
-> Buffer Word8
-> IO (Buffer CharBufElem, Buffer Word8)
recoverEncode CodingFailureMode
cfm) Word32 -> Int -> CodeBuffer CharBufElem Word8
cpEncode Word32
cp
      }

newCP :: (Buffer from -> Buffer to -> IO (Buffer from, Buffer to))
      -> (Word32 -> Int -> CodeBuffer from to)
      -> Word32
      -> IO (BufferCodec from to ())
newCP :: forall from to.
(Buffer from -> Buffer to -> IO (Buffer from, Buffer to))
-> (Word32 -> Int -> CodeBuffer from to)
-> Word32
-> IO (BufferCodec from to ())
newCP Buffer from -> Buffer to -> IO (Buffer from, Buffer to)
rec Word32 -> Int -> CodeBuffer from to
fn Word32
cp = do
  -- Fail early if the code page doesn't exist, to match the behaviour of the IConv TextEncoding
  max_char_size <- (Ptr CPINFO -> IO Int) -> IO Int
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr CPINFO -> IO Int) -> IO Int)
-> (Ptr CPINFO -> IO Int) -> IO Int
forall a b. (a -> b) -> a -> b
$ \Ptr CPINFO
cpinfo_ptr -> do
    success <- Word32 -> Ptr CPINFO -> IO Bool
c_GetCPInfo Word32
cp Ptr CPINFO
cpinfo_ptr
    when (not success) $ throwGetLastError ("GetCPInfo " ++ show cp)
    fmap (fromIntegral . maxCharSize) $ peek cpinfo_ptr

  debugIO $ "GetCPInfo " ++ show cp ++ " = " ++ show max_char_size

  return $ BufferCodec {
    encode = fn cp max_char_size,
    recover = rec,
    close  = return (),
    -- Windows doesn't supply a way to save/restore the state and doesn't need one
    -- since it's a dumb string->string API rather than a clever streaming one.
    getState = return (),
    setState = const $ return ()
  }


utf16_native_encode' :: EncodeBuffer
utf16_native_decode' :: DecodeBuffer
#if defined(WORDS_BIGENDIAN)
utf16_native_encode' i o = IO $ \st -> case utf16be_encode i o st of
  (# st', c, i', o' #) -> (# st', (c, i', o') #)
utf16_native_decode' i o = IO $ \st -> case utf16be_decode i o st of
  (# st', c, i', o' #) -> (# st', (c, i', o') #)
#else
utf16_native_encode' :: CodeBuffer CharBufElem Word8
utf16_native_encode' Buffer CharBufElem
i Buffer Word8
o = (State# RealWorld
 -> (# State# RealWorld,
       (CodingProgress, Buffer CharBufElem, Buffer Word8) #))
-> IO (CodingProgress, Buffer CharBufElem, Buffer Word8)
forall a. (State# RealWorld -> (# State# RealWorld, a #)) -> IO a
IO ((State# RealWorld
  -> (# State# RealWorld,
        (CodingProgress, Buffer CharBufElem, Buffer Word8) #))
 -> IO (CodingProgress, Buffer CharBufElem, Buffer Word8))
-> (State# RealWorld
    -> (# State# RealWorld,
          (CodingProgress, Buffer CharBufElem, Buffer Word8) #))
-> IO (CodingProgress, Buffer CharBufElem, Buffer Word8)
forall a b. (a -> b) -> a -> b
$ \State# RealWorld
st -> case EncodeBuffer#
utf16le_encode Buffer CharBufElem
i Buffer Word8
o State# RealWorld
st of
  (# State# RealWorld
st', CodingProgress
c, Buffer CharBufElem
i', Buffer Word8
o' #) -> (# State# RealWorld
st', (CodingProgress
c, Buffer CharBufElem
i', Buffer Word8
o') #)
utf16_native_decode' :: CodeBuffer Word8 CharBufElem
utf16_native_decode' Buffer Word8
i Buffer CharBufElem
o = (State# RealWorld
 -> (# State# RealWorld,
       (CodingProgress, Buffer Word8, Buffer CharBufElem) #))
-> IO (CodingProgress, Buffer Word8, Buffer CharBufElem)
forall a. (State# RealWorld -> (# State# RealWorld, a #)) -> IO a
IO ((State# RealWorld
  -> (# State# RealWorld,
        (CodingProgress, Buffer Word8, Buffer CharBufElem) #))
 -> IO (CodingProgress, Buffer Word8, Buffer CharBufElem))
-> (State# RealWorld
    -> (# State# RealWorld,
          (CodingProgress, Buffer Word8, Buffer CharBufElem) #))
-> IO (CodingProgress, Buffer Word8, Buffer CharBufElem)
forall a b. (a -> b) -> a -> b
$ \State# RealWorld
st -> case DecodeBuffer#
utf16le_decode Buffer Word8
i Buffer CharBufElem
o State# RealWorld
st of
  (# State# RealWorld
st', CodingProgress
c, Buffer Word8
i', Buffer CharBufElem
o' #) -> (# State# RealWorld
st', (CodingProgress
c, Buffer Word8
i', Buffer CharBufElem
o') #)
#endif

saner :: CodeBuffer from to
      -> Buffer from -> Buffer to
      -> IO (CodingProgress, Int, Buffer from, Buffer to)
saner :: forall from to.
CodeBuffer from to
-> Buffer from
-> Buffer to
-> IO (CodingProgress, Int, Buffer from, Buffer to)
saner CodeBuffer from to
code Buffer from
ibuf Buffer to
obuf = do
  (why, ibuf', obuf') <- CodeBuffer from to
code Buffer from
ibuf Buffer to
obuf
  -- Weird but true: the UTF16 codes have a special case (see the "done" functions)
  -- whereby if they entirely consume the input instead of returning an input buffer
  -- that is empty because bufL has reached bufR, they return a buffer that is empty
  -- because bufL = bufR = 0.
  --
  -- This is really very odd and confusing for our code that expects the difference
  -- between the old and new input buffer bufLs to indicate the number of elements
  -- that were consumed!
  --
  -- We fix it by explicitly extracting an integer which is the # of things consumed, like so:
  if isEmptyBuffer ibuf'
   then return (InputUnderflow, bufferElems ibuf,       ibuf', obuf')
   else return (why,            bufL ibuf' - bufL ibuf, ibuf', obuf')

byteView :: Buffer CWchar -> Buffer Word8
byteView :: Buffer CWchar -> Buffer Word8
byteView (Buffer {Int
Word64
RawBuffer CWchar
BufferState
bufL :: forall e. Buffer e -> Int
bufRaw :: RawBuffer CWchar
bufState :: BufferState
bufSize :: Int
bufOffset :: Word64
bufL :: Int
bufR :: Int
bufR :: forall e. Buffer e -> Int
bufOffset :: forall e. Buffer e -> Word64
bufSize :: forall e. Buffer e -> Int
bufState :: forall e. Buffer e -> BufferState
bufRaw :: forall e. Buffer e -> RawBuffer e
..}) = Buffer { bufState :: BufferState
bufState = BufferState
bufState, bufRaw :: RawBuffer Word8
bufRaw = RawBuffer CWchar -> RawBuffer Word8
forall a b. ForeignPtr a -> ForeignPtr b
castForeignPtr RawBuffer CWchar
bufRaw, bufSize :: Int
bufSize = Int
bufSize Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
2, bufOffset :: Word64
bufOffset = Word64
bufOffset, bufL :: Int
bufL = Int
bufL Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
2, bufR :: Int
bufR = Int
bufR Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
2 }

cwcharView :: Buffer Word8 -> Buffer CWchar
cwcharView :: Buffer Word8 -> Buffer CWchar
cwcharView (Buffer {Int
Word64
RawBuffer Word8
BufferState
bufL :: forall e. Buffer e -> Int
bufR :: forall e. Buffer e -> Int
bufOffset :: forall e. Buffer e -> Word64
bufSize :: forall e. Buffer e -> Int
bufState :: forall e. Buffer e -> BufferState
bufRaw :: forall e. Buffer e -> RawBuffer e
bufRaw :: RawBuffer Word8
bufState :: BufferState
bufSize :: Int
bufOffset :: Word64
bufL :: Int
bufR :: Int
..}) = Buffer { bufState :: BufferState
bufState = BufferState
bufState, bufRaw :: RawBuffer CWchar
bufRaw = RawBuffer Word8 -> RawBuffer CWchar
forall a b. ForeignPtr a -> ForeignPtr b
castForeignPtr RawBuffer Word8
bufRaw, bufSize :: Int
bufSize = Int -> Int
forall {a}. Integral a => a -> a
half Int
bufSize, bufOffset :: Word64
bufOffset = Word64
bufOffset, bufL :: Int
bufL = Int -> Int
forall {a}. Integral a => a -> a
half Int
bufL, bufR :: Int
bufR = Int -> Int
forall {a}. Integral a => a -> a
half Int
bufR }
  where half :: a -> a
half a
x = case a
x a -> a -> (a, a)
forall a. Integral a => a -> a -> (a, a)
`divMod` a
2 of (a
y, a
0) -> a
y
                                      (a, a)
_      -> String -> a
forall a. String -> a
errorWithoutStackTrace String
"cwcharView: utf16_(encode|decode) (wrote out|consumed) non multiple-of-2 number of bytes"

utf16_native_encode :: CodeBuffer Char CWchar
utf16_native_encode :: CodeBuffer CharBufElem CWchar
utf16_native_encode Buffer CharBufElem
ibuf Buffer CWchar
obuf = do
  (why, ibuf, obuf) <- CodeBuffer CharBufElem Word8
utf16_native_encode' Buffer CharBufElem
ibuf (Buffer CWchar -> Buffer Word8
byteView Buffer CWchar
obuf)
  return (why, ibuf, cwcharView obuf)

utf16_native_decode :: CodeBuffer CWchar Char
utf16_native_decode :: CodeBuffer CWchar CharBufElem
utf16_native_decode Buffer CWchar
ibuf Buffer CharBufElem
obuf = do
  (why, ibuf, obuf) <- CodeBuffer Word8 CharBufElem
utf16_native_decode' (Buffer CWchar -> Buffer Word8
byteView Buffer CWchar
ibuf) Buffer CharBufElem
obuf
  return (why, cwcharView ibuf, obuf)

cpDecode :: Word32 -> Int -> DecodeBuffer
cpDecode :: Word32 -> Int -> CodeBuffer Word8 CharBufElem
cpDecode Word32
cp Int
max_char_size = \Buffer Word8
ibuf Buffer CharBufElem
obuf -> do
#if defined(CHARBUF_UTF16)
    let mbuf = obuf
#else
    -- FIXME: share the buffer between runs, even if the buffer is not the perfect size
    let sz :: Int
sz =       (Buffer Word8 -> Int
forall e. Buffer e -> Int
bufferElems Buffer Word8
ibuf Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
2)     -- I guess in the worst case the input CP text consists of 1-byte sequences that map entirely to things outside the BMP and so require 2 UTF-16 chars
             Int -> Int -> Int
forall a. Ord a => a -> a -> a
`min` (Buffer CharBufElem -> Int
forall e. Buffer e -> Int
bufferAvailable Buffer CharBufElem
obuf Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
2) -- In the best case, each pair of UTF-16 points becomes a single UTF-32 point
    mbuf <- Int -> Int -> BufferState -> IO (Buffer CWchar)
forall e. Int -> Int -> BufferState -> IO (Buffer e)
newBuffer (Int
2 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
sz) Int
sz BufferState
WriteBuffer :: IO (Buffer CWchar)
#endif
    debugIO $ "cpDecode " ++ summaryBuffer ibuf ++ " " ++ summaryBuffer mbuf
    (why1, ibuf', mbuf') <- cpRecode try' is_valid_prefix max_char_size 1 0 1 ibuf mbuf
    debugIO $ "cpRecode (cpDecode) = " ++ show why1 ++ " " ++ summaryBuffer ibuf' ++ " " ++ summaryBuffer mbuf'
#if defined(CHARBUF_UTF16)
    return (why1, ibuf', mbuf')
#else
    -- Convert as much UTF-16 as possible to UTF-32. Note that it's impossible for this to fail
    -- due to illegal characters since the output from Window's encoding function should be correct UTF-16.
    -- However, it's perfectly possible to run out of either output or input buffer.
    debugIO $ "utf16_native_decode " ++ summaryBuffer mbuf' ++ " " ++ summaryBuffer obuf
    (why2, target_utf16_count, mbuf', obuf) <- saner utf16_native_decode (mbuf' { bufState = ReadBuffer }) obuf
    debugIO $ "utf16_native_decode = " ++ show why2 ++ " " ++ summaryBuffer mbuf' ++ " " ++ summaryBuffer obuf
    case why2 of
      -- If we successfully translate all of the UTF-16 buffer, we need to know why we couldn't get any more
      -- UTF-16 out of the Windows API
      CodingProgress
InputUnderflow | Buffer CWchar -> Bool
forall e. Buffer e -> Bool
isEmptyBuffer Buffer CWchar
mbuf' -> (CodingProgress, Buffer Word8, Buffer CharBufElem)
-> IO (CodingProgress, Buffer Word8, Buffer CharBufElem)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (CodingProgress
why1, Buffer Word8
ibuf', Buffer CharBufElem
obuf)
                     | Bool
otherwise           -> String -> IO (CodingProgress, Buffer Word8, Buffer CharBufElem)
forall a. String -> a
errorWithoutStackTrace String
"cpDecode: impossible underflown UTF-16 buffer"
      -- InvalidSequence should be impossible since mbuf' is output from Windows.
      CodingProgress
InvalidSequence -> String -> IO (CodingProgress, Buffer Word8, Buffer CharBufElem)
forall a. String -> a
errorWithoutStackTrace String
"InvalidSequence on output of Windows API"
      -- If we run out of space in obuf, we need to ask for more output buffer space, while also returning
      -- the characters we have managed to consume so far.
      CodingProgress
OutputUnderflow -> do
        -- We have an interesting problem here similar to the cpEncode case where we have to figure out how much
        -- of the byte buffer was consumed to reach as far as the last UTF-16 character we actually decoded to UTF-32 OK.
        --
        -- The minimum number of bytes it could take is half the number of UTF-16 chars we got on the output, since
        -- one byte could theoretically generate two UTF-16 characters.
        -- The common case (ASCII text) is that every byte in the input maps to a single UTF-16 character.
        -- In the worst case max_char_size bytes map to each UTF-16 character.
        byte_count <- String
-> CodeBuffer Word8 CWchar
-> Buffer Word8
-> Buffer CWchar
-> Int
-> Int
-> Int
-> Int
-> IO Int
forall from to.
String
-> CodeBuffer from to
-> Buffer from
-> Buffer to
-> Int
-> Int
-> Int
-> Int
-> IO Int
bSearch String
"cpDecode" ((Ptr Word8 -> Int -> Ptr CWchar -> Int -> IO (Either Bool Int))
-> (Word8 -> IO Bool)
-> Int
-> Int
-> Int
-> Int
-> CodeBuffer Word8 CWchar
forall from to.
Storable from =>
(Ptr from -> Int -> Ptr to -> Int -> IO (Either Bool Int))
-> (from -> IO Bool)
-> Int
-> Int
-> Int
-> Int
-> CodeBuffer from to
cpRecode Ptr Word8 -> Int -> Ptr CWchar -> Int -> IO (Either Bool Int)
forall {a} {a} {b}.
(Integral a, Integral a, Show a, Show a, Num b) =>
Ptr Word8 -> a -> Ptr CWchar -> a -> IO (Either Bool b)
try' Word8 -> IO Bool
is_valid_prefix Int
max_char_size Int
1 Int
0 Int
1) Buffer Word8
ibuf Buffer CWchar
mbuf Int
target_utf16_count (Int
target_utf16_count Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
2) Int
target_utf16_count (Int
target_utf16_count Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
max_char_size)
        return (OutputUnderflow, bufferRemove byte_count ibuf, obuf)
#endif
  where
    is_valid_prefix :: Word8 -> IO Bool
is_valid_prefix = Word32 -> Word8 -> IO Bool
c_IsDBCSLeadByteEx Word32
cp
    try' :: Ptr Word8 -> a -> Ptr CWchar -> a -> IO (Either Bool b)
try' Ptr Word8
iptr a
icnt Ptr CWchar
optr a
ocnt
     -- MultiByteToWideChar does surprising things if you have ocnt == 0
     | a
ocnt a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
0 = Either Bool b -> IO (Either Bool b)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Either Bool b
forall a b. a -> Either a b
Left Bool
True)
     | Bool
otherwise = do
        err <- Word32
-> Word32 -> Ptr Word8 -> CInt -> Ptr CWchar -> CInt -> IO CInt
c_MultiByteToWideChar (Word32 -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word32
cp) Word32
8 -- MB_ERR_INVALID_CHARS == 8: Fail if an invalid input character is encountered
                                     Ptr Word8
iptr (a -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral a
icnt) Ptr CWchar
optr (a -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral a
ocnt)
        debugIO $ "MultiByteToWideChar " ++ show cp ++ " 8 " ++ show iptr ++ " " ++ show icnt ++ " " ++ show optr ++ " " ++ show ocnt ++ "\n = " ++ show err
        case err of
          -- 0 indicates that we did not succeed
          CInt
0 -> do
            err <- IO Word32
getLastError
            case err of
                Word32
122  -> Either Bool b -> IO (Either Bool b)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Either Bool b
forall a b. a -> Either a b
Left Bool
True)
                Word32
1113 -> Either Bool b -> IO (Either Bool b)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Either Bool b
forall a b. a -> Either a b
Left Bool
False)
                Word32
_    -> String -> Word32 -> IO (Either Bool b)
forall a. String -> Word32 -> IO a
failWith String
"MultiByteToWideChar" Word32
err
          CInt
wrote_chars -> Either Bool b -> IO (Either Bool b)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (b -> Either Bool b
forall a b. b -> Either a b
Right (CInt -> b
forall a b. (Integral a, Num b) => a -> b
fromIntegral CInt
wrote_chars))

cpEncode :: Word32 -> Int -> EncodeBuffer
cpEncode :: Word32 -> Int -> CodeBuffer CharBufElem Word8
cpEncode Word32
cp Int
_max_char_size = \Buffer CharBufElem
ibuf Buffer Word8
obuf -> do
#if defined(CHARBUF_UTF16)
    let mbuf' = ibuf
#else
    -- FIXME: share the buffer between runs, even though that means we can't size the buffer as we want.
    let sz :: Int
sz =       (Buffer CharBufElem -> Int
forall e. Buffer e -> Int
bufferElems Buffer CharBufElem
ibuf Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
2)     -- UTF-32 always uses 4 bytes. UTF-16 uses at most 4 bytes.
             Int -> Int -> Int
forall a. Ord a => a -> a -> a
`min` (Buffer Word8 -> Int
forall e. Buffer e -> Int
bufferAvailable Buffer Word8
obuf Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
2) -- In the best case, each pair of UTF-16 points fits into only 1 byte
    mbuf <- Int -> Int -> BufferState -> IO (Buffer CWchar)
forall e. Int -> Int -> BufferState -> IO (Buffer e)
newBuffer (Int
2 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
sz) Int
sz BufferState
WriteBuffer

    -- Convert as much UTF-32 as possible to UTF-16. NB: this can't fail due to output underflow
    -- since we sized the output buffer correctly. However, it could fail due to an illegal character
    -- in the input if it encounters a lone surrogate. In this case, our recovery will be applied as normal.
    (why1, ibuf', mbuf') <- utf16_native_encode ibuf mbuf
#endif
    debugIO $ "\ncpEncode " ++ summaryBuffer mbuf' ++ " " ++ summaryBuffer obuf
    (why2, target_utf16_count, mbuf', obuf) <- saner (cpRecode try' is_valid_prefix 2 1 1 0) (mbuf' { bufState = ReadBuffer }) obuf
    debugIO $ "cpRecode (cpEncode) = " ++ show why2 ++ " " ++ summaryBuffer mbuf' ++ " " ++ summaryBuffer obuf
#if defined(CHARBUF_UTF16)
    return (why2, mbuf', obuf)
#else
    case why2 of
      -- If we successfully translate all of the UTF-16 buffer, we need to know why
      -- we weren't able to get any more UTF-16 out of the UTF-32 buffer
      CodingProgress
InputUnderflow | Buffer CWchar -> Bool
forall e. Buffer e -> Bool
isEmptyBuffer Buffer CWchar
mbuf' -> (CodingProgress, Buffer CharBufElem, Buffer Word8)
-> IO (CodingProgress, Buffer CharBufElem, Buffer Word8)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (CodingProgress
why1, Buffer CharBufElem
ibuf', Buffer Word8
obuf)
                     | Bool
otherwise           -> String -> IO (CodingProgress, Buffer CharBufElem, Buffer Word8)
forall a. String -> a
errorWithoutStackTrace String
"cpEncode: impossible underflown UTF-16 buffer"
      -- With OutputUnderflow/InvalidSequence we only care about the failings of the UTF-16->CP translation.
      -- Yes, InvalidSequence is possible even though mbuf' is guaranteed to be valid UTF-16, because
      -- the code page may not be able to represent the encoded Unicode codepoint.
      CodingProgress
_ -> do
        -- Here is an interesting problem. If we have only managed to translate part of the mbuf'
        -- then we need to return an ibuf which has consumed exactly those bytes required to obtain
        -- that part of the mbuf'. To reconstruct this information, we binary search for the number of
        -- UTF-32 characters required to get the consumed count of UTF-16 characters:
        --
        -- When dealing with data from the BMP (the common case), consuming N UTF-16 characters will be the same as consuming N
        -- UTF-32 characters. We start our search there so that most binary searches will terminate in a single iteration.
        -- Furthermore, the absolute minimum number of UTF-32 characters this can correspond to is 1/2 the UTF-16 byte count
        -- (this will be realised when the input data is entirely not in the BMP).
        utf32_count <- String
-> CodeBuffer CharBufElem CWchar
-> Buffer CharBufElem
-> Buffer CWchar
-> Int
-> Int
-> Int
-> Int
-> IO Int
forall from to.
String
-> CodeBuffer from to
-> Buffer from
-> Buffer to
-> Int
-> Int
-> Int
-> Int
-> IO Int
bSearch String
"cpEncode" CodeBuffer CharBufElem CWchar
utf16_native_encode Buffer CharBufElem
ibuf Buffer CWchar
mbuf Int
target_utf16_count (Int
target_utf16_count Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
2) Int
target_utf16_count Int
target_utf16_count
        return (why2, bufferRemove utf32_count ibuf, obuf)
#endif
  where
    -- Single characters should be mappable to bytes. If they aren't supported by the CP then we have an invalid input sequence.
    is_valid_prefix :: p -> m Bool
is_valid_prefix p
_ = Bool -> m Bool
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False

    try' :: Ptr CWchar -> a -> Ptr Word8 -> a -> IO (Either Bool b)
try' Ptr CWchar
iptr a
icnt Ptr Word8
optr a
ocnt
     -- WideCharToMultiByte does surprising things if you call it with ocnt == 0
     | a
ocnt a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
0 = Either Bool b -> IO (Either Bool b)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Either Bool b
forall a b. a -> Either a b
Left Bool
True)
     | Bool
otherwise = (Ptr Bool -> IO (Either Bool b)) -> IO (Either Bool b)
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr Bool -> IO (Either Bool b)) -> IO (Either Bool b))
-> (Ptr Bool -> IO (Either Bool b)) -> IO (Either Bool b)
forall a b. (a -> b) -> a -> b
$ \Ptr Bool
defaulted_ptr -> do
      Ptr Bool -> Bool -> IO ()
forall a. Storable a => Ptr a -> a -> IO ()
poke Ptr Bool
defaulted_ptr Bool
False
      err <- Word32
-> Word32
-> Ptr CWchar
-> CInt
-> Ptr Word8
-> CInt
-> Ptr Word8
-> Ptr Bool
-> IO CInt
c_WideCharToMultiByte (Word32 -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word32
cp) Word32
0 -- NB: the WC_ERR_INVALID_CHARS flag is useless: only has an effect with the UTF-8 code page
                                   Ptr CWchar
iptr (a -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral a
icnt) Ptr Word8
optr (a -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral a
ocnt)
                                   Ptr Word8
forall a. Ptr a
nullPtr Ptr Bool
defaulted_ptr
      defaulted <- peek defaulted_ptr
      debugIO $ "WideCharToMultiByte " ++ show cp ++ " 0 " ++ show iptr ++ " " ++ show icnt ++ " " ++ show optr ++ " " ++ show ocnt ++ " NULL " ++ show defaulted_ptr ++ "\n = " ++ show err ++ ", " ++ show defaulted
      case err of
          -- 0 indicates that we did not succeed
          CInt
0 -> do
            err <- IO Word32
getLastError
            case err of
                Word32
122  -> Either Bool b -> IO (Either Bool b)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Either Bool b
forall a b. a -> Either a b
Left Bool
True)
                Word32
1113 -> Either Bool b -> IO (Either Bool b)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Either Bool b
forall a b. a -> Either a b
Left Bool
False)
                Word32
_    -> String -> Word32 -> IO (Either Bool b)
forall a. String -> Word32 -> IO a
failWith String
"WideCharToMultiByte" Word32
err
          CInt
wrote_bytes | Bool
defaulted -> Either Bool b -> IO (Either Bool b)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Either Bool b
forall a b. a -> Either a b
Left Bool
False)
                      | Bool
otherwise -> Either Bool b -> IO (Either Bool b)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (b -> Either Bool b
forall a b. b -> Either a b
Right (CInt -> b
forall a b. (Integral a, Num b) => a -> b
fromIntegral CInt
wrote_bytes))

bSearch :: String
        -> CodeBuffer from to
        -> Buffer from -> Buffer to -- From buffer (crucial data source) and to buffer (temporary storage only). To buffer must be empty (L=R).
        -> Int               -- Target size of to buffer
        -> Int -> Int -> Int -- Binary search min, mid, max
        -> IO Int            -- Size of from buffer required to reach target size of to buffer
bSearch :: forall from to.
String
-> CodeBuffer from to
-> Buffer from
-> Buffer to
-> Int
-> Int
-> Int
-> Int
-> IO Int
bSearch String
msg CodeBuffer from to
code Buffer from
ibuf Buffer to
mbuf Int
target_to_elems = Int -> Int -> Int -> IO Int
go
  where
    go :: Int -> Int -> Int -> IO Int
go Int
mn Int
md Int
mx = do
      -- NB: this loop repeatedly reencodes on top of mbuf using a varying fraction of ibuf. It doesn't
      -- matter if we blast the contents of mbuf since we already consumed all of the contents we are going to use.
      (_why, ibuf, mbuf) <- CodeBuffer from to
code (Buffer from
ibuf { bufR = bufL ibuf + md }) Buffer to
mbuf
      debugIO $ "code (bSearch " ++ msg ++ ") " ++ show md ++ " = " ++ show _why ++ ", " ++ summaryBuffer ibuf ++ summaryBuffer mbuf
      -- The normal case is to get InputUnderflow here, which indicates that coding basically
      -- terminated normally.
      --
      -- However, InvalidSequence is also possible if we are being called from cpDecode if we
      -- have just been unlucky enough to set md so that ibuf straddles a byte boundary.
      -- In this case we have to be really careful, because we don't want to report that
      -- "md" elements is the right number when in actual fact we could have had md-1 input
      -- elements and still produced the same number of bufferElems in mbuf.
      --
      -- In fact, we have to worry about this possibility even if we get InputUnderflow
      -- since that will report InputUnderflow rather than InvalidSequence if the buffer
      -- ends in a valid lead byte. So the expedient thing to do is simply to check if
      -- the input buffer was entirely consumed.
      --
      -- When called from cpDecode, OutputUnderflow is also possible.
      --
      -- Luckily if we have InvalidSequence/OutputUnderflow and we do not appear to have reached
      -- the target, what we should do is the same as normal because the fraction of ibuf that our
      -- first "code" coded successfully must be invalid-sequence-free, and ibuf will always
      -- have been decoded as far as the first invalid sequence in it.
      case bufferElems mbuf `compare` target_to_elems of
        -- Coding n "from" chars from the input yields exactly as many "to" chars
        -- as were consumed by the recode. All is peachy:
        Ordering
EQ -> String -> IO ()
debugIO (String
"bSearch = " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
solution) IO () -> IO Int -> IO Int
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Int -> IO Int
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Int
solution
          where solution :: Int
solution = Int
md Int -> Int -> Int
forall a. Num a => a -> a -> a
- Buffer from -> Int
forall e. Buffer e -> Int
bufferElems Buffer from
ibuf
        -- If we encoded fewer "to" characters than the target number, try again with more "from" characters (and vice-versa)
        Ordering
LT -> Int -> Int -> IO Int
go' (Int
mdInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1) Int
mx
        Ordering
GT -> Int -> Int -> IO Int
go' Int
mn (Int
mdInt -> Int -> Int
forall a. Num a => a -> a -> a
-Int
1)
    go' :: Int -> Int -> IO Int
go' Int
mn Int
mx | Int
mn Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
mx  = Int -> Int -> Int -> IO Int
go Int
mn (Int
mn Int -> Int -> Int
forall a. Num a => a -> a -> a
+ ((Int
mx Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
mn) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
2)) Int
mx
              | Bool
otherwise = String -> IO Int
forall a. String -> a
errorWithoutStackTrace (String -> IO Int) -> String -> IO Int
forall a b. (a -> b) -> a -> b
$ String
"bSearch(" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
msg String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"): search crossed! " String -> String -> String
forall a. [a] -> [a] -> [a]
++ (String, String, Int, Int, Int) -> String
forall a. Show a => a -> String
show (Buffer from -> String
forall a. Buffer a -> String
summaryBuffer Buffer from
ibuf, Buffer to -> String
forall a. Buffer a -> String
summaryBuffer Buffer to
mbuf, Int
target_to_elems, Int
mn, Int
mx)

cpRecode :: forall from to. Storable from
         => (Ptr from -> Int -> Ptr to -> Int -> IO (Either Bool Int))
         -> (from -> IO Bool)
         -> Int -- ^ Maximum length of a complete translatable sequence in the input (e.g. 2 if the input is UTF-16, 1 if the input is a SBCS, 2 is the input is a DBCS). Must be at least 1.
         -> Int -- ^ Minimum number of output elements per complete translatable sequence in the input (almost certainly 1)
         -> Int -> Int
         -> CodeBuffer from to
cpRecode :: forall from to.
Storable from =>
(Ptr from -> Int -> Ptr to -> Int -> IO (Either Bool Int))
-> (from -> IO Bool)
-> Int
-> Int
-> Int
-> Int
-> CodeBuffer from to
cpRecode Ptr from -> Int -> Ptr to -> Int -> IO (Either Bool Int)
try' from -> IO Bool
is_valid_prefix Int
max_i_size Int
min_o_size Int
iscale Int
oscale = CodeBuffer from to
go
  where
    go :: CodeBuffer from to
    go :: CodeBuffer from to
go Buffer from
ibuf Buffer to
obuf | Buffer from -> Bool
forall e. Buffer e -> Bool
isEmptyBuffer Buffer from
ibuf                = (CodingProgress, Buffer from, Buffer to)
-> IO (CodingProgress, Buffer from, Buffer to)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (CodingProgress
InputUnderflow,  Buffer from
ibuf, Buffer to
obuf)
                 | Buffer to -> Int
forall e. Buffer e -> Int
bufferAvailable Buffer to
obuf Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
min_o_size = (CodingProgress, Buffer from, Buffer to)
-> IO (CodingProgress, Buffer from, Buffer to)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (CodingProgress
OutputUnderflow, Buffer from
ibuf, Buffer to
obuf)
                 | Bool
otherwise                         = Int
-> (Int -> Bool -> IO (CodingProgress, Buffer from, Buffer to))
-> IO (CodingProgress, Buffer from, Buffer to)
try (Buffer from -> Int
forall e. Buffer e -> Int
bufferElems Buffer from
ibuf Int -> Int -> Int
forall a. Ord a => a -> a -> a
`min` ((Int
max_i_size Int -> Int -> Int
forall a. Num a => a -> a -> a
* Buffer to -> Int
forall e. Buffer e -> Int
bufferAvailable Buffer to
obuf) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
min_o_size)) Int -> Bool -> IO (CodingProgress, Buffer from, Buffer to)
seek_smaller
      where
        done :: a -> m (a, Buffer from, Buffer to)
done a
why = (a, Buffer from, Buffer to) -> m (a, Buffer from, Buffer to)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (a
why, Buffer from
ibuf, Buffer to
obuf)

        seek_smaller :: Int -> Bool -> IO (CodingProgress, Buffer from, Buffer to)
seek_smaller Int
n Bool
longer_was_valid
          -- In this case, we can't shrink any further via any method. Calling (try 0) wouldn't be right because that will always claim InputUnderflow...
          | Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
1 = if Bool
longer_was_valid
                      -- try m (where m >= n) was valid but we overflowed the output buffer with even a single input element
                      then CodingProgress -> IO (CodingProgress, Buffer from, Buffer to)
forall {m :: * -> *} {a}.
Monad m =>
a -> m (a, Buffer from, Buffer to)
done CodingProgress
OutputUnderflow
                      -- there was no initial valid sequence in the input, but it might just be a truncated buffer - we need to check
                      else do byte <- Buffer from -> (Ptr from -> IO from) -> IO from
forall e a. Buffer e -> (Ptr e -> IO a) -> IO a
withBuffer Buffer from
ibuf ((Ptr from -> IO from) -> IO from)
-> (Ptr from -> IO from) -> IO from
forall a b. (a -> b) -> a -> b
$ \Ptr from
ptr -> Ptr from -> Int -> IO from
forall a. Storable a => Ptr a -> Int -> IO a
peekElemOff Ptr from
ptr (Buffer from -> Int
forall e. Buffer e -> Int
bufL Buffer from
ibuf)
                              valid_prefix <- is_valid_prefix byte
                              done (if valid_prefix && bufferElems ibuf < max_i_size then InputUnderflow else InvalidSequence)
          -- If we're already looking at very small buffers, try every n down to 1, to ensure we spot as long a sequence as exists while avoiding trying 0.
          -- Doing it this way ensures that we spot a single initial sequence of length <= max_i_size if any such exists.
          | Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
2 Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
max_i_size = Int
-> (Int -> Bool -> IO (CodingProgress, Buffer from, Buffer to))
-> IO (CodingProgress, Buffer from, Buffer to)
try (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) (\Int
pred_n Bool
pred_n_was_valid -> Int -> Bool -> IO (CodingProgress, Buffer from, Buffer to)
seek_smaller Int
pred_n (Bool
longer_was_valid Bool -> Bool -> Bool
|| Bool
pred_n_was_valid))
          -- Otherwise, try a binary chop to try to either get the prefix before the invalid input, or shrink the output down so it fits
          -- in the output buffer. After the chop, try to consume extra input elements to try to recover as much of the sequence as possible if we
          -- end up chopping a multi-element input sequence into two parts.
          --
          -- Note that since max_i_size >= 1:
          --  * (n `div` 2) >= 1, so we don't try 0
          --  * ((n `div` 2) + (max_i_size - 1)) < n, so we don't get into a loop where (seek_smaller n) calls post_divide (n `div` 2) calls (seek_smaller n)
          | let n' :: Int
n' = Int
n Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
2 = Int
-> (Int -> Bool -> IO (CodingProgress, Buffer from, Buffer to))
-> IO (CodingProgress, Buffer from, Buffer to)
try Int
n' (Int
-> Bool
-> Int
-> Bool
-> IO (CodingProgress, Buffer from, Buffer to)
post_divide Int
n' Bool
longer_was_valid)

        post_divide :: Int
-> Bool
-> Int
-> Bool
-> IO (CodingProgress, Buffer from, Buffer to)
post_divide Int
_  Bool
_                Int
n Bool
True  = Int -> Bool -> IO (CodingProgress, Buffer from, Buffer to)
seek_smaller Int
n Bool
True
        post_divide Int
n' Bool
longer_was_valid Int
n Bool
False | Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
n' Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
max_i_size Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1 = Int
-> (Int -> Bool -> IO (CodingProgress, Buffer from, Buffer to))
-> IO (CodingProgress, Buffer from, Buffer to)
try (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) (Int
-> Bool
-> Int
-> Bool
-> IO (CodingProgress, Buffer from, Buffer to)
post_divide Int
n' Bool
longer_was_valid) -- There's still a chance..
                                                | Bool
otherwise               = Int -> Bool -> IO (CodingProgress, Buffer from, Buffer to)
seek_smaller Int
n' Bool
longer_was_valid              -- No amount of recovery could save us :(

        try :: Int
-> (Int -> Bool -> IO (CodingProgress, Buffer from, Buffer to))
-> IO (CodingProgress, Buffer from, Buffer to)
try Int
n Int -> Bool -> IO (CodingProgress, Buffer from, Buffer to)
k_fail = Buffer from
-> (Ptr from -> IO (CodingProgress, Buffer from, Buffer to))
-> IO (CodingProgress, Buffer from, Buffer to)
forall e a. Buffer e -> (Ptr e -> IO a) -> IO a
withBuffer Buffer from
ibuf ((Ptr from -> IO (CodingProgress, Buffer from, Buffer to))
 -> IO (CodingProgress, Buffer from, Buffer to))
-> (Ptr from -> IO (CodingProgress, Buffer from, Buffer to))
-> IO (CodingProgress, Buffer from, Buffer to)
forall a b. (a -> b) -> a -> b
$ \Ptr from
iptr -> Buffer to
-> (Ptr to -> IO (CodingProgress, Buffer from, Buffer to))
-> IO (CodingProgress, Buffer from, Buffer to)
forall e a. Buffer e -> (Ptr e -> IO a) -> IO a
withBuffer Buffer to
obuf ((Ptr to -> IO (CodingProgress, Buffer from, Buffer to))
 -> IO (CodingProgress, Buffer from, Buffer to))
-> (Ptr to -> IO (CodingProgress, Buffer from, Buffer to))
-> IO (CodingProgress, Buffer from, Buffer to)
forall a b. (a -> b) -> a -> b
$ \Ptr to
optr -> do
          ei_err_wrote <- Ptr from -> Int -> Ptr to -> Int -> IO (Either Bool Int)
try' (Ptr from
iptr Ptr from -> Int -> Ptr from
forall a b. Ptr a -> Int -> Ptr b
`plusPtr` (Buffer from -> Int
forall e. Buffer e -> Int
bufL Buffer from
ibuf Int -> Int -> Int
forall a. Bits a => a -> Int -> a
`shiftL` Int
iscale)) Int
n
                               (Ptr to
optr Ptr to -> Int -> Ptr to
forall a b. Ptr a -> Int -> Ptr b
`plusPtr` (Buffer to -> Int
forall e. Buffer e -> Int
bufR Buffer to
obuf Int -> Int -> Int
forall a. Bits a => a -> Int -> a
`shiftL` Int
oscale)) (Buffer to -> Int
forall e. Buffer e -> Int
bufferAvailable Buffer to
obuf)
          debugIO $ "try " ++ show n ++ " = " ++ show ei_err_wrote
          case ei_err_wrote of
            -- ERROR_INSUFFICIENT_BUFFER: A supplied buffer size was not large enough, or it was incorrectly set to NULL.
            Left Bool
True  -> Int -> Bool -> IO (CodingProgress, Buffer from, Buffer to)
k_fail Int
n Bool
True
            -- ERROR_NO_UNICODE_TRANSLATION: Invalid Unicode was found in a string.
            Left Bool
False -> Int -> Bool -> IO (CodingProgress, Buffer from, Buffer to)
k_fail Int
n Bool
False
            -- Must have interpreted all given bytes successfully
            -- We need to iterate until we have consumed the complete contents of the buffer
            Right Int
wrote_elts -> CodeBuffer from to
go (Int -> Buffer from -> Buffer from
forall e. Int -> Buffer e -> Buffer e
bufferRemove Int
n Buffer from
ibuf) (Buffer to
obuf { bufR = bufR obuf + wrote_elts })

#endif