    {-# LANGUAGE MagicHash #-}

    -- ---------------------------------------------------------------------------
    -- |
    -- Module      : Text.Show.ByteString.Util
    -- Copyright   : (c) 2008 Dan Doel
    -- Maintainer  : Dan Doel
    -- Stability   : Experimental
    -- Portability : Non-portable (magic hash, GHC libraries)
    --
    -- Utility functions for producing readable byte strings.

    module Text.Show.ByteString.Util ( putAscii
                                     , putUTF8
                                     , putAsciiStr
                                     , putUTF8Str
                                     , unsafePutDigit
                                     , unsafePutDigit#
                                     ) where

    import GHC.Base
    import GHC.Word

    import Data.Binary
    import Data.Bits

    import Data.ByteString.Internal (c2w)

    -- | Writes a single Char to a byte string, assuming it's ascii.
    putAscii :: Char -> Put
    putAscii = putWord8 . c2w

    -- | Writes a single Char to a byte string, properly UTF-8 encoded
    putUTF8 :: Char -> Put
    putUTF8 c
      | oc <= 0x7f   = putWord8 (fromIntegral oc)
      | oc <= 0x7ff  = do putWord8 . fromIntegral $ 0xc0 + (oc `shiftR` 6)
                          putWord8 . fromIntegral $ 0x80 + oc .&. 0x3f
      | oc <= 0xffff = do putWord8 . fromIntegral $ 0xf0 + (oc `shiftR` 12)
                          putWord8 . fromIntegral $ 0x80 + ((oc `shiftR` 6) .&. 0x3f)
                          putWord8 . fromIntegral $ 0x80 + oc .&. 0x3f
      | otherwise    = do putWord8 . fromIntegral $ 0xf0 + (oc `shiftR` 18)
                          putWord8 . fromIntegral $ 0x80 + ((oc `shiftR` 12) .&. 0x3f)
                          putWord8 . fromIntegral $ 0x80 + ((oc `shiftR` 6) .&. 0x3f)
                          putWord8 . fromIntegral $ 0x80 + oc .&. 0x3f
     where
     oc = ord c

    -- | Writes a string of ascii characters to a byte string
    putAsciiStr :: String -> Put
    putAsciiStr = mapM_ putAscii

    -- | Writes a string of unicode characters to a byte string,
    -- properly UTF-8 encoded
    putUTF8Str :: String -> Put
    putUTF8Str = mapM_ putUTF8

    -- | Puts the decimal digit corresponding to the given Int without
    -- checking that it is in the interval [0,9]
    unsafePutDigit :: Int -> Put
    unsafePutDigit (I# i#) = unsafePutDigit# (int2Word# i#)

    unsafePutDigit# :: Word# -> Put
    unsafePutDigit# w# = putWord8 (W8# (w# `plusWord#` int2Word# 48#))