module Utility.DataUnits (
dataUnits,
storageUnits,
memoryUnits,
bandwidthUnits,
oldSchoolUnits,
Unit(..),
ByteSize,
roughSize,
roughSize',
compareSizes,
readSize
) where
import Data.List
import Data.Char
import Utility.HumanNumber
type ByteSize = Integer
type Name = String
type Abbrev = String
data Unit = Unit ByteSize Abbrev Name
deriving (Ord, Show, Eq)
dataUnits :: [Unit]
dataUnits = storageUnits ++ memoryUnits
storageUnits :: [Unit]
storageUnits =
[ Unit (p 8) "YB" "yottabyte"
, Unit (p 7) "ZB" "zettabyte"
, Unit (p 6) "EB" "exabyte"
, Unit (p 5) "PB" "petabyte"
, Unit (p 4) "TB" "terabyte"
, Unit (p 3) "GB" "gigabyte"
, Unit (p 2) "MB" "megabyte"
, Unit (p 1) "kB" "kilobyte"
, Unit (p 0) "B" "byte"
]
where
p :: Integer -> Integer
p n = 1000^n
memoryUnits :: [Unit]
memoryUnits =
[ Unit (p 8) "YiB" "yobibyte"
, Unit (p 7) "ZiB" "zebibyte"
, Unit (p 6) "EiB" "exbibyte"
, Unit (p 5) "PiB" "pebibyte"
, Unit (p 4) "TiB" "tebibyte"
, Unit (p 3) "GiB" "gibibyte"
, Unit (p 2) "MiB" "mebibyte"
, Unit (p 1) "KiB" "kibibyte"
, Unit (p 0) "B" "byte"
]
where
p :: Integer -> Integer
p n = 2^(n*10)
bandwidthUnits :: [Unit]
bandwidthUnits = error "stop trying to rip people off"
oldSchoolUnits :: [Unit]
oldSchoolUnits = zipWith (curry mingle) storageUnits memoryUnits
where
mingle (Unit _ a n, Unit s' _ _) = Unit s' a n
roughSize :: [Unit] -> Bool -> ByteSize -> String
roughSize units short i = roughSize' units short 2 i
roughSize' :: [Unit] -> Bool -> Int -> ByteSize -> String
roughSize' units short precision i
| i < 0 = '-' : findUnit units' (negate i)
| otherwise = findUnit units' i
where
units' = sortBy (flip compare) units
findUnit (u@(Unit s _ _):us) i'
| i' >= s = showUnit i' u
| otherwise = findUnit us i'
findUnit [] i' = showUnit i' (last units')
showUnit x (Unit size abbrev name) = s ++ " " ++ unit
where
v = (fromInteger x :: Double) / fromInteger size
s = showImprecise precision v
unit
| short = abbrev
| s == "1" = name
| otherwise = name ++ "s"
compareSizes :: [Unit] -> Bool -> ByteSize -> ByteSize -> String
compareSizes units abbrev old new
| old > new = roughSize units abbrev (old - new) ++ " smaller"
| old < new = roughSize units abbrev (new - old) ++ " larger"
| otherwise = "same"
readSize :: [Unit] -> String -> Maybe ByteSize
readSize units input
| null parsednum || null parsedunit = Nothing
| otherwise = Just $ round $ number * fromIntegral multiplier
where
(number, rest) = head parsednum
multiplier = head parsedunit
unitname = takeWhile isAlpha $ dropWhile isSpace rest
parsednum = reads input :: [(Double, String)]
parsedunit = lookupUnit units unitname
lookupUnit _ [] = [1]
lookupUnit [] _ = []
lookupUnit (Unit s a n:us) v
| a ~~ v || n ~~ v = [s]
| plural n ~~ v || a ~~ byteabbrev v = [s]
| otherwise = lookupUnit us v
a ~~ b = map toLower a == map toLower b
plural n = n ++ "s"
byteabbrev a = a ++ "b"