{-# LANGUAGE OverloadedStrings , ScopedTypeVariables , UnboxedTuples , UnicodeSyntax #-} -- 本當にこんなものを自分で書く必要があったのだらうか。Printf は重いの -- で駄目だが、それ以外のモジュールを探しても見付からなかった。 module Network.HTTP.Lucu.Format ( fmtInt , fmtDec , fmtHex ) where import qualified Blaze.ByteString.Builder.Char8 as BC import Data.Ascii (AsciiBuilder) import qualified Data.ByteString.Char8 as BS import qualified Data.Ascii as A import Data.Char import Data.Monoid.Unicode import Prelude.Unicode fmtInt ∷ ∀n. Integral n ⇒ n → Int → n → AsciiBuilder {-# INLINEABLE fmtInt #-} fmtInt base minWidth n = let (# raw, len #) = fmt' (abs n) (∅) 0 in if n < 0 then ( A.toAsciiBuilder "-" ⊕ mkPad (minWidth - 1) len ⊕ raw ) else mkPad minWidth len ⊕ raw where fmt' ∷ n → AsciiBuilder → Int → (# AsciiBuilder, Int #) {-# INLINEABLE fmt' #-} fmt' x b len | x < base = let b' = b ⊕ fromDigit x in (# b', len + 1 #) | otherwise = let x' = x `div` base y = x `mod` base b' = b ⊕ fromDigit y in fmt' x' b' (len + 1) mkPad ∷ Int → Int → AsciiBuilder {-# INLINEABLE mkPad #-} mkPad minWidth len = A.toAsciiBuilder $ A.unsafeFromByteString $ BS.replicate (minWidth - len) '0' fmtDec ∷ Integral n ⇒ Int → n → AsciiBuilder {-# INLINE fmtDec #-} fmtDec minWidth n | minWidth == 2 = fmtDec2 n -- optimization | minWidth == 3 = fmtDec3 n -- optimization | minWidth == 4 = fmtDec4 n -- optimization | otherwise = fmtInt 10 minWidth n fmtDec2 ∷ Integral n ⇒ n → AsciiBuilder {-# INLINEABLE fmtDec2 #-} fmtDec2 n | n < 0 ∨ n ≥ 100 = fmtInt 10 2 n -- fallback | n < 10 = A.toAsciiBuilder "0" ⊕ fromDigit n | otherwise = fromDigit (n `div` 10) ⊕ fromDigit (n `mod` 10) fmtDec3 ∷ Integral n ⇒ n → AsciiBuilder {-# INLINEABLE fmtDec3 #-} fmtDec3 n | n < 0 ∨ n ≥ 1000 = fmtInt 10 3 n -- fallback | n < 10 = A.toAsciiBuilder "00" ⊕ fromDigit n | n < 100 = A.toAsciiBuilder "0" ⊕ fromDigit ((n `div` 10) `mod` 10) ⊕ fromDigit ( n `mod` 10) | otherwise = fromDigit (n `div` 100) ⊕ fromDigit ((n `div` 10) `mod` 10) ⊕ fromDigit ( n `mod` 10) fmtDec4 ∷ Integral n ⇒ n → AsciiBuilder {-# INLINEABLE fmtDec4 #-} fmtDec4 n | n < 0 ∨ n ≥ 10000 = fmtInt 10 4 n -- fallback | n < 10 = A.toAsciiBuilder "000" ⊕ fromDigit n | n < 100 = A.toAsciiBuilder "00" ⊕ fromDigit ((n `div` 10) `mod` 10) ⊕ fromDigit ( n `mod` 10) | n < 1000 = A.toAsciiBuilder "0" ⊕ fromDigit ((n `div` 100) `mod` 10) ⊕ fromDigit ((n `div` 10) `mod` 10) ⊕ fromDigit ( n `mod` 10) | otherwise = fromDigit (n `div` 1000) ⊕ fromDigit ((n `div` 100) `mod` 10) ⊕ fromDigit ((n `div` 10) `mod` 10) ⊕ fromDigit ( n `mod` 10) fmtHex ∷ Integral n ⇒ Int → n → AsciiBuilder {-# INLINE fmtHex #-} fmtHex = fmtInt 16 digitToChar ∷ Integral n ⇒ n → Char {-# INLINE digitToChar #-} digitToChar n | n < 0 = (⊥) | n < 10 = chr (ord '0' + fromIntegral n ) | n < 16 = chr (ord 'A' + fromIntegral (n-10)) | otherwise = (⊥) fromDigit ∷ Integral n ⇒ n → AsciiBuilder {-# INLINE fromDigit #-} fromDigit = A.unsafeFromBuilder ∘ BC.fromChar ∘ digitToChar