{-# LANGUAGE CPP #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE BangPatterns #-}

-- ---------------------------------------------------------------------------
-- |
-- Module      : Text.Show.ByteString.Int
-- Copyright   : (c) 2008 Dan Doel
-- Maintainer  : Dan Doel
-- Stability   : Experimental
-- Portability : Non-portable (magic hash)
--
-- Putting integers and words.
--
-- The code in this module is based on the printing in the GHC modules.

#include "MachDeps.h"

module Text.Show.ByteString.Int where

import GHC.Base
import GHC.Int
import GHC.Word

import Data.Binary

import Text.Show.ByteString.Util

putI :: Int# -> Put
putI i#
#if __GLASGOW_HASKELL__ && __GLASGOW_HASKELL__ >= 708
  = case i# <# 0# of
      1#       -> let !(I# minInt#) = minInt
#elif __GLASGOW_HASKELL__ && __GLASGOW_HASKELL__ >= 611
  | i# <# 0#  = let !(I# minInt#) = minInt
#else
  | i# <# 0#  = let I# minInt# = minInt
#endif
                in case i# ==# minInt# of
#if __GLASGOW_HASKELL__ && __GLASGOW_HASKELL__ >= 708
                     1# ->
#else
                     True ->
#endif
                        putWord8 45 >> putW (int2Word# (negateInt# (i# `quotInt#` 10#)))
                                    >> putW (int2Word# (negateInt# (i# `remInt#` 10#)))
                     _ ->
                        putWord8 45 >> putW (int2Word# (negateInt# i#))
#if __GLASGOW_HASKELL__ && __GLASGOW_HASKELL__ >= 708
      _        -> putW (int2Word# i#)
#else
  | otherwise = putW (int2Word# i#)
#endif

putW :: Word# -> Put
putW w#
#if __GLASGOW_HASKELL__ && __GLASGOW_HASKELL__ >= 708
  = case w# `ltWord#` int2Word# 10# of
      1# ->
#else
  | w# `ltWord#` int2Word# 10# =
#endif
        unsafePutDigit# w#
#if __GLASGOW_HASKELL__ && __GLASGOW_HASKELL__ >= 708
      _  ->
#else
  | otherwise =
#endif
        putW (w# `quotWord#` int2Word# 10#)
        >> unsafePutDigit# (w# `remWord#` int2Word# 10#)

showpInt :: Int -> Put
showpInt (I# i#) = putI i#

showpInt8 :: Int8 -> Put
showpInt8 (I8# i#) = putI i#

showpInt16 :: Int16 -> Put
showpInt16 (I16# i#) = putI i#

showpInt32 :: Int32 -> Put
showpInt32 (I32# i#) = putI i#

showpInt64 :: Int64 -> Put
#if WORD_SIZE_IN_BITS >= 64
showpInt64 (I64# i#) = putI i#
#else /* WORD_SIZE_IN_BITS < 64 */
showpInt64 = putI64

-- Unboxed 64-bit-specific operations aren't exported

putI64 :: Int64 -> Put
putI64 i | i == minBound = putWord8 45
                           >> putW64 (fromIntegral $ negate (i `quot` 10))
                           >> putW64 (fromIntegral $ negate (i `rem` 10))
         | i < 0         = putWord8 45 >> putW64 (fromIntegral $ negate i)
         | otherwise     = putW64 (fromIntegral i)
#endif

showpWord :: Word -> Put
showpWord (W# w#) = putW w#

showpWord8 :: Word8 -> Put
showpWord8 (W8# w#) = putW w#

showpWord16 :: Word16 -> Put
showpWord16 (W16# w#) = putW w#

showpWord32 :: Word32 -> Put
showpWord32 (W32# w#) = putW w#

showpWord64 :: Word64 -> Put
#if WORD_SIZE_IN_BITS >= 64
showpWord64 (W64# w#) = putW w#
#else /* WORD_SIZE_IN_BITS < 64 */
showpWord64 = putW64

putW64 :: Word64 -> Put
putW64 w | w < 10    = unsafePutDigit64 w
         | otherwise = putW64 (w `quot` 10)
                       >> unsafePutDigit64 (w `rem` 10)
    where unsafePutDigit64 w = unsafePutDigit# (case fromIntegral w of (W# w#) -> w#)

#endif
