Eben.hs 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. {-# LANGUAGE OverloadedStrings #-}
  2. {-# LANGUAGE ViewPatterns #-}
  3. module Data.Eben where
  4. import qualified Data.ByteString as B
  5. import Data.ByteString.Lazy (ByteString)
  6. import qualified Data.ByteString.Lazy as BS
  7. import Data.ByteString.Builder (Builder)
  8. import qualified Data.ByteString.Builder as BL
  9. import Data.Int (Int64)
  10. import Data.List (sortOn)
  11. import Data.Map.Strict (Map)
  12. import qualified Data.Map as M
  13. import Data.Monoid ((<>))
  14. import Data.Word (Word8)
  15. data Value
  16. = List [Value]
  17. | Dict (Map B.ByteString Value)
  18. | Integer Int64
  19. | Float Float
  20. | String B.ByteString
  21. deriving (Eq, Show, Read)
  22. decode :: ByteString -> Maybe (Value, ByteString)
  23. decode bs = go
  24. where go = case BS.uncons bs of
  25. Just (108, rs) -> decodeList
  26. Just (100, rs) -> decodeDict
  27. Just (105, rs) -> decodeInt
  28. Just (102, rs) -> decodeFloat
  29. Just (i , rs)
  30. | isDigit (fromIntegral i) ->
  31. let (is, rs') = BS.break (== 58) rs
  32. len = toNum (toDigit i) is
  33. (str, rs'') = BS.splitAt len (BS.tail rs')
  34. in Just (String (BS.toStrict str), rs'')
  35. | otherwise -> Nothing
  36. isDigit :: Word8 -> Bool
  37. isDigit n = n >= 48 && n <= 57
  38. toDigit :: Word8 -> Int64
  39. toDigit n = fromIntegral n - 48
  40. toNum :: Int64 -> ByteString -> Int64
  41. toNum n (BS.uncons->Just(b, bs)) =
  42. toNum (n * 10 + fromIntegral b) bs
  43. toNum n _ = n
  44. encode :: Value -> ByteString
  45. encode = BL.toLazyByteString . go
  46. where go (List vs) =
  47. BL.char7 'l' <> foldMap go vs <> BL.char7 'e'
  48. go (Dict vs) =
  49. BL.char7 'd'
  50. <> mconcat [ str k <> go v | (k, v) <- sortOn fst (M.toList vs) ]
  51. <> BL.char7 'e'
  52. go (Integer i) =
  53. BL.char7 'i' <> BL.string8 (show i) <> BL.char7 'e'
  54. go (Float f) =
  55. BL.char7 'f' <> BL.floatLE f <> BL.char7 'e'
  56. go (String bs) = str bs
  57. str bs =
  58. BL.intDec (B.length bs)
  59. <> BL.char7 ':'
  60. <> BL.byteString bs