packages feed

cborg 0.1.1.0 → 0.2.0.0

raw patch · 12 files changed

+1513/−239 lines, 12 filesdep ~basedep ~ghc-primdep ~halfPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: base, ghc-prim, half

API changes (from Hackage documentation)

+ Codec.CBOR.ByteArray: BA :: ByteArray -> ByteArray
+ Codec.CBOR.ByteArray: [unBA] :: ByteArray -> ByteArray
+ Codec.CBOR.ByteArray: fromByteString :: ByteString -> ByteArray
+ Codec.CBOR.ByteArray: fromShortByteString :: ShortByteString -> ByteArray
+ Codec.CBOR.ByteArray: instance Data.String.IsString Codec.CBOR.ByteArray.ByteArray
+ Codec.CBOR.ByteArray: instance GHC.Classes.Eq Codec.CBOR.ByteArray.ByteArray
+ Codec.CBOR.ByteArray: instance GHC.Classes.Ord Codec.CBOR.ByteArray.ByteArray
+ Codec.CBOR.ByteArray: instance GHC.Exts.IsList Codec.CBOR.ByteArray.ByteArray
+ Codec.CBOR.ByteArray: instance GHC.Show.Show Codec.CBOR.ByteArray.ByteArray
+ Codec.CBOR.ByteArray: newtype ByteArray
+ Codec.CBOR.ByteArray: sizeofByteArray :: ByteArray -> Int
+ Codec.CBOR.ByteArray: toBuilder :: ByteArray -> Builder
+ Codec.CBOR.ByteArray: toShortByteString :: ByteArray -> ShortByteString
+ Codec.CBOR.ByteArray: toSliced :: ByteArray -> SlicedByteArray
+ Codec.CBOR.ByteArray.Sliced: SBA :: !ByteArray -> !Int -> !Int -> SlicedByteArray
+ Codec.CBOR.ByteArray.Sliced: [length] :: SlicedByteArray -> !Int
+ Codec.CBOR.ByteArray.Sliced: [offset] :: SlicedByteArray -> !Int
+ Codec.CBOR.ByteArray.Sliced: [unSBA] :: SlicedByteArray -> !ByteArray
+ Codec.CBOR.ByteArray.Sliced: data SlicedByteArray
+ Codec.CBOR.ByteArray.Sliced: fromByteArray :: ByteArray -> SlicedByteArray
+ Codec.CBOR.ByteArray.Sliced: fromByteString :: ByteString -> SlicedByteArray
+ Codec.CBOR.ByteArray.Sliced: fromShortByteString :: ShortByteString -> SlicedByteArray
+ Codec.CBOR.ByteArray.Sliced: instance Data.String.IsString Codec.CBOR.ByteArray.Sliced.SlicedByteArray
+ Codec.CBOR.ByteArray.Sliced: instance GHC.Classes.Eq Codec.CBOR.ByteArray.Sliced.SlicedByteArray
+ Codec.CBOR.ByteArray.Sliced: instance GHC.Classes.Ord Codec.CBOR.ByteArray.Sliced.SlicedByteArray
+ Codec.CBOR.ByteArray.Sliced: instance GHC.Exts.IsList Codec.CBOR.ByteArray.Sliced.SlicedByteArray
+ Codec.CBOR.ByteArray.Sliced: instance GHC.Show.Show Codec.CBOR.ByteArray.Sliced.SlicedByteArray
+ Codec.CBOR.ByteArray.Sliced: sizeofSlicedByteArray :: SlicedByteArray -> Int
+ Codec.CBOR.ByteArray.Sliced: toBuilder :: SlicedByteArray -> Builder
+ Codec.CBOR.ByteArray.Sliced: toByteString :: SlicedByteArray -> ByteString
+ Codec.CBOR.Decoding: ConsumeByteArray :: (ByteArray -> ST s (DecodeAction s a)) -> DecodeAction s a
+ Codec.CBOR.Decoding: ConsumeDoubleCanonical :: (Double# -> ST s (DecodeAction s a)) -> DecodeAction s a
+ Codec.CBOR.Decoding: ConsumeFloat16Canonical :: (Float# -> ST s (DecodeAction s a)) -> DecodeAction s a
+ Codec.CBOR.Decoding: ConsumeFloatCanonical :: (Float# -> ST s (DecodeAction s a)) -> DecodeAction s a
+ Codec.CBOR.Decoding: ConsumeInt16Canonical :: (Int# -> ST s (DecodeAction s a)) -> DecodeAction s a
+ Codec.CBOR.Decoding: ConsumeInt32Canonical :: (Int# -> ST s (DecodeAction s a)) -> DecodeAction s a
+ Codec.CBOR.Decoding: ConsumeInt8Canonical :: (Int# -> ST s (DecodeAction s a)) -> DecodeAction s a
+ Codec.CBOR.Decoding: ConsumeIntCanonical :: (Int# -> ST s (DecodeAction s a)) -> DecodeAction s a
+ Codec.CBOR.Decoding: ConsumeIntegerCanonical :: (Integer -> ST s (DecodeAction s a)) -> DecodeAction s a
+ Codec.CBOR.Decoding: ConsumeListLenCanonical :: (Int# -> ST s (DecodeAction s a)) -> DecodeAction s a
+ Codec.CBOR.Decoding: ConsumeMapLenCanonical :: (Int# -> ST s (DecodeAction s a)) -> DecodeAction s a
+ Codec.CBOR.Decoding: ConsumeNegWordCanonical :: (Word# -> ST s (DecodeAction s a)) -> DecodeAction s a
+ Codec.CBOR.Decoding: ConsumeSimpleCanonical :: (Word# -> ST s (DecodeAction s a)) -> DecodeAction s a
+ Codec.CBOR.Decoding: ConsumeTagCanonical :: (Word# -> ST s (DecodeAction s a)) -> DecodeAction s a
+ Codec.CBOR.Decoding: ConsumeUtf8ByteArray :: (ByteArray -> ST s (DecodeAction s a)) -> DecodeAction s a
+ Codec.CBOR.Decoding: ConsumeWord16Canonical :: (Word# -> ST s (DecodeAction s a)) -> DecodeAction s a
+ Codec.CBOR.Decoding: ConsumeWord32Canonical :: (Word# -> ST s (DecodeAction s a)) -> DecodeAction s a
+ Codec.CBOR.Decoding: ConsumeWord8Canonical :: (Word# -> ST s (DecodeAction s a)) -> DecodeAction s a
+ Codec.CBOR.Decoding: ConsumeWordCanonical :: (Word# -> ST s (DecodeAction s a)) -> DecodeAction s a
+ Codec.CBOR.Decoding: decodeByteArray :: Decoder s ByteArray
+ Codec.CBOR.Decoding: decodeDoubleCanonical :: Decoder s Double
+ Codec.CBOR.Decoding: decodeFloat16Canonical :: Decoder s Float
+ Codec.CBOR.Decoding: decodeFloatCanonical :: Decoder s Float
+ Codec.CBOR.Decoding: decodeInt16Canonical :: Decoder s Int16
+ Codec.CBOR.Decoding: decodeInt32Canonical :: Decoder s Int32
+ Codec.CBOR.Decoding: decodeInt64Canonical :: Decoder s Int64
+ Codec.CBOR.Decoding: decodeInt8Canonical :: Decoder s Int8
+ Codec.CBOR.Decoding: decodeIntCanonical :: Decoder s Int
+ Codec.CBOR.Decoding: decodeIntegerCanonical :: Decoder s Integer
+ Codec.CBOR.Decoding: decodeListLenCanonical :: Decoder s Int
+ Codec.CBOR.Decoding: decodeListLenCanonicalOf :: Int -> Decoder s ()
+ Codec.CBOR.Decoding: decodeMapLenCanonical :: Decoder s Int
+ Codec.CBOR.Decoding: decodeNegWord64Canonical :: Decoder s Word64
+ Codec.CBOR.Decoding: decodeNegWordCanonical :: Decoder s Word
+ Codec.CBOR.Decoding: decodeSimpleCanonical :: Decoder s Word8
+ Codec.CBOR.Decoding: decodeTag64Canonical :: Decoder s Word64
+ Codec.CBOR.Decoding: decodeTagCanonical :: Decoder s Word
+ Codec.CBOR.Decoding: decodeUtf8ByteArray :: Decoder s ByteArray
+ Codec.CBOR.Decoding: decodeWord16Canonical :: Decoder s Word16
+ Codec.CBOR.Decoding: decodeWord32Canonical :: Decoder s Word32
+ Codec.CBOR.Decoding: decodeWord64Canonical :: Decoder s Word64
+ Codec.CBOR.Decoding: decodeWord8Canonical :: Decoder s Word8
+ Codec.CBOR.Decoding: decodeWordCanonical :: Decoder s Word
+ Codec.CBOR.Decoding: decodeWordCanonicalOf :: Word -> Decoder s ()
+ Codec.CBOR.Encoding: TkByteArray :: {-# UNPACK #-} !SlicedByteArray -> Tokens -> Tokens
+ Codec.CBOR.Encoding: TkUtf8ByteArray :: {-# UNPACK #-} !SlicedByteArray -> Tokens -> Tokens
+ Codec.CBOR.Encoding: encodeByteArray :: SlicedByteArray -> Encoding
+ Codec.CBOR.Encoding: encodeUtf8ByteArray :: SlicedByteArray -> Encoding
+ Codec.CBOR.Read: deserialiseFromBytesWithSize :: (forall s. Decoder s a) -> ByteString -> Either DeserialiseFailure (ByteString, ByteOffset, a)

Files

cborg.cabal view
@@ -1,5 +1,5 @@ name:                cborg-version:             0.1.1.0+version:             0.2.0.0 synopsis:            Concise Binary Object Representation license:             BSD3 license-file:        LICENSE.txt@@ -17,11 +17,15 @@ description:   This package (formerly @binary-serialise-cbor@) provides an efficient   implementation of the Concise Binary Object Representation (CBOR), as-  specified by RFC 7049.+  specified by [RFC 7049](https://tools.ietf.org/html/rfc7049).   .   If you are looking for a library for serialisation of Haskell values,-  have a look at the @serialise@ package, which is built upon this library.-+  have a look at the [serialise](/package/serialise) package, which is+  built upon this library.+  .+  An implementation of the standard bijection between CBOR and JSON is provided+  by the [cborg-json](/package/cborg-json) package. Also see [cbor-tool](/package/cbor-tool)+  for a convenient command-line utility for working with CBOR data. extra-source-files:   src/cbits/cbor.h @@ -56,6 +60,11 @@     Codec.CBOR.Read     Codec.CBOR.Write     Codec.CBOR.Term+    Codec.CBOR.ByteArray+    Codec.CBOR.ByteArray.Sliced++  other-modules:+    Codec.CBOR.ByteArray.Internal    other-extensions:     CPP, ForeignFunctionInterface, MagicHash,
src/Codec/CBOR.hs view
@@ -1,3 +1,5 @@+{-# OPTIONS_GHC -fno-warn-unused-imports #-}+ -- | -- Module      : Codec.CBOR -- Copyright   : (c) Duncan Coutts 2015-2017@@ -10,5 +12,102 @@ -- A library for working with CBOR. -- module Codec.CBOR- (+ ( -- $intro+   -- * Library Structure+   -- $structure  ) where++-- used by Haddocks+import Codec.CBOR.Decoding (Decoder)+import Codec.CBOR.Encoding (Encoding)+import Codec.CBOR.FlatTerm (FlatTerm)+import Codec.CBOR.Term     (Term, encodeTerm, decodeTerm)++{- $intro++The @cborg@ library is a low-level parsing and encoding library for the+Compact Binary Object Representation (CBOR) defined in RFC 7049. CBOR is a+language-agnostic, extensible, and size- and computation-efficient encoding+for arbitrary data, with a well-defined bijection to the ubiquitous JSON format+and a precisely specified canonical form.++Note, however, that @cborg@ does not itself aim to be a serialisation+library; it merely serves as the substrate on which such a library might be+built. See the [serialise](/package/serialise) library if you are looking for+convenient serialisation of Haskell values.++Instead, @cborg@ targets cases where precise control over the CBOR object+structure is needed such as when working with externally-specified CBOR formats.++-}++{- $structure++The library is split into a number of modules,++* Decoding++    * "Codec.CBOR.Decoding" defines the machinery for decoding primitive CBOR terms+      into Haskell values. In particular, the 'Decoder' type and associated decoders,++      @+      data 'Decoder' s a++      -- for, e.g., safe in-place mutation during decoding+      liftST      :: ST s a -> 'Decoder' s a++      -- primitive decoders+      decodeWord  :: 'Decoder' s Word+      decodeBytes :: 'Decoder' s ByteString+      -- et cetera+      @+    * "Codec.CBOR.Read" defines the low-level wire-format decoder, e.g.++      @+      'Codec.CBOR.Read.deserialiseFromBytes' :: 'Decoder' a+                           -> ByteString+                           -> Either String (ByteString, a)+      @++* Encoding++      * "Codec.CBOR.Encoding" defines the 'Encoding' type, which is in essence+        difference-list of CBOR tokens and is used to construct CBOR encodings.++        @+        data 'Encoding'+        instance Monoid 'Encoding'++        encodeWord  :: Word       -> Encoding+        encodeBytes :: ByteString -> Encoding+        -- et cetera+        @++      * "Codec.CBOR.Write" defines the low-level wire-format encoder, e.g.++        @+        'Codec.CBOR.Write.toBuilder' :: 'Encoding' a -> Data.ByteString.Builder.Builder+        @++* Capturing arbitrary terms++      * "Codec.CBOR.Term" provides the 'Term' type, which provides a type for+        capturing arbitrary CBOR terms. 'Term's can be encoded and decoded with,++        @+        data 'Term'+          = TInt   Int+          | TBytes ByteString+          -- et cetera++        'encodeTerm' :: 'Term' -> 'Encoding'+        'decodeTerm' :: 'Decoder' 'Term'+        @++* Debugging++    * "Codec.CBOR.FlatTerm" contains the 'FlatTerm' type, which provides a+      concrete AST for capturing primitive CBOR wire encodings. This can be+      useful when testing decoders and encoders.++-}
+ src/Codec/CBOR/ByteArray.hs view
@@ -0,0 +1,89 @@+{-# LANGUAGE CPP                 #-}+{-# LANGUAGE MagicHash           #-}+{-# LANGUAGE RankNTypes          #-}+{-# LANGUAGE TypeFamilies        #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- |+-- Module      : Codec.CBOR.ByteArray+-- Copyright   : (c) Ben Gamari 2017-2018+-- License     : BSD3-style (see LICENSE.txt)+--+-- Maintainer  : duncan@community.haskell.org+-- Stability   : experimental+-- Portability : non-portable (GHC extensions)+--+-- A ByteArray with more instances than 'Data.Primitive.ByteArray.ByteArray'.+-- Some day when these instances are reliably available from @primitive@ we can+-- likely replace this with 'Data.Primitive.ByteArray.ByteArray'.+--+module Codec.CBOR.ByteArray+  ( -- * Simple byte arrays+    ByteArray(..)+  , sizeofByteArray+    -- * Conversions+  , fromShortByteString+  , toShortByteString+  , fromByteString+  , toBuilder+  , toSliced+  ) where++import Data.Char (ord)+import Data.Word+import GHC.Exts (IsList(..), IsString(..))++import qualified Data.Primitive.ByteArray as Prim+import qualified Data.ByteString as BS+import qualified Data.ByteString.Short as BSS+import qualified Data.ByteString.Short.Internal as BSS+import qualified Data.ByteString.Builder as BSB++import qualified Codec.CBOR.ByteArray.Sliced as Sliced+import           Codec.CBOR.ByteArray.Internal++newtype ByteArray = BA {unBA :: Prim.ByteArray}++sizeofByteArray :: ByteArray -> Int+{-# INLINE sizeofByteArray #-}+sizeofByteArray (BA ba) = Prim.sizeofByteArray ba++fromShortByteString :: BSS.ShortByteString -> ByteArray+fromShortByteString (BSS.SBS ba) = BA (Prim.ByteArray ba)++toShortByteString :: ByteArray -> BSS.ShortByteString+toShortByteString (BA (Prim.ByteArray ba)) = BSS.SBS ba++fromByteString :: BS.ByteString -> ByteArray+fromByteString = fromShortByteString . BSS.toShort++toBuilder :: ByteArray -> BSB.Builder+toBuilder = Sliced.toBuilder . toSliced++toSliced :: ByteArray -> Sliced.SlicedByteArray+toSliced ba@(BA arr) = Sliced.SBA arr 0 (sizeofByteArray ba)++instance Show ByteArray where+  showsPrec _ = shows . toSliced++instance Eq ByteArray where+  ba1 == ba2 = toSliced ba1 == toSliced ba2++instance Ord ByteArray where+  ba1 `compare` ba2 = toSliced ba1 `compare` toSliced ba2++instance IsString ByteArray where+  fromString = fromList . map checkedOrd+    where+      checkedOrd c+        | c > '\xff' = error "IsString(Codec.CBOR.ByteArray): Non-ASCII character"+        | otherwise  = fromIntegral $ ord c++instance IsList ByteArray where+  type Item ByteArray = Word8+  fromList xs = fromListN (Prelude.length xs) xs+  fromListN n xs =+      let arr = mkByteArray n xs+      in BA arr+  toList ba@(BA arr) =+      foldrByteArray (:) [] 0 (sizeofByteArray ba) arr
+ src/Codec/CBOR/ByteArray/Internal.hs view
@@ -0,0 +1,85 @@+{-# LANGUAGE BangPatterns        #-}+{-# LANGUAGE CPP                 #-}+{-# LANGUAGE MagicHash           #-}+{-# LANGUAGE RankNTypes          #-}+{-# LANGUAGE UnboxedTuples       #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- |+-- Module      : Codec.CBOR.ByteArray.Internal+-- Copyright   : (c) Ben Gamari 2017-2018+-- License     : BSD3-style (see LICENSE.txt)+--+-- Maintainer  : duncan@community.haskell.org+-- Stability   : experimental+-- Portability : non-portable (GHC extensions)+--+-- Various bytearray utilities+--+module Codec.CBOR.ByteArray.Internal+  ( foldrByteArray+  , copyToAddr+  , isTrue#+  , sameByteArray+  , mkByteArray+  , isByteArrayPinned+  , touch+  ) where++import Control.Monad.ST+import Control.Monad+import GHC.IO (IO(..))+import GHC.Exts+import GHC.Word++import qualified Data.Primitive.ByteArray as Prim++foldrByteArray :: (Word8 -> a -> a) -> a+               -> Int             -- ^ offset+               -> Int             -- ^ length+               -> Prim.ByteArray  -- ^ array+               -> a+foldrByteArray f z off0 len ba = go off0+  where+    go !off+      | off == len = z+      | otherwise  =+        let x = Prim.indexByteArray ba off+        in f x (go (off+1))++copyToAddr :: Prim.ByteArray -> Int -> Ptr a -> Int -> IO ()+copyToAddr (Prim.ByteArray ba) (I# off) (Ptr addr) (I# len) =+    IO (\s -> case copyByteArrayToAddr# ba off addr len s of+                s' -> (# s', () #))++#if __GLASGOW_HASKELL__ < 706+isTrue# :: Bool -> Bool+isTrue# = id+#endif++sameByteArray :: Prim.ByteArray -> Prim.ByteArray -> Bool+sameByteArray (Prim.ByteArray ba1#) (Prim.ByteArray ba2#) =+    case reallyUnsafePtrEquality# (unsafeCoerce# ba1# :: ()) (unsafeCoerce# ba2# :: ()) of+      r -> isTrue# r++-- | @mkByteArray n xs@ forms a 'Prim.ByteArray' with contents @xs@. Note that+-- @n@ must be the precise length of @xs@.+mkByteArray :: Int -> [Word8] -> Prim.ByteArray+mkByteArray n xs = runST $ do+    arr <- Prim.newByteArray n+    zipWithM_ (Prim.writeByteArray arr) [0..n-1] (take n $ xs ++ repeat 0)+    Prim.unsafeFreezeByteArray arr++-- | A conservative estimate of pinned-ness.+isByteArrayPinned :: Prim.ByteArray -> Bool+isByteArrayPinned (Prim.ByteArray ba) =+#if __GLASGOW_HASKELL__ > 800+    case isByteArrayPinned# ba of+      0# -> False+      _  -> True+#else+    False+#endif++touch :: a -> IO ()+touch x = IO $ \s -> case touch# x s of s' -> (# s', () #)
+ src/Codec/CBOR/ByteArray/Sliced.hs view
@@ -0,0 +1,153 @@+{-# LANGUAGE BangPatterns        #-}+{-# LANGUAGE MagicHash           #-}+{-# LANGUAGE CPP                 #-}+{-# LANGUAGE TypeFamilies        #-}++-- |+-- Module      : Codec.CBOR.ByteArray.Sliced+-- Copyright   : (c) Ben Gamari 2017-2018+-- License     : BSD3-style (see LICENSE.txt)+--+-- Maintainer  : duncan@community.haskell.org+-- Stability   : experimental+-- Portability : non-portable (GHC extensions)+--+-- A ByteArray with more instances than 'Data.Primitive.ByteArray.ByteArray'.+-- Some day when these instances are reliably available from @primitive@ we can+-- likely replace this with 'Data.Primitive.ByteArray.ByteArray'.+--+module Codec.CBOR.ByteArray.Sliced+  ( SlicedByteArray(..)+    -- * Conversions+  , sizeofSlicedByteArray+  , fromShortByteString+  , fromByteString+  , fromByteArray+  , toByteString+  , toBuilder+  ) where++import GHC.Exts+import Data.Char (chr, ord)+import Data.Word+import Foreign.Ptr+import Control.Monad.ST+import System.IO.Unsafe++import qualified Data.Primitive.ByteArray as Prim+import           Data.Primitive.Types (Addr(..))+import qualified Data.ByteString as BS+import qualified Data.ByteString.Unsafe as BS+import qualified Data.ByteString.Short as BSS+import qualified Data.ByteString.Short.Internal as BSS+import qualified Data.ByteString.Builder as BSB+import qualified Data.ByteString.Builder.Internal as BSB++import Codec.CBOR.ByteArray.Internal++data SlicedByteArray = SBA {unSBA :: !Prim.ByteArray, offset :: !Int, length :: !Int}++fromShortByteString :: BSS.ShortByteString -> SlicedByteArray+fromShortByteString (BSS.SBS ba) = fromByteArray (Prim.ByteArray ba)++fromByteString :: BS.ByteString -> SlicedByteArray+fromByteString = fromShortByteString . BSS.toShort++fromByteArray :: Prim.ByteArray -> SlicedByteArray+fromByteArray ba = SBA ba 0 (Prim.sizeofByteArray ba)++sizeofSlicedByteArray :: SlicedByteArray -> Int+sizeofSlicedByteArray (SBA _ _ len) = len++-- | Note that this may require a copy.+toByteString :: SlicedByteArray -> BS.ByteString+toByteString sba =+    unsafePerformIO+    $ BS.unsafePackCStringFinalizer ptr (sizeofSlicedByteArray sba) (touch pinned)+  where+    pinned = toPinned sba+    !(Addr addr#) = Prim.byteArrayContents pinned+    ptr = Ptr addr#++toPinned :: SlicedByteArray -> Prim.ByteArray+toPinned (SBA ba off len)+  | isByteArrayPinned ba = ba+  | otherwise = runST $ do+        ba' <- Prim.newPinnedByteArray len+        Prim.copyByteArray ba' 0 ba off len+        Prim.unsafeFreezeByteArray ba'++toBuilder :: SlicedByteArray -> BSB.Builder+toBuilder = \(SBA ba off len) -> BSB.builder (go ba off len)+  where+    go ba !ip !ipe !k (BSB.BufferRange op ope)+      | inpRemaining <= outRemaining = do+          copyToAddr ba ip op inpRemaining+          let !br' = BSB.BufferRange (op `plusPtr` inpRemaining) ope+          k br'+      | otherwise = do+          copyToAddr ba ip op outRemaining+          let !ip' = ip + outRemaining+          return $ BSB.bufferFull 1 ope (go ba ip' ipe k)+      where+        outRemaining = ope `minusPtr` op+        inpRemaining = ipe - ip++instance IsString SlicedByteArray where+  fromString = fromList . map checkedOrd+    where+      checkedOrd c+        | c > '\xff' = error "IsString(Codec.CBOR.ByteArray.Sliced): Non-ASCII character"+        | otherwise  = fromIntegral $ ord c++instance IsList SlicedByteArray where+  type Item SlicedByteArray = Word8+  fromList xs = fromListN (Prelude.length xs) xs+  -- Note that we make no attempt to behave sensibly if @n /= length xs@.+  -- The class definition allows this.+  fromListN n xs =+      let arr = mkByteArray n xs+      in SBA arr 0 n+  toList (SBA arr off len) =+      foldrByteArray (:) [] off len arr++instance Show SlicedByteArray where+  showsPrec _ = shows . map (chr . fromIntegral) . toList++instance Eq SlicedByteArray where+  SBA arr1 off1 len1 == SBA arr2 off2 len2+    | len1 /= len2+    = False++    | sameByteArray arr1 arr2+    , off1 == off2+    , len1 == len2+    = True++    | otherwise+    = let (!) :: Prim.ByteArray -> Int -> Word8+          (!) = Prim.indexByteArray+          go i1 i2+            | i1 == len1 && i2 == len2   = True+            | i1 == len1 || i2 == len2   = False+            | (arr1 ! i1) == (arr2 ! i2) = go (i1+1) (i2+1)+            | otherwise                  = False+      in go off1 off2++instance Ord SlicedByteArray where+  SBA arr1 off1 len1 `compare` SBA arr2 off2 len2+    | sameByteArray arr1 arr2+    , off1 == off2+    , len1 == len2+    = EQ++    | otherwise+    = let (!) :: Prim.ByteArray -> Int -> Word8+          (!) = Prim.indexByteArray+          go i1 i2+            | i1 == len1 && i2 == len2 = EQ+            | i1 == len1 || i2 == len2 = len1 `compare` len2+            | EQ <- o                  = go (i1+1) (i2+1)+            | otherwise                = o+            where o = (arr1 ! i1) `compare` (arr2 ! i2)+      in go off1 off2
src/Codec/CBOR/Decoding.hs view
@@ -36,26 +36,51 @@   , decodeInt16         -- :: Decoder s Int16   , decodeInt32         -- :: Decoder s Int32   , decodeInt64         -- :: Decoder s Int64+  , decodeWordCanonical      -- :: Decoder s Word+  , decodeWord8Canonical     -- :: Decoder s Word8+  , decodeWord16Canonical    -- :: Decoder s Word16+  , decodeWord32Canonical    -- :: Decoder s Word32+  , decodeWord64Canonical    -- :: Decoder s Word64+  , decodeNegWordCanonical   -- :: Decoder s Word+  , decodeNegWord64Canonical -- :: Decoder s Word64+  , decodeIntCanonical       -- :: Decoder s Int+  , decodeInt8Canonical      -- :: Decoder s Int8+  , decodeInt16Canonical     -- :: Decoder s Int16+  , decodeInt32Canonical     -- :: Decoder s Int32+  , decodeInt64Canonical     -- :: Decoder s Int64   , decodeInteger       -- :: Decoder s Integer   , decodeFloat         -- :: Decoder s Float   , decodeDouble        -- :: Decoder s Double   , decodeBytes         -- :: Decoder s ByteString   , decodeBytesIndef    -- :: Decoder s ()+  , decodeByteArray     -- :: Decoder s ByteArray   , decodeString        -- :: Decoder s Text   , decodeStringIndef   -- :: Decoder s ()+  , decodeUtf8ByteArray -- :: Decoder s ByteArray   , decodeListLen       -- :: Decoder s Int+  , decodeListLenCanonical -- :: Decoder s Int   , decodeListLenIndef  -- :: Decoder s ()   , decodeMapLen        -- :: Decoder s Int+  , decodeMapLenCanonical -- :: Decoder s Int   , decodeMapLenIndef   -- :: Decoder s ()   , decodeTag           -- :: Decoder s Word   , decodeTag64         -- :: Decoder s Word64+  , decodeTagCanonical   -- :: Decoder s Word+  , decodeTag64Canonical -- :: Decoder s Word64   , decodeBool          -- :: Decoder s Bool   , decodeNull          -- :: Decoder s ()   , decodeSimple        -- :: Decoder s Word8+  , decodeIntegerCanonical -- :: Decoder s Integer+  , decodeFloat16Canonical -- :: Decoder s Float+  , decodeFloatCanonical   -- :: Decoder s Float+  , decodeDoubleCanonical  -- :: Decoder s Double+  , decodeSimpleCanonical  -- :: Decoder s Word8    -- ** Specialised Read input token operations   , decodeWordOf        -- :: Word -> Decoder s ()   , decodeListLenOf     -- :: Int  -> Decoder s ()+  , decodeWordCanonicalOf    -- :: Word -> Decoder s ()+  , decodeListLenCanonicalOf -- :: Int  -> Decoder s ()    -- ** Branching operations --, decodeBytesOrIndef@@ -89,6 +114,8 @@ import           Control.Monad.ST import qualified Control.Monad.Fail as Fail +import           Codec.CBOR.ByteArray (ByteArray)+ import           Prelude hiding (decodeFloat)  @@ -121,6 +148,19 @@     | ConsumeMapLen  (Int#  -> ST s (DecodeAction s a))     | ConsumeTag     (Word# -> ST s (DecodeAction s a)) +    | ConsumeWordCanonical    (Word# -> ST s (DecodeAction s a))+    | ConsumeWord8Canonical   (Word# -> ST s (DecodeAction s a))+    | ConsumeWord16Canonical  (Word# -> ST s (DecodeAction s a))+    | ConsumeWord32Canonical  (Word# -> ST s (DecodeAction s a))+    | ConsumeNegWordCanonical (Word# -> ST s (DecodeAction s a))+    | ConsumeIntCanonical     (Int#  -> ST s (DecodeAction s a))+    | ConsumeInt8Canonical    (Int#  -> ST s (DecodeAction s a))+    | ConsumeInt16Canonical   (Int#  -> ST s (DecodeAction s a))+    | ConsumeInt32Canonical   (Int#  -> ST s (DecodeAction s a))+    | ConsumeListLenCanonical (Int#  -> ST s (DecodeAction s a))+    | ConsumeMapLenCanonical  (Int#  -> ST s (DecodeAction s a))+    | ConsumeTagCanonical     (Word# -> ST s (DecodeAction s a))+ -- 64bit variants for 32bit machines #if defined(ARCH_32bit)     | ConsumeWord64    (Word64# -> ST s (DecodeAction s a))@@ -129,16 +169,31 @@     | ConsumeListLen64 (Int64#  -> ST s (DecodeAction s a))     | ConsumeMapLen64  (Int64#  -> ST s (DecodeAction s a))     | ConsumeTag64     (Word64# -> ST s (DecodeAction s a))++    | ConsumeWord64Canonical    (Word64# -> ST s (DecodeAction s a))+    | ConsumeNegWord64Canonical (Word64# -> ST s (DecodeAction s a))+    | ConsumeInt64Canonical     (Int64#  -> ST s (DecodeAction s a))+    | ConsumeListLen64Canonical (Int64#  -> ST s (DecodeAction s a))+    | ConsumeMapLen64Canonical  (Int64#  -> ST s (DecodeAction s a))+    | ConsumeTag64Canonical     (Word64# -> ST s (DecodeAction s a)) #endif -    | ConsumeInteger (Integer    -> ST s (DecodeAction s a))-    | ConsumeFloat   (Float#     -> ST s (DecodeAction s a))-    | ConsumeDouble  (Double#    -> ST s (DecodeAction s a))-    | ConsumeBytes   (ByteString -> ST s (DecodeAction s a))-    | ConsumeString  (Text       -> ST s (DecodeAction s a))-    | ConsumeBool    (Bool       -> ST s (DecodeAction s a))-    | ConsumeSimple  (Word#      -> ST s (DecodeAction s a))+    | ConsumeInteger       (Integer   -> ST s (DecodeAction s a))+    | ConsumeFloat         (Float#    -> ST s (DecodeAction s a))+    | ConsumeDouble        (Double#   -> ST s (DecodeAction s a))+    | ConsumeBytes         (ByteString-> ST s (DecodeAction s a))+    | ConsumeByteArray     (ByteArray -> ST s (DecodeAction s a))+    | ConsumeString        (Text      -> ST s (DecodeAction s a))+    | ConsumeUtf8ByteArray (ByteArray -> ST s (DecodeAction s a))+    | ConsumeBool          (Bool      -> ST s (DecodeAction s a))+    | ConsumeSimple        (Word#     -> ST s (DecodeAction s a)) +    | ConsumeIntegerCanonical (Integer -> ST s (DecodeAction s a))+    | ConsumeFloat16Canonical (Float#  -> ST s (DecodeAction s a))+    | ConsumeFloatCanonical   (Float#  -> ST s (DecodeAction s a))+    | ConsumeDoubleCanonical  (Double# -> ST s (DecodeAction s a))+    | ConsumeSimpleCanonical  (Word#   -> ST s (DecodeAction s a))+     | ConsumeBytesIndef   (ST s (DecodeAction s a))     | ConsumeStringIndef  (ST s (DecodeAction s a))     | ConsumeListLenIndef (ST s (DecodeAction s a))@@ -220,6 +275,10 @@ instance Fail.MonadFail (Decoder s) where     fail msg = Decoder $ \_ -> return (Fail msg) +-- | Lift an @ST@ action into a @Decoder@. Useful for, e.g., leveraging+-- in-place mutation to efficiently build a deserialised value.+--+-- @since 0.2.0.0 liftST :: ST s a -> Decoder s a liftST m = Decoder $ \k -> m >>= k @@ -333,6 +392,105 @@   Decoder (\k -> return (ConsumeInt64 (\n64# -> k (I64# n64#)))) #endif +-- | Decode canonical representation of a @'Word'@.+--+-- @since 0.2.0.0+decodeWordCanonical :: Decoder s Word+decodeWordCanonical = Decoder (\k -> return (ConsumeWordCanonical (\w# -> k (W# w#))))+{-# INLINE decodeWordCanonical #-}++-- | Decode canonical representation of a @'Word8'@.+--+-- @since 0.2.0.0+decodeWord8Canonical :: Decoder s Word8+decodeWord8Canonical = Decoder (\k -> return (ConsumeWord8Canonical (\w# -> k (W8# w#))))+{-# INLINE decodeWord8Canonical #-}++-- | Decode canonical representation of a @'Word16'@.+--+-- @since 0.2.0.0+decodeWord16Canonical :: Decoder s Word16+decodeWord16Canonical = Decoder (\k -> return (ConsumeWord16Canonical (\w# -> k (W16# w#))))+{-# INLINE decodeWord16Canonical #-}++-- | Decode canonical representation of a @'Word32'@.+--+-- @since 0.2.0.0+decodeWord32Canonical :: Decoder s Word32+decodeWord32Canonical = Decoder (\k -> return (ConsumeWord32Canonical (\w# -> k (W32# w#))))+{-# INLINE decodeWord32Canonical #-}++-- | Decode canonical representation of a @'Word64'@.+--+-- @since 0.2.0.0+decodeWord64Canonical :: Decoder s Word64+{-# INLINE decodeWord64Canonical #-}+decodeWord64Canonical =+#if defined(ARCH_64bit)+  Decoder (\k -> return (ConsumeWordCanonical (\w# -> k (W64# w#))))+#else+  Decoder (\k -> return (ConsumeWord64Canonical (\w64# -> k (W64# w64#))))+#endif++-- | Decode canonical representation of a negative @'Word'@.+--+-- @since 0.2.0.0+decodeNegWordCanonical :: Decoder s Word+decodeNegWordCanonical = Decoder (\k -> return (ConsumeNegWordCanonical (\w# -> k (W# w#))))+{-# INLINE decodeNegWordCanonical #-}++-- | Decode canonical representation of a negative @'Word64'@.+--+-- @since 0.2.0.0+decodeNegWord64Canonical :: Decoder s Word64+{-# INLINE decodeNegWord64Canonical #-}+decodeNegWord64Canonical =+#if defined(ARCH_64bit)+  Decoder (\k -> return (ConsumeNegWordCanonical (\w# -> k (W64# w#))))+#else+  Decoder (\k -> return (ConsumeNegWord64Canonical (\w64# -> k (W64# w64#))))+#endif++-- | Decode canonical representation of an @'Int'@.+--+-- @since 0.2.0.0+decodeIntCanonical :: Decoder s Int+decodeIntCanonical = Decoder (\k -> return (ConsumeIntCanonical (\n# -> k (I# n#))))+{-# INLINE decodeIntCanonical #-}++-- | Decode canonical representation of an @'Int8'@.+--+-- @since 0.2.0.0+decodeInt8Canonical :: Decoder s Int8+decodeInt8Canonical = Decoder (\k -> return (ConsumeInt8Canonical (\w# -> k (I8# w#))))+{-# INLINE decodeInt8Canonical #-}++-- | Decode canonical representation of an @'Int16'@.+--+-- @since 0.2.0.0+decodeInt16Canonical :: Decoder s Int16+decodeInt16Canonical = Decoder (\k -> return (ConsumeInt16Canonical (\w# -> k (I16# w#))))+{-# INLINE decodeInt16Canonical #-}++-- | Decode canonical representation of an @'Int32'@.+--+-- @since 0.2.0.0+decodeInt32Canonical :: Decoder s Int32+decodeInt32Canonical = Decoder (\k -> return (ConsumeInt32Canonical (\w# -> k (I32# w#))))+{-# INLINE decodeInt32Canonical #-}++-- | Decode canonical representation of an @'Int64'@.+--+-- @since 0.2.0.0+decodeInt64Canonical :: Decoder s Int64+{-# INLINE decodeInt64Canonical #-}+decodeInt64Canonical =+#if defined(ARCH_64bit)+  Decoder (\k -> return (ConsumeIntCanonical (\n# -> k (I64# n#))))+#else+  Decoder (\k -> return (ConsumeInt64Canonical (\n64# -> k (I64# n64#))))+#endif+ -- | Decode an @'Integer'@. -- -- @since 0.2.0.0@@ -369,6 +527,17 @@ decodeBytesIndef = Decoder (\k -> return (ConsumeBytesIndef (k ()))) {-# INLINE decodeBytesIndef #-} +-- | Decode a string of bytes as a 'ByteArray'.+--+-- Also note that this will eagerly copy the content out of the input+-- to ensure that the input does not leak in the event that the 'ByteArray' is+-- live but not forced.+--+-- @since 0.2.0.0+decodeByteArray :: Decoder s ByteArray+decodeByteArray = Decoder (\k -> return (ConsumeByteArray k))+{-# INLINE decodeByteArray #-}+ -- | Decode a textual string as a piece of @'Text'@. -- -- @since 0.2.0.0@@ -384,6 +553,18 @@ decodeStringIndef = Decoder (\k -> return (ConsumeStringIndef (k ()))) {-# INLINE decodeStringIndef #-} +-- | Decode a textual string as UTF-8 encoded 'ByteArray'. Note that+-- the result is not validated to be well-formed UTF-8.+--+-- Also note that this will eagerly copy the content out of the input+-- to ensure that the input does not leak in the event that the 'ByteArray' is+-- live but not forced.+--+-- @since 0.2.0.0+decodeUtf8ByteArray :: Decoder s ByteArray+decodeUtf8ByteArray = Decoder (\k -> return (ConsumeUtf8ByteArray k))+{-# INLINE decodeUtf8ByteArray #-}+ -- | Decode the length of a list. -- -- @since 0.2.0.0@@ -391,6 +572,13 @@ decodeListLen = Decoder (\k -> return (ConsumeListLen (\n# -> k (I# n#)))) {-# INLINE decodeListLen #-} +-- | Decode canonical representation of the length of a list.+--+-- @since 0.2.0.0+decodeListLenCanonical :: Decoder s Int+decodeListLenCanonical = Decoder (\k -> return (ConsumeListLenCanonical (\n# -> k (I# n#))))+{-# INLINE decodeListLenCanonical #-}+ -- | Decode a token marking the beginning of a list of indefinite -- length. --@@ -406,6 +594,13 @@ decodeMapLen = Decoder (\k -> return (ConsumeMapLen (\n# -> k (I# n#)))) {-# INLINE decodeMapLen #-} +-- | Decode canonical representation of the length of a map.+--+-- @since 0.2.0.0+decodeMapLenCanonical :: Decoder s Int+decodeMapLenCanonical = Decoder (\k -> return (ConsumeMapLenCanonical (\n# -> k (I# n#))))+{-# INLINE decodeMapLenCanonical #-}+ -- | Decode a token marking the beginning of a map of indefinite -- length. --@@ -433,6 +628,27 @@   Decoder (\k -> return (ConsumeTag64 (\w64# -> k (W64# w64#)))) #endif +-- | Decode canonical representation of an arbitrary tag and return it as a+-- @'Word'@.+--+-- @since 0.2.0.0+decodeTagCanonical :: Decoder s Word+decodeTagCanonical = Decoder (\k -> return (ConsumeTagCanonical (\w# -> k (W# w#))))+{-# INLINE decodeTagCanonical #-}++-- | Decode canonical representation of an arbitrary 64-bit tag and return it as+-- a @'Word64'@.+--+-- @since 0.2.0.0+decodeTag64Canonical :: Decoder s Word64+{-# INLINE decodeTag64Canonical #-}+decodeTag64Canonical =+#if defined(ARCH_64bit)+  Decoder (\k -> return (ConsumeTagCanonical (\w# -> k (W64# w#))))+#else+  Decoder (\k -> return (ConsumeTag64Canonical (\w64# -> k (W64# w64#))))+#endif+ -- | Decode a bool. -- -- @since 0.2.0.0@@ -455,7 +671,42 @@ decodeSimple = Decoder (\k -> return (ConsumeSimple (\w# -> k (W8# w#)))) {-# INLINE decodeSimple #-} +-- | Decode canonical representation of an @'Integer'@.+--+-- @since 0.2.0.0+decodeIntegerCanonical :: Decoder s Integer+decodeIntegerCanonical = Decoder (\k -> return (ConsumeIntegerCanonical (\n -> k n)))+{-# INLINE decodeIntegerCanonical #-} +-- | Decode canonical representation of a half-precision @'Float'@.+--+-- @since 0.2.0.0+decodeFloat16Canonical :: Decoder s Float+decodeFloat16Canonical = Decoder (\k -> return (ConsumeFloat16Canonical (\f# -> k (F# f#))))+{-# INLINE decodeFloat16Canonical #-}++-- | Decode canonical representation of a @'Float'@.+--+-- @since 0.2.0.0+decodeFloatCanonical :: Decoder s Float+decodeFloatCanonical = Decoder (\k -> return (ConsumeFloatCanonical (\f# -> k (F# f#))))+{-# INLINE decodeFloatCanonical #-}++-- | Decode canonical representation of a @'Double'@.+--+-- @since 0.2.0.0+decodeDoubleCanonical :: Decoder s Double+decodeDoubleCanonical = Decoder (\k -> return (ConsumeDoubleCanonical (\f# -> k (D# f#))))+{-# INLINE decodeDoubleCanonical #-}++-- | Decode canonical representation of a 'simple' CBOR value and give back a+-- @'Word8'@. You probably don't ever need to use this.+--+-- @since 0.2.0.0+decodeSimpleCanonical :: Decoder s Word8+decodeSimpleCanonical = Decoder (\k -> return (ConsumeSimpleCanonical (\w# -> k (W8# w#))))+{-# INLINE decodeSimpleCanonical #-}+ -------------------------------------------------------------- -- Specialised read operations: expect a token with a specific value --@@ -466,10 +717,7 @@ -- @since 0.2.0.0 decodeWordOf :: Word -- ^ Expected value of the decoded word              -> Decoder s ()-decodeWordOf n = do-  n' <- decodeWord-  if n == n' then return ()-             else fail $ "expected word " ++ show n+decodeWordOf = decodeWordOfHelper decodeWord {-# INLINE decodeWordOf #-}  -- | Attempt to decode a list length using @'decodeListLen'@, and@@ -477,12 +725,40 @@ -- -- @since 0.2.0.0 decodeListLenOf :: Int -> Decoder s ()-decodeListLenOf len = do-  len' <- decodeListLen+decodeListLenOf = decodeListLenOfHelper decodeListLen+{-# INLINE decodeListLenOf #-}++-- | Attempt to decode canonical representation of a word with @'decodeWordCanonical'@,+-- and ensure the word is exactly as expected, or fail.+--+-- @since 0.2.0.0+decodeWordCanonicalOf :: Word -- ^ Expected value of the decoded word+                      -> Decoder s ()+decodeWordCanonicalOf = decodeWordOfHelper decodeWordCanonical+{-# INLINE decodeWordCanonicalOf #-}++-- | Attempt to decode canonical representation of a list length using+-- @'decodeListLenCanonical'@, and ensure it is exactly the specified length, or+-- fail.+--+-- @since 0.2.0.0+decodeListLenCanonicalOf :: Int -> Decoder s ()+decodeListLenCanonicalOf = decodeListLenOfHelper decodeListLenCanonical+{-# INLINE decodeListLenCanonicalOf #-}++decodeListLenOfHelper :: (Show a, Eq a, Monad m) => m a -> a -> m ()+decodeListLenOfHelper decodeFun = \len -> do+  len' <- decodeFun   if len == len' then return ()                  else fail $ "expected list of length " ++ show len-{-# INLINE decodeListLenOf #-}+{-# INLINE decodeListLenOfHelper #-} +decodeWordOfHelper :: (Show a, Eq a, Monad m) => m a -> a -> m ()+decodeWordOfHelper decodeFun = \n -> do+  n' <- decodeFun+  if n == n' then return ()+             else fail $ "expected word " ++ show n+{-# INLINE decodeWordOfHelper #-}  -------------------------------------------------------------- -- Branching operations
src/Codec/CBOR/Encoding.hs view
@@ -31,8 +31,10 @@   , encodeInteger            -- :: Integer -> Encoding   , encodeBytes              -- :: B.ByteString -> Encoding   , encodeBytesIndef         -- :: Encoding+  , encodeByteArray          -- :: ByteArray -> Encoding   , encodeString             -- :: T.Text -> Encoding   , encodeStringIndef        -- :: Encoding+  , encodeUtf8ByteArray      -- :: ByteArray -> Encoding   , encodeListLen            -- :: Word -> Encoding   , encodeListLenIndef       -- :: Encoding   , encodeMapLen             -- :: Word -> Encoding@@ -58,6 +60,8 @@ import qualified Data.ByteString as B import qualified Data.Text       as T +import           Codec.CBOR.ByteArray.Sliced (SlicedByteArray)+ import           Prelude         hiding (encodeFloat)  import {-# SOURCE #-} qualified Codec.CBOR.FlatTerm as FlatTerm@@ -95,10 +99,12 @@     | TkInt64    {-# UNPACK #-} !Int64        Tokens      -- Bytes and string (type 2,3)-    | TkBytes    {-# UNPACK #-} !B.ByteString Tokens-    | TkBytesBegin                            Tokens-    | TkString   {-# UNPACK #-} !T.Text       Tokens-    | TkStringBegin                           Tokens+    | TkBytes         {-# UNPACK #-} !B.ByteString    Tokens+    | TkBytesBegin                                    Tokens+    | TkByteArray     {-# UNPACK #-} !SlicedByteArray Tokens+    | TkString        {-# UNPACK #-} !T.Text          Tokens+    | TkUtf8ByteArray {-# UNPACK #-} !SlicedByteArray Tokens+    | TkStringBegin                                   Tokens      -- Structures (type 4,5)     | TkListLen  {-# UNPACK #-} !Word         Tokens@@ -214,6 +220,12 @@ encodeBytes :: B.ByteString -> Encoding encodeBytes = Encoding . TkBytes +-- | Encode a bytestring in a flattened format.+--+-- @since 0.2.0.0+encodeByteArray :: SlicedByteArray -> Encoding+encodeByteArray = Encoding . TkByteArray+ -- | Encode a token specifying the beginning of a string of bytes of -- indefinite length. In reality, this specifies a stream of many -- occurrences of `encodeBytes`, each specifying a single chunk of the@@ -235,6 +247,13 @@ -- @since 0.2.0.0 encodeStringIndef :: Encoding encodeStringIndef = Encoding TkStringBegin++-- | Encode a UTF-8 string in a flattened format. Note that the contents+-- is not validated to be well-formed UTF-8.+--+-- @since 0.2.0.0+encodeUtf8ByteArray :: SlicedByteArray -> Encoding+encodeUtf8ByteArray = Encoding . TkUtf8ByteArray  -- | Encode the length of a list, used to indicate that the following -- tokens represent the list values.
src/Codec/CBOR/FlatTerm.hs view
@@ -43,6 +43,8 @@ import           Codec.CBOR.Encoding (Encoding(..)) import qualified Codec.CBOR.Encoding as Enc import           Codec.CBOR.Decoding as Dec+import qualified Codec.CBOR.ByteArray        as BA+import qualified Codec.CBOR.ByteArray.Sliced as BAS  import           Data.Int #if defined(ARCH_32bit)@@ -56,6 +58,7 @@  import           Data.Word import           Data.Text (Text)+import qualified Data.Text.Encoding as TE import           Data.ByteString (ByteString) import           Control.Monad.ST @@ -118,8 +121,12 @@   | otherwise                       = TkInteger   n : convFlatTerm ts convFlatTerm (Enc.TkBytes    bs ts) = TkBytes    bs : convFlatTerm ts convFlatTerm (Enc.TkBytesBegin  ts) = TkBytesBegin  : convFlatTerm ts+convFlatTerm (Enc.TkByteArray a ts)+  = TkBytes (BAS.toByteString a) : convFlatTerm ts convFlatTerm (Enc.TkString   st ts) = TkString   st : convFlatTerm ts convFlatTerm (Enc.TkStringBegin ts) = TkStringBegin : convFlatTerm ts+convFlatTerm (Enc.TkUtf8ByteArray a ts)+  = TkString (TE.decodeUtf8 $ BAS.toByteString a) : convFlatTerm ts convFlatTerm (Enc.TkListLen  n  ts) = TkListLen   n : convFlatTerm ts convFlatTerm (Enc.TkListBegin   ts) = TkListBegin   : convFlatTerm ts convFlatTerm (Enc.TkMapLen   n  ts) = TkMapLen    n : convFlatTerm ts@@ -196,6 +203,52 @@     go (TkTag     n : ts) (ConsumeTag     k)         | n <= maxWord                       = k (unW# (fromIntegral n)) >>= go ts +    go (TkInt     n : ts) (ConsumeWordCanonical k)+        | n >= 0                             = k (unW# (fromIntegral n)) >>= go ts+    go (TkInteger n : ts) (ConsumeWordCanonical k)+        | n >= 0                             = k (unW# (fromIntegral n)) >>= go ts+    go (TkInt     n : ts) (ConsumeWord8Canonical k)+        | n >= 0 && n <= maxWord8            = k (unW# (fromIntegral n)) >>= go ts+    go (TkInteger n : ts) (ConsumeWord8Canonical k)+        | n >= 0 && n <= maxWord8            = k (unW# (fromIntegral n)) >>= go ts+    go (TkInt     n : ts) (ConsumeWord16Canonical k)+        | n >= 0 && n <= maxWord16           = k (unW# (fromIntegral n)) >>= go ts+    go (TkInteger n : ts) (ConsumeWord16Canonical k)+        | n >= 0 && n <= maxWord16           = k (unW# (fromIntegral n)) >>= go ts+    go (TkInt     n : ts) (ConsumeWord32Canonical k)+        -- NOTE: we have to be very careful about this branch+        -- on 32 bit machines, because maxBound :: Int < maxBound :: Word32+        | intIsValidWord32 n                 = k (unW# (fromIntegral n)) >>= go ts+    go (TkInteger n : ts) (ConsumeWord32Canonical k)+        | n >= 0 && n <= maxWord32           = k (unW# (fromIntegral n)) >>= go ts+    go (TkInt     n : ts) (ConsumeNegWordCanonical k)+        | n <  0                             = k (unW# (fromIntegral (-1-n))) >>= go ts+    go (TkInteger n : ts) (ConsumeNegWordCanonical k)+        | n <  0                             = k (unW# (fromIntegral (-1-n))) >>= go ts+    go (TkInt     n : ts) (ConsumeIntCanonical k)     = k (unI# n) >>= go ts+    go (TkInteger n : ts) (ConsumeInt k)+        | n <= maxInt                        = k (unI# (fromIntegral n)) >>= go ts+    go (TkInt     n : ts) (ConsumeInt8Canonical k)+        | n >= minInt8 && n <= maxInt8       = k (unI# n) >>= go ts+    go (TkInteger n : ts) (ConsumeInt8Canonical k)+        | n >= minInt8 && n <= maxInt8       = k (unI# (fromIntegral n)) >>= go ts+    go (TkInt     n : ts) (ConsumeInt16Canonical k)+        | n >= minInt16 && n <= maxInt16     = k (unI# n) >>= go ts+    go (TkInteger n : ts) (ConsumeInt16Canonical k)+        | n >= minInt16 && n <= maxInt16     = k (unI# (fromIntegral n)) >>= go ts+    go (TkInt     n : ts) (ConsumeInt32Canonical k)+        | n >= minInt32 && n <= maxInt32     = k (unI# n) >>= go ts+    go (TkInteger n : ts) (ConsumeInt32Canonical k)+        | n >= minInt32 && n <= maxInt32     = k (unI# (fromIntegral n)) >>= go ts+    go (TkInt     n : ts) (ConsumeIntegerCanonical k) = k (fromIntegral n) >>= go ts+    go (TkInteger n : ts) (ConsumeIntegerCanonical k) = k n >>= go ts+    go (TkListLen n : ts) (ConsumeListLenCanonical k)+        | n <= maxInt                        = k (unI# (fromIntegral n)) >>= go ts+    go (TkMapLen  n : ts) (ConsumeMapLenCanonical  k)+        | n <= maxInt                        = k (unI# (fromIntegral n)) >>= go ts+    go (TkTag     n : ts) (ConsumeTagCanonical     k)+        | n <= maxWord                       = k (unW# (fromIntegral n)) >>= go ts+ #if defined(ARCH_32bit)     -- 64bit variants for 32bit machines     go (TkInt       n : ts) (ConsumeWord64    k)@@ -212,22 +265,47 @@      go (TkTag       n : ts) (ConsumeTag64     k) = k (unW64# n) >>= go ts +    go (TkInt       n : ts) (ConsumeWord64Canonical    k)+      | n >= 0                                   = k (unW64# (fromIntegral n)) >>= go ts+    go (TkInteger   n : ts) (ConsumeWord64Canonical    k)+      | n >= 0                                   = k (unW64# (fromIntegral n)) >>= go ts+    go (TkInt       n : ts) (ConsumeNegWord64Canonical k)+      | n < 0                                    = k (unW64# (fromIntegral (-1-n))) >>= go ts+    go (TkInteger   n : ts) (ConsumeNegWord64Canonical k)+      | n < 0                                    = k (unW64# (fromIntegral (-1-n))) >>= go ts++    go (TkInt       n : ts) (ConsumeInt64Canonical     k) = k (unI64# (fromIntegral n)) >>= go ts+    go (TkInteger   n : ts) (ConsumeInt64Canonical     k) = k (unI64# (fromIntegral n)) >>= go ts++    go (TkTag       n : ts) (ConsumeTag64Canonical     k) = k (unW64# n) >>= go ts++     -- TODO FIXME (aseipp/dcoutts): are these going to be utilized?     -- see fallthrough case below if/when fixed.-    go ts (ConsumeListLen64 _)                   = unexpected "decodeListLen64" ts-    go ts (ConsumeMapLen64  _)                   = unexpected "decodeMapLen64"  ts+    go ts (ConsumeListLen64 _)          = unexpected "decodeListLen64" ts+    go ts (ConsumeMapLen64  _)          = unexpected "decodeMapLen64"  ts+    go ts (ConsumeListLen64Canonical _) = unexpected "decodeListLen64Canonical" ts+    go ts (ConsumeMapLen64Canonical  _) = unexpected "decodeMapLen64Canonical"  ts #endif -    go (TkFloat16 f : ts) (ConsumeFloat  k) = k (unF# f) >>= go ts-    go (TkFloat32 f : ts) (ConsumeFloat  k) = k (unF# f) >>= go ts-    go (TkFloat16 f : ts) (ConsumeDouble k) = k (unD# (float2Double f)) >>= go ts-    go (TkFloat32 f : ts) (ConsumeDouble k) = k (unD# (float2Double f)) >>= go ts-    go (TkFloat64 f : ts) (ConsumeDouble k) = k (unD# f) >>= go ts-    go (TkBytes  bs : ts) (ConsumeBytes  k) = k bs >>= go ts-    go (TkString st : ts) (ConsumeString k) = k st >>= go ts-    go (TkBool    b : ts) (ConsumeBool   k) = k b >>= go ts-    go (TkSimple  n : ts) (ConsumeSimple k) = k (unW8# n) >>= go ts+    go (TkFloat16 f : ts) (ConsumeFloat  k)        = k (unF# f) >>= go ts+    go (TkFloat32 f : ts) (ConsumeFloat  k)        = k (unF# f) >>= go ts+    go (TkFloat16 f : ts) (ConsumeDouble k)        = k (unD# (float2Double f)) >>= go ts+    go (TkFloat32 f : ts) (ConsumeDouble k)        = k (unD# (float2Double f)) >>= go ts+    go (TkFloat64 f : ts) (ConsumeDouble k)        = k (unD# f) >>= go ts+    go (TkBytes  bs : ts) (ConsumeBytes  k)        = k bs >>= go ts+    go (TkBytes  bs : ts) (ConsumeByteArray k)     = k (BA.fromByteString bs) >>= go ts+    go (TkString st : ts) (ConsumeString k)        = k st >>= go ts+    go (TkString st : ts) (ConsumeUtf8ByteArray k) = k (BA.fromByteString $ TE.encodeUtf8 st)+                                                     >>= go ts+    go (TkBool    b : ts) (ConsumeBool   k)        = k b >>= go ts+    go (TkSimple  n : ts) (ConsumeSimple k)        = k (unW8# n) >>= go ts +    go (TkFloat16 f : ts) (ConsumeFloat16Canonical k) = k (unF# f) >>= go ts+    go (TkFloat32 f : ts) (ConsumeFloatCanonical   k) = k (unF# f) >>= go ts+    go (TkFloat64 f : ts) (ConsumeDoubleCanonical  k) = k (unD# f) >>= go ts+    go (TkSimple  n : ts) (ConsumeSimpleCanonical  k) = k (unW8# n) >>= go ts+     go (TkBytesBegin  : ts) (ConsumeBytesIndef   da) = da >>= go ts     go (TkStringBegin : ts) (ConsumeStringIndef  da) = da >>= go ts     go (TkListBegin   : ts) (ConsumeListLenIndef da) = da >>= go ts@@ -269,13 +347,35 @@     go ts (ConsumeMapLen  _) = unexpected "decodeMapLen"  ts     go ts (ConsumeTag     _) = unexpected "decodeTag"     ts +    go ts (ConsumeWordCanonical    _) = unexpected "decodeWordCanonical"    ts+    go ts (ConsumeWord8Canonical   _) = unexpected "decodeWord8Canonical"   ts+    go ts (ConsumeWord16Canonical  _) = unexpected "decodeWord16Canonical"  ts+    go ts (ConsumeWord32Canonical  _) = unexpected "decodeWord32Canonical"  ts+    go ts (ConsumeNegWordCanonical _) = unexpected "decodeNegWordCanonical" ts+    go ts (ConsumeIntCanonical     _) = unexpected "decodeIntCanonical"     ts+    go ts (ConsumeInt8Canonical    _) = unexpected "decodeInt8Canonical"    ts+    go ts (ConsumeInt16Canonical   _) = unexpected "decodeInt16Canonical"   ts+    go ts (ConsumeInt32Canonical   _) = unexpected "decodeInt32Canonical"   ts+    go ts (ConsumeIntegerCanonical _) = unexpected "decodeIntegerCanonical" ts++    go ts (ConsumeListLenCanonical _) = unexpected "decodeListLenCanonical" ts+    go ts (ConsumeMapLenCanonical  _) = unexpected "decodeMapLenCanonical"  ts+    go ts (ConsumeTagCanonical     _) = unexpected "decodeTagCanonical"     ts+     go ts (ConsumeFloat  _) = unexpected "decodeFloat"  ts     go ts (ConsumeDouble _) = unexpected "decodeDouble" ts     go ts (ConsumeBytes  _) = unexpected "decodeBytes"  ts+    go ts (ConsumeByteArray     _) = unexpected "decodeByteArray"     ts     go ts (ConsumeString _) = unexpected "decodeString" ts+    go ts (ConsumeUtf8ByteArray _) = unexpected "decodeUtf8ByteArray" ts     go ts (ConsumeBool   _) = unexpected "decodeBool"   ts     go ts (ConsumeSimple _) = unexpected "decodeSimple" ts +    go ts (ConsumeFloat16Canonical _) = unexpected "decodeFloat16Canonical" ts+    go ts (ConsumeFloatCanonical   _) = unexpected "decodeFloatCanonical"   ts+    go ts (ConsumeDoubleCanonical  _) = unexpected "decodeDoubleCanonical"  ts+    go ts (ConsumeSimpleCanonical  _) = unexpected "decodeSimpleCanonical"  ts+ #if defined(ARCH_32bit)     -- 64bit variants for 32bit machines     go ts (ConsumeWord64    _) = unexpected "decodeWord64"    ts@@ -284,6 +384,13 @@     go ts (ConsumeTag64     _) = unexpected "decodeTag64"     ts   --go ts (ConsumeListLen64 _) = unexpected "decodeListLen64" ts   --go ts (ConsumeMapLen64  _) = unexpected "decodeMapLen64"  ts++    go ts (ConsumeWord64Canonical    _) = unexpected "decodeWord64Canonical"    ts+    go ts (ConsumeNegWord64Canonical _) = unexpected "decodeNegWord64Canonical" ts+    go ts (ConsumeInt64Canonical     _) = unexpected "decodeInt64Canonical"     ts+    go ts (ConsumeTag64Canonical     _) = unexpected "decodeTag64Canonical"     ts+  --go ts (ConsumeListLen64Canonical _) = unexpected "decodeListLen64Canonical" ts+  --go ts (ConsumeMapLen64Canonical  _) = unexpected "decodeMapLen64Canonical"  ts #endif      go ts (ConsumeBytesIndef   _) = unexpected "decodeBytesIndef"   ts
src/Codec/CBOR/Magic.hs view
@@ -236,13 +236,8 @@ -- with it. withBsPtr :: (Ptr b -> a) -> ByteString -> a withBsPtr f (BS.PS x off _) =-#if MIN_VERSION_bytestring(0,10,6)-    BS.accursedUnutterablePerformIO $ withForeignPtr x $-        \(Ptr addr#) -> return $! (f (Ptr addr# `plusPtr` off))-#else     unsafeDupablePerformIO $ withForeignPtr x $         \(Ptr addr#) -> return $! (f (Ptr addr# `plusPtr` off))-#endif {-# INLINE withBsPtr #-}  --------------------------------------------------------------------------------
src/Codec/CBOR/Pretty.hs view
@@ -279,8 +279,10 @@ unconsToken (TkInt64 i     tks) = Just (TkInt64 i     TkEnd,tks) unconsToken (TkBytes bs    tks) = Just (TkBytes bs    TkEnd,tks) unconsToken (TkBytesBegin  tks) = Just (TkBytesBegin  TkEnd,tks)+unconsToken (TkByteArray a tks) = Just (TkByteArray a TkEnd,tks) unconsToken (TkString t    tks) = Just (TkString t    TkEnd,tks) unconsToken (TkStringBegin tks) = Just (TkStringBegin TkEnd,tks)+unconsToken (TkUtf8ByteArray a tks) = Just (TkUtf8ByteArray a TkEnd,tks) unconsToken (TkListLen len tks) = Just (TkListLen len TkEnd,tks) unconsToken (TkListBegin   tks) = Just (TkListBegin   TkEnd,tks) unconsToken (TkMapLen len  tks) = Just (TkMapLen len  TkEnd,tks)
src/Codec/CBOR/Read.hs view
@@ -2,11 +2,14 @@ {-# LANGUAGE MagicHash          #-} {-# LANGUAGE BangPatterns       #-} {-# LANGUAGE RankNTypes         #-}+{-# LANGUAGE OverloadedStrings  #-} {-# LANGUAGE DeriveDataTypeable #-} +#if __GLASGOW_HASKELL__ > 706 -- Bump up from the default 1.5, otherwise our decoder fast path is no good. -- We went over the threshold when we switched to using ST. {-# OPTIONS_GHC -funfolding-keeness-factor=2.0 #-}+#endif  -- | -- Module      : Codec.CBOR.Read@@ -21,8 +24,9 @@ -- back into ordinary values. -- module Codec.CBOR.Read-  ( deserialiseFromBytes   -- :: Decoder a -> ByteString -> Either String (ByteString, a)-  , deserialiseIncremental -- :: Decoder a -> ST s (IDecode s a)+  ( deserialiseFromBytes         -- :: Decoder a -> ByteString -> Either String (ByteString, a)+  , deserialiseFromBytesWithSize -- :: Decoder a -> ByteString -> Either String (ByteString, ByteOffset, a)+  , deserialiseIncremental       -- :: Decoder a -> ST s (IDecode s a)   , DeserialiseFailure(..)   , IDecode(..)   , ByteOffset@@ -51,11 +55,15 @@ import qualified Data.Text.Encoding as T import           Data.Word import           GHC.Word+#if defined(ARCH_32bit)+import           GHC.IntWord64+#endif import           GHC.Exts import           GHC.Float (float2Double) import           Data.Typeable import           Control.Exception +import qualified Codec.CBOR.ByteArray as BA import           Codec.CBOR.Decoding hiding (DecodeAction(Done, Fail)) import           Codec.CBOR.Decoding (DecodeAction) import qualified Codec.CBOR.Decoding as D@@ -108,22 +116,37 @@                      -> LBS.ByteString                      -> Either DeserialiseFailure (LBS.ByteString, a) deserialiseFromBytes d lbs =-    runIDecode (deserialiseIncremental d) lbs+    fmap f $ runIDecode (deserialiseIncremental d) lbs+  where f (rest, _, x) = (rest, x) +-- | Given a @'Decoder'@ and some @'LBS.ByteString'@ representing+-- an encoded CBOR value, return @'Either'@ the decoded CBOR value+-- or an error. In addition to the decoded value return any remaining input+-- content and the number of bytes consumed.+--+-- @since 0.2.0.0+deserialiseFromBytesWithSize :: (forall s. Decoder s a)+                             -> LBS.ByteString+                             -> Either DeserialiseFailure (LBS.ByteString, ByteOffset, a)+deserialiseFromBytesWithSize d lbs =+    runIDecode (deserialiseIncremental d) lbs  runIDecode :: (forall s. ST s (IDecode s a))            -> LBS.ByteString-           -> Either DeserialiseFailure (LBS.ByteString, a)+           -> Either DeserialiseFailure (LBS.ByteString, ByteOffset, a) runIDecode d lbs =     runST (go lbs =<< d)   where     go :: LBS.ByteString        -> IDecode s a-       -> ST s (Either DeserialiseFailure (LBS.ByteString, a))-    go  _                  (Fail _ _ err) = return (Left err)-    go  lbs'               (Done bs _ x)  = return (Right (LBS.Chunk bs lbs', x))-    go  LBS.Empty          (Partial  k)   = k Nothing   >>= go LBS.Empty-    go (LBS.Chunk bs lbs') (Partial  k)   = k (Just bs) >>= go lbs'+       -> ST s (Either DeserialiseFailure (LBS.ByteString, ByteOffset, a))+    go  _                  (Fail _ _ err)  = return (Left err)+    go  lbs'               (Done bs off x) = let rest+                                                   | BS.null bs = lbs'+                                                   | otherwise  = LBS.Chunk bs lbs'+                                             in return (Right (rest, off, x))+    go  LBS.Empty          (Partial  k)    = k Nothing   >>= go LBS.Empty+    go (LBS.Chunk bs lbs') (Partial  k)    = k (Just bs) >>= go lbs'  -- | Run a @'Decoder'@ incrementally, returning a continuation -- representing the result of the incremental decode.@@ -208,11 +231,13 @@ -- of tokens spanning a chunk boundary. -- data SlowPath s a-   = FastDone               {-# UNPACK #-} !ByteString a-   | SlowConsumeTokenString {-# UNPACK #-} !ByteString (T.Text     -> ST s (DecodeAction s a)) {-# UNPACK #-} !Int-   | SlowConsumeTokenBytes  {-# UNPACK #-} !ByteString (ByteString -> ST s (DecodeAction s a)) {-# UNPACK #-} !Int-   | SlowDecodeAction       {-# UNPACK #-} !ByteString (DecodeAction s a)-   | SlowFail               {-# UNPACK #-} !ByteString String+   = FastDone                      {-# UNPACK #-} !ByteString a+   | SlowConsumeTokenBytes         {-# UNPACK #-} !ByteString (ByteString   -> ST s (DecodeAction s a)) {-# UNPACK #-} !Int+   | SlowConsumeTokenByteArray     {-# UNPACK #-} !ByteString (BA.ByteArray -> ST s (DecodeAction s a)) {-# UNPACK #-} !Int+   | SlowConsumeTokenString        {-# UNPACK #-} !ByteString (T.Text       -> ST s (DecodeAction s a)) {-# UNPACK #-} !Int+   | SlowConsumeTokenUtf8ByteArray {-# UNPACK #-} !ByteString (BA.ByteArray -> ST s (DecodeAction s a)) {-# UNPACK #-} !Int+   | SlowDecodeAction              {-# UNPACK #-} !ByteString (DecodeAction s a)+   | SlowFail                      {-# UNPACK #-} !ByteString String   -- The main fast path. The fast path itself is actually split into two parts@@ -313,7 +338,113 @@       DecodeFailure           -> go_fast_end bs da       DecodedToken sz (W# w#) -> k w# >>= go_fast (BS.unsafeDrop sz bs) +go_fast !bs da@(ConsumeWordCanonical k) =+    case tryConsumeWord (BS.unsafeHead bs) bs of+      DecodeFailure           -> go_fast_end bs da+      DecodedToken sz (W# w#)+        | isWordCanonical sz w# -> k w# >>= go_fast (BS.unsafeDrop sz bs)+        | otherwise             -> go_fast_end bs da++go_fast !bs da@(ConsumeWord8Canonical k) =+    case tryConsumeWord (BS.unsafeHead bs) bs of+      DecodeFailure           -> go_fast_end bs da+      DecodedToken sz (W# w#) ->+        case gtWord# w# 0xff## of+          0# | isWordCanonical sz w# -> k w# >>= go_fast (BS.unsafeDrop sz bs)+          _                          -> go_fast_end bs da++go_fast !bs da@(ConsumeWord16Canonical k) =+    case tryConsumeWord (BS.unsafeHead bs) bs of+      DecodeFailure           -> go_fast_end bs da+      DecodedToken sz (W# w#) ->+        case gtWord# w# 0xffff## of+          0# | isWordCanonical sz w# -> k w# >>= go_fast (BS.unsafeDrop sz bs)+          _                          -> go_fast_end bs da++go_fast !bs da@(ConsumeWord32Canonical k) =+    case tryConsumeWord (BS.unsafeHead bs) bs of+      DecodeFailure           -> go_fast_end bs da+      DecodedToken sz (W# w#) ->+        case w_out_of_range w# of+          0# | isWordCanonical sz w# -> k w# >>= go_fast (BS.unsafeDrop sz bs)+          _                          -> go_fast_end bs da+  where+    w_out_of_range _w# = #if defined(ARCH_32bit)+      0#+#else+      gtWord# _w# 0xffffffff##+#endif++go_fast !bs da@(ConsumeNegWordCanonical k) =+    case tryConsumeNegWord (BS.unsafeHead bs) bs of+      DecodeFailure           -> go_fast_end bs da+      DecodedToken sz (W# w#)+        | isWordCanonical sz w# -> k w# >>= go_fast (BS.unsafeDrop sz bs)+        | otherwise             -> go_fast_end bs da++go_fast !bs da@(ConsumeIntCanonical k) =+    case tryConsumeInt (BS.unsafeHead bs) bs of+      DecodeFailure           -> go_fast_end bs da+      DecodedToken sz (I# n#)+        | isIntCanonical sz n# -> k n# >>= go_fast (BS.unsafeDrop sz bs)+        | otherwise            -> go_fast_end bs da++go_fast !bs da@(ConsumeInt8Canonical k) =+    case tryConsumeInt (BS.unsafeHead bs) bs of+      DecodeFailure           -> go_fast_end bs da+      DecodedToken sz (I# n#) ->+        case (n# ># 0x7f#) `orI#` (n# <# -0x80#) of+          0# | isIntCanonical sz n# -> k n# >>= go_fast (BS.unsafeDrop sz bs)+          _                         -> go_fast_end bs da++go_fast !bs da@(ConsumeInt16Canonical k) =+    case tryConsumeInt (BS.unsafeHead bs) bs of+      DecodeFailure           -> go_fast_end bs da+      DecodedToken sz (I# n#) ->+        case (n# ># 0x7fff#) `orI#` (n# <# -0x8000#) of+          0# | isIntCanonical sz n# -> k n# >>= go_fast (BS.unsafeDrop sz bs)+          _                         -> go_fast_end bs da++go_fast !bs da@(ConsumeInt32Canonical k) =+    case tryConsumeInt (BS.unsafeHead bs) bs of+      DecodeFailure           -> go_fast_end bs da+      DecodedToken sz (I# n#) ->+        case n_out_of_range n# of+          0# | isIntCanonical sz n# -> k n# >>= go_fast (BS.unsafeDrop sz bs)+          _                         -> go_fast_end bs da+  where+    n_out_of_range _n# =+#if defined(ARCH_32bit)+      0#+#else+      (_n# ># 0x7fffffff#) `orI#` (_n# <# -0x80000000#)+#endif++go_fast !bs da@(ConsumeListLenCanonical k) =+    case tryConsumeListLen (BS.unsafeHead bs) bs of+      DecodeFailure           -> go_fast_end bs da+      DecodedToken sz (I# n#)+          -- List length can't be negative, cast it to Word#.+        | isWordCanonical sz (int2Word# n#) -> k n# >>= go_fast (BS.unsafeDrop sz bs)+        | otherwise                         -> go_fast_end bs da++go_fast !bs da@(ConsumeMapLenCanonical k) =+    case tryConsumeMapLen (BS.unsafeHead bs) bs of+      DecodeFailure           -> go_fast_end bs da+      DecodedToken sz (I# n#)+          -- Map length can't be negative, cast it to Word#.+        | isWordCanonical sz (int2Word# n#) -> k n# >>= go_fast (BS.unsafeDrop sz bs)+        | otherwise                         -> go_fast_end bs da++go_fast !bs da@(ConsumeTagCanonical k) =+    case tryConsumeTag (BS.unsafeHead bs) bs of+      DecodeFailure           -> go_fast_end bs da+      DecodedToken sz (W# w#)+        | isWordCanonical sz w# -> k w# >>= go_fast (BS.unsafeDrop sz bs)+        | otherwise             -> go_fast_end bs da++#if defined(ARCH_32bit) go_fast !bs da@(ConsumeWord64 k) =   case tryConsumeWord64 (BS.unsafeHead bs) bs of     DecodeFailure             -> go_fast_end bs da@@ -343,12 +474,56 @@   case tryConsumeTag64 (BS.unsafeHead bs) bs of     DecodeFailure             -> go_fast_end bs da     DecodedToken sz (W64# w#) -> k w# >>= go_fast (BS.unsafeDrop sz bs)++go_fast !bs da@(ConsumeWord64Canonical k) =+  case tryConsumeWord64 (BS.unsafeHead bs) bs of+    DecodeFailure             -> go_fast_end bs da+    DecodedToken sz (W64# w#)+      | isWord64Canonical -> k w# >>= go_fast (BS.unsafeDrop sz bs)+      | otherwise         -> go_fast_end bs da++go_fast !bs da@(ConsumeNegWord64Canonical k) =+  case tryConsumeNegWord64 (BS.unsafeHead bs) bs of+    DecodeFailure             -> go_fast_end bs da+    DecodedToken sz (W64# w#)+      | isWord64Canonical sz w# -> k w# >>= go_fast (BS.unsafeDrop sz bs)+      | otherwise               -> go_fast_end bs da++go_fast !bs da@(ConsumeInt64Canonical k) =+  case tryConsumeInt64 (BS.unsafeHead bs) bs of+    DecodeFailure             -> go_fast_end bs da+    DecodedToken sz (I64# i#)+      | isInt64Canonical sz i# -> k i# >>= go_fast (BS.unsafeDrop sz bs)+      | otherwise              -> go_fast_end bs da++go_fast !bs da@(ConsumeListLen64Canonical k) =+  case tryConsumeListLen64 (BS.unsafeHead bs) bs of+    DecodeFailure             -> go_fast_end bs da+    DecodedToken sz (I64# i#)+        -- List length can't be negative, cast it to Word64#.+      | isWord64Canonical sz (int64ToWord64 i#) -> k i# >>= go_fast (BS.unsafeDrop sz bs)+      | otherwise                               -> go_fast_end bs da++go_fast !bs da@(ConsumeMapLen64Canonical k) =+  case tryConsumeMapLen64 (BS.unsafeHead bs) bs of+    DecodeFailure             -> go_fast_end bs da+    DecodedToken sz (I64# i#)+        -- Map length can't be negative, cast it to Word64#.+      | isWord64Canonical sz (int64ToWord64 i#) -> k i# >>= go_fast (BS.unsafeDrop sz bs)+      | otherwise                               -> go_fast_end bs da++go_fast !bs da@(ConsumeTag64Canonical k) =+  case tryConsumeTag64 (BS.unsafeHead bs) bs of+    DecodeFailure             -> go_fast_end bs da+    DecodedToken sz (W64# w#)+      | isWord64Canonical sz w# -> k w# >>= go_fast (BS.unsafeDrop sz bs)+      | otherwise               -> go_fast_end bs da #endif  go_fast !bs da@(ConsumeInteger k) =     case tryConsumeInteger (BS.unsafeHead bs) bs of-      DecodedToken sz (BigIntToken n) -> k n >>= go_fast (BS.unsafeDrop sz bs)-      _                               -> go_fast_end bs da+      DecodedToken sz (BigIntToken _ n) -> k n >>= go_fast (BS.unsafeDrop sz bs)+      _                                 -> go_fast_end bs da  go_fast !bs da@(ConsumeFloat k) =     case tryConsumeFloat (BS.unsafeHead bs) bs of@@ -366,12 +541,26 @@       DecodedToken sz (Fits bstr)   -> k bstr >>= go_fast (BS.unsafeDrop sz bs)       DecodedToken sz (TooLong len) -> return $! SlowConsumeTokenBytes (BS.unsafeDrop sz bs) k len +go_fast !bs da@(ConsumeByteArray k) =+    case tryConsumeBytes (BS.unsafeHead bs) bs of+      DecodeFailure                 -> go_fast_end bs da+      DecodedToken sz (Fits str)    -> k (BA.fromByteString str) >>= go_fast (BS.unsafeDrop sz bs)+      DecodedToken sz (TooLong len) -> return $! SlowConsumeTokenByteArray (BS.unsafeDrop sz bs) k len+ go_fast !bs da@(ConsumeString k) =     case tryConsumeString (BS.unsafeHead bs) bs of       DecodeFailure                 -> go_fast_end bs da-      DecodedToken sz (Fits str)    -> k str >>= go_fast (BS.unsafeDrop sz bs)+      DecodedToken sz (Fits str)    -> case T.decodeUtf8' str of+                                         Right t -> k t >>= go_fast (BS.unsafeDrop sz bs)+                                         Left e  -> return $! SlowFail bs (show e)       DecodedToken sz (TooLong len) -> return $! SlowConsumeTokenString (BS.unsafeDrop sz bs) k len +go_fast !bs da@(ConsumeUtf8ByteArray k) =+    case tryConsumeString (BS.unsafeHead bs) bs of+      DecodeFailure                 -> go_fast_end bs da+      DecodedToken sz (Fits str)    -> k (BA.fromByteString str) >>= go_fast (BS.unsafeDrop sz bs)+      DecodedToken sz (TooLong len) -> return $! SlowConsumeTokenUtf8ByteArray (BS.unsafeDrop sz bs) k len+ go_fast !bs da@(ConsumeBool k) =     case tryConsumeBool (BS.unsafeHead bs) of       DecodeFailure     -> go_fast_end bs da@@ -382,6 +571,40 @@       DecodeFailure           -> go_fast_end bs da       DecodedToken sz (W# w#) -> k w# >>= go_fast (BS.unsafeDrop sz bs) +go_fast !bs da@(ConsumeIntegerCanonical k) =+    case tryConsumeInteger (BS.unsafeHead bs) bs of+      DecodedToken sz (BigIntToken True n) -> k n >>= go_fast (BS.unsafeDrop sz bs)+      _                                    -> go_fast_end bs da+++go_fast !bs da@(ConsumeFloat16Canonical k) =+    case tryConsumeFloat (BS.unsafeHead bs) bs of+      DecodeFailure     -> go_fast_end bs da+      DecodedToken sz (F# f#)+        | isFloat16Canonical sz -> k f# >>= go_fast (BS.unsafeDrop sz bs)+        | otherwise             -> go_fast_end bs da++go_fast !bs da@(ConsumeFloatCanonical k) =+    case tryConsumeFloat (BS.unsafeHead bs) bs of+      DecodeFailure     -> go_fast_end bs da+      DecodedToken sz f@(F# f#)+        | isFloatCanonical sz bs f -> k f# >>= go_fast (BS.unsafeDrop sz bs)+        | otherwise                -> go_fast_end bs da++go_fast !bs da@(ConsumeDoubleCanonical k) =+    case tryConsumeDouble (BS.unsafeHead bs) bs of+      DecodeFailure     -> go_fast_end bs da+      DecodedToken sz f@(D# f#)+        | isDoubleCanonical sz bs f -> k f# >>= go_fast (BS.unsafeDrop sz bs)+        | otherwise                 -> go_fast_end bs da++go_fast !bs da@(ConsumeSimpleCanonical k) =+    case tryConsumeSimple (BS.unsafeHead bs) bs of+      DecodeFailure           -> go_fast_end bs da+      DecodedToken sz (W# w#)+        | isWordCanonical sz w# -> k w# >>= go_fast (BS.unsafeDrop sz bs)+        | otherwise             -> go_fast_end bs da+ go_fast !bs da@(ConsumeBytesIndef k) =     case tryConsumeBytesIndef (BS.unsafeHead bs) of       DecodeFailure     -> go_fast_end bs da@@ -554,7 +777,116 @@       DecodeFailure           -> return $! SlowFail bs "expected tag"       DecodedToken sz (W# w#) -> k w# >>= go_fast_end (BS.unsafeDrop sz bs) +go_fast_end !bs (ConsumeWordCanonical k) =+    case tryConsumeWord (BS.unsafeHead bs) bs of+      DecodeFailure           -> return $! SlowFail bs "expected word"+      DecodedToken sz (W# w#)+        | isWordCanonical sz w# -> k w# >>= go_fast_end (BS.unsafeDrop sz bs)+        | otherwise             -> return $! SlowFail bs "non-canonical word"++go_fast_end !bs (ConsumeWord8Canonical k) =+    case tryConsumeWord (BS.unsafeHead bs) bs of+      DecodeFailure           -> return $! SlowFail bs "expected word8"+      DecodedToken sz (W# w#) -> case gtWord# w# 0xff## of+          0# | isWordCanonical sz w# -> k w# >>= go_fast_end (BS.unsafeDrop sz bs)+             | otherwise             -> return $! SlowFail bs "non-canonical word8"+          _                          -> return $! SlowFail bs "expected word8"++go_fast_end !bs (ConsumeWord16Canonical k) =+    case tryConsumeWord (BS.unsafeHead bs) bs of+      DecodeFailure           -> return $! SlowFail bs "expected word16"+      DecodedToken sz (W# w#) -> case gtWord# w# 0xffff## of+        0# | isWordCanonical sz w# -> k w# >>= go_fast_end (BS.unsafeDrop sz bs)+           | otherwise             -> return $! SlowFail bs "non-canonical word16"+        _                          -> return $! SlowFail bs "expected word16"++go_fast_end !bs (ConsumeWord32Canonical k) =+    case tryConsumeWord (BS.unsafeHead bs) bs of+      DecodeFailure           -> return $! SlowFail bs "expected word32"+      DecodedToken sz (W# w#) -> case w_out_of_range w# of+        0# | isWordCanonical sz w# -> k w# >>= go_fast_end (BS.unsafeDrop sz bs)+           | otherwise             -> return $! SlowFail bs "non-canonical word32"+        _                          -> return $! SlowFail bs "expected word32"+  where+    w_out_of_range _w# = #if defined(ARCH_32bit)+      0#+#else+      gtWord# _w# 0xffffffff##+#endif++go_fast_end !bs (ConsumeNegWordCanonical k) =+    case tryConsumeNegWord (BS.unsafeHead bs) bs of+      DecodeFailure           -> return $! SlowFail bs "expected negative int"+      DecodedToken sz (W# w#)+        | isWordCanonical sz w# -> k w# >>= go_fast_end (BS.unsafeDrop sz bs)+        | otherwise             -> return $! SlowFail bs "non-canonical negative int"++go_fast_end !bs (ConsumeIntCanonical k) =+    case tryConsumeInt (BS.unsafeHead bs) bs of+      DecodeFailure           -> return $! SlowFail bs "expected int"+      DecodedToken sz (I# n#)+        | isIntCanonical sz n# -> k n# >>= go_fast_end (BS.unsafeDrop sz bs)+        | otherwise            -> return $! SlowFail bs "non-canonical int"++go_fast_end !bs (ConsumeInt8Canonical k) =+    case tryConsumeInt (BS.unsafeHead bs) bs of+      DecodeFailure           -> return $! SlowFail bs "expected int8"+      DecodedToken sz (I# n#) ->+        case (n# ># 0x7f#) `orI#` (n# <# -0x80#) of+          0# | isIntCanonical sz n# -> k n# >>= go_fast_end (BS.unsafeDrop sz bs)+             | otherwise            -> return $! SlowFail bs "non-canonical int8"+          _                         -> return $! SlowFail bs "expected int8"++go_fast_end !bs (ConsumeInt16Canonical k) =+    case tryConsumeInt (BS.unsafeHead bs) bs of+      DecodeFailure           -> return $! SlowFail bs "expected int16"+      DecodedToken sz (I# n#) ->+        case (n# ># 0x7fff#) `orI#` (n# <# -0x8000#) of+          0# | isIntCanonical sz n# -> k n# >>= go_fast_end (BS.unsafeDrop sz bs)+             | otherwise            -> return $! SlowFail bs "non-canonical int16"+          _                         -> return $! SlowFail bs "expected int16"++go_fast_end !bs (ConsumeInt32Canonical k) =+    case tryConsumeInt (BS.unsafeHead bs) bs of+      DecodeFailure           -> return $! SlowFail bs "expected int32"+      DecodedToken sz (I# n#) ->+        case n_out_of_range n# of+          0# | isIntCanonical sz n# -> k n# >>= go_fast_end (BS.unsafeDrop sz bs)+             | otherwise            -> return $! SlowFail bs "non-canonical int32"+          _                         -> return $! SlowFail bs "expected int32"+  where+    n_out_of_range _n# =+#if defined(ARCH_32bit)+      0#+#else+      (_n# ># 0x7fffffff#) `orI#` (_n# <# -0x80000000#)+#endif++go_fast_end !bs (ConsumeListLenCanonical k) =+    case tryConsumeListLen (BS.unsafeHead bs) bs of+      DecodeFailure           -> return $! SlowFail bs "expected list len"+      DecodedToken sz (I# n#)+          -- List length can't be negative, cast it to Word#.+        | isWordCanonical sz (int2Word# n#) -> k n# >>= go_fast_end (BS.unsafeDrop sz bs)+        | otherwise                         -> return $! SlowFail bs "non-canonical list len"++go_fast_end !bs (ConsumeMapLenCanonical k) =+    case tryConsumeMapLen (BS.unsafeHead bs) bs of+      DecodeFailure           -> return $! SlowFail bs "expected map len"+      DecodedToken sz (I# n#)+          -- Map length can't be negative, cast it to Word#.+        | isWordCanonical sz (int2Word# n#) -> k n# >>= go_fast_end (BS.unsafeDrop sz bs)+        | otherwise                         -> return $! SlowFail bs "non-canonical map len"++go_fast_end !bs (ConsumeTagCanonical k) =+    case tryConsumeTag (BS.unsafeHead bs) bs of+      DecodeFailure           -> return $! SlowFail bs "expected tag"+      DecodedToken sz (W# w#)+        | isWordCanonical sz w# -> k w# >>= go_fast_end (BS.unsafeDrop sz bs)+        | otherwise             -> return $! SlowFail bs "non-canonical tag"++#if defined(ARCH_32bit) go_fast_end !bs (ConsumeWord64 k) =   case tryConsumeWord64 (BS.unsafeHead bs) bs of     DecodeFailure             -> return $! SlowFail bs "expected word64"@@ -589,7 +921,7 @@ go_fast_end !bs (ConsumeInteger k) =     case tryConsumeInteger (BS.unsafeHead bs) bs of       DecodeFailure                         -> return $! SlowFail bs "expected integer"-      DecodedToken sz (BigIntToken n)       -> k n >>= go_fast_end (BS.unsafeDrop sz bs)+      DecodedToken sz (BigIntToken _ n)     -> k n >>= go_fast_end (BS.unsafeDrop sz bs)       DecodedToken sz (BigUIntNeedBody len) -> return $! SlowConsumeTokenBytes (BS.unsafeDrop sz bs) (adjustContBigUIntNeedBody k) len       DecodedToken sz (BigNIntNeedBody len) -> return $! SlowConsumeTokenBytes (BS.unsafeDrop sz bs) (adjustContBigNIntNeedBody k) len       DecodedToken sz  BigUIntNeedHeader    -> return $! SlowDecodeAction      (BS.unsafeDrop sz bs) (adjustContBigUIntNeedHeader k)@@ -611,12 +943,26 @@       DecodedToken sz (Fits bstr)   -> k bstr >>= go_fast_end (BS.unsafeDrop sz bs)       DecodedToken sz (TooLong len) -> return $! SlowConsumeTokenBytes (BS.unsafeDrop sz bs) k len +go_fast_end !bs (ConsumeByteArray k) =+    case tryConsumeBytes (BS.unsafeHead bs) bs of+      DecodeFailure                 -> return $! SlowFail bs "expected string"+      DecodedToken sz (Fits str)    -> (k $! BA.fromByteString str) >>= go_fast_end (BS.unsafeDrop sz bs)+      DecodedToken sz (TooLong len) -> return $! SlowConsumeTokenByteArray (BS.unsafeDrop sz bs) k len+ go_fast_end !bs (ConsumeString k) =     case tryConsumeString (BS.unsafeHead bs) bs of       DecodeFailure                 -> return $! SlowFail bs "expected string"-      DecodedToken sz (Fits str)    -> k str >>= go_fast_end (BS.unsafeDrop sz bs)+      DecodedToken sz (Fits str)    -> case T.decodeUtf8' str of+                                         Right t -> k t >>= go_fast_end (BS.unsafeDrop sz bs)+                                         Left e  -> return $! SlowFail bs (show e)       DecodedToken sz (TooLong len) -> return $! SlowConsumeTokenString (BS.unsafeDrop sz bs) k len +go_fast_end !bs (ConsumeUtf8ByteArray k) =+    case tryConsumeString (BS.unsafeHead bs) bs of+      DecodeFailure                 -> return $! SlowFail bs "expected string"+      DecodedToken sz (Fits str)    -> (k $! BA.fromByteString str) >>= go_fast_end (BS.unsafeDrop sz bs)+      DecodedToken sz (TooLong len) -> return $! SlowConsumeTokenUtf8ByteArray (BS.unsafeDrop sz bs) k len+ go_fast_end !bs (ConsumeBool k) =     case tryConsumeBool (BS.unsafeHead bs) of       DecodeFailure     -> return $! SlowFail bs "expected bool"@@ -627,6 +973,45 @@       DecodeFailure           -> return $! SlowFail bs "expected simple"       DecodedToken sz (W# w#) -> k w# >>= go_fast_end (BS.unsafeDrop sz bs) +go_fast_end !bs (ConsumeIntegerCanonical k) =+    case tryConsumeInteger (BS.unsafeHead bs) bs of+      DecodeFailure                         -> return $! SlowFail bs "expected integer"+      DecodedToken sz (BigIntToken canonical n)+        | canonical -> k n >>= go_fast_end (BS.unsafeDrop sz bs)+        | otherwise -> return $! SlowFail bs "non-canonical integer"+      DecodedToken sz (BigUIntNeedBody len) -> return $! SlowConsumeTokenBytes (BS.unsafeDrop sz bs) (adjustContBigUIntNeedBody k) len+      DecodedToken sz (BigNIntNeedBody len) -> return $! SlowConsumeTokenBytes (BS.unsafeDrop sz bs) (adjustContBigNIntNeedBody k) len+      DecodedToken sz  BigUIntNeedHeader    -> return $! SlowDecodeAction      (BS.unsafeDrop sz bs) (adjustContBigUIntNeedHeader k)+      DecodedToken sz  BigNIntNeedHeader    -> return $! SlowDecodeAction      (BS.unsafeDrop sz bs) (adjustContBigNIntNeedHeader k)++go_fast_end !bs (ConsumeFloat16Canonical k) =+    case tryConsumeFloat (BS.unsafeHead bs) bs of+      DecodeFailure     -> return $! SlowFail bs "expected float"+      DecodedToken sz (F# f#)+        | isFloat16Canonical sz -> k f# >>= go_fast_end (BS.unsafeDrop sz bs)+        | otherwise             -> return $! SlowFail bs "non-canonical float16"++go_fast_end !bs (ConsumeFloatCanonical k) =+    case tryConsumeFloat (BS.unsafeHead bs) bs of+      DecodeFailure     -> return $! SlowFail bs "expected float"+      DecodedToken sz f@(F# f#)+        | isFloatCanonical sz bs f -> k f# >>= go_fast_end (BS.unsafeDrop sz bs)+        | otherwise                -> return $! SlowFail bs "non-canonical float"++go_fast_end !bs (ConsumeDoubleCanonical k) =+    case tryConsumeDouble (BS.unsafeHead bs) bs of+      DecodeFailure           -> return $! SlowFail bs "expected double"+      DecodedToken sz f@(D# f#)+        | isDoubleCanonical sz bs f -> k f# >>= go_fast_end (BS.unsafeDrop sz bs)+        | otherwise                 -> return $! SlowFail bs "non-canonical double"++go_fast_end !bs (ConsumeSimpleCanonical k) =+    case tryConsumeSimple (BS.unsafeHead bs) bs of+      DecodeFailure           -> return $! SlowFail bs "expected simple"+      DecodedToken sz (W# w#)+        | isWordCanonical sz w# -> k w# >>= go_fast_end (BS.unsafeDrop sz bs)+        | otherwise             -> return $! SlowFail bs "non-canonical simple"+ go_fast_end !bs (ConsumeBytesIndef k) =     case tryConsumeBytesIndef (BS.unsafeHead bs) of       DecodeFailure     -> return $! SlowFail bs "expected bytes start"@@ -685,6 +1070,13 @@       where         !offset' = offset + fromIntegral (BS.length bs - BS.length bs') +    SlowConsumeTokenByteArray bs' k len -> do+      (bstr, bs'') <- getTokenVarLen len bs' offset'+      let !str = BA.fromByteString bstr+      lift (k str) >>= \daz -> go_slow daz bs'' (offset' + fromIntegral len)+      where+        !offset' = offset + fromIntegral (BS.length bs - BS.length bs')+     SlowConsumeTokenString bs' k len -> do       (bstr, bs'') <- getTokenVarLen len bs' offset'       let !str = T.decodeUtf8 bstr  -- TODO FIXME: utf8 validation@@ -692,6 +1084,13 @@       where         !offset' = offset + fromIntegral (BS.length bs - BS.length bs') +    SlowConsumeTokenUtf8ByteArray bs' k len -> do+      (bstr, bs'') <- getTokenVarLen len bs' offset'+      let !str = BA.fromByteString bstr+      lift (k str) >>= \daz -> go_slow daz bs'' (offset' + fromIntegral len)+      where+        !offset' = offset + fromIntegral (BS.length bs - BS.length bs')+     -- we didn't have enough input in the buffer     SlowDecodeAction bs' da' | BS.null bs' -> do       -- in this case we're exactly out of input@@ -783,32 +1182,39 @@         assert (BS.null bs_empty) $         return (bs', offset', x) -      SlowConsumeTokenBytes bs_empty k len ->-        assert (BS.null bs_empty) $ do-        (bstr, bs'') <- if BS.length bs' < len-                          then getTokenVarLen len bs' offset'-                          else let !bstr = BS.take len bs'-                                   !bs'' = BS.drop len bs'-                                in return (bstr, bs'')-        lift (k bstr) >>= \daz -> go_slow daz bs'' (offset' + fromIntegral len)+      SlowConsumeTokenBytes bs_empty k len         -> consume id                bs' offset' bs_empty k len -      SlowConsumeTokenString bs_empty k len ->+      SlowConsumeTokenByteArray bs_empty k len     -> consume BA.fromByteString bs' offset' bs_empty k len++      SlowConsumeTokenString bs_empty k len        -> consume T.decodeUtf8      bs' offset' bs_empty k len+        -- TODO FIXME: utf8 validation++      SlowConsumeTokenUtf8ByteArray bs_empty k len -> consume BA.fromByteString bs' offset' bs_empty k len++      SlowFail bs_unconsumed msg ->+        decodeFail (bs_unconsumed <> bs') offset'' msg+        where+          !offset'' = offset + fromIntegral (sz - BS.length bs_unconsumed)+  where+    {-# INLINE consume #-}+    consume :: (BS.ByteString -> a)+            -> BS.ByteString                   -- bs'+            -> Int64                           -- offset'+            -> BS.ByteString                   -- bs_empty+            -> (a -> ST s (DecodeAction s r))  -- continuation+            -> Int                             -- length+            -> IncrementalDecoder s (ByteString, ByteOffset, r)+    consume pack bs' offset' bs_empty k len =         assert (BS.null bs_empty) $ do         (bstr, bs'') <- if BS.length bs' < len                           then getTokenVarLen len bs' offset'                           else let !bstr = BS.take len bs'                                    !bs'' = BS.drop len bs'                                 in return (bstr, bs'')-        let !str = T.decodeUtf8 bstr  -- TODO FIXME: utf8 validation+        let !str = pack bstr         lift (k str) >>= \daz -> go_slow daz bs'' (offset' + fromIntegral len) -      SlowFail bs_unconsumed msg ->-        decodeFail (bs_unconsumed <> bs') offset'' msg-        where-          !offset'' = offset + fromIntegral (sz - BS.length bs_unconsumed) -- -- TODO FIXME: we can do slightly better here. If we're returning a -- lazy string (String, lazy Text, lazy ByteString) then we don't have -- to strictify here and if we're returning a strict string perhaps we@@ -907,14 +1313,66 @@ encodeHeader :: Word -> Word -> Word8 encodeHeader mt ai = fromIntegral (mt `shiftL` 5 .|. ai) - data DecodedToken a = DecodedToken !Int !a | DecodeFailure   deriving Show  data LongToken a = Fits !a | TooLong !Int   deriving Show +{-# INLINE isFloat16Canonical #-}+isFloat16Canonical :: Int -> Bool+isFloat16Canonical sz+  | sz == 3   = True+  | otherwise = False +{-# INLINE isFloatCanonical #-}+isFloatCanonical :: Int -> BS.ByteString -> Float -> Bool+isFloatCanonical sz bs f+  | isNaN f   = sz == 3 && "\xf9\x7e\x00" `BS.isPrefixOf` bs+  | otherwise = sz == 5++{-# INLINE isDoubleCanonical #-}+isDoubleCanonical :: Int -> BS.ByteString -> Double -> Bool+isDoubleCanonical sz bs f+  | isNaN f   = sz == 3 && "\xf9\x7e\x00" `BS.isPrefixOf` bs+  | otherwise = sz == 9++{-# INLINE isWordCanonical #-}+isWordCanonical :: Int -> Word# -> Bool+isWordCanonical sz w#+  | sz == 2 && isTrue# (w# `leWord#` 0x17##)       = False+  | sz == 3 && isTrue# (w# `leWord#` 0xff##)       = False+  | sz == 5 && isTrue# (w# `leWord#` 0xffff##)     = False+  | sz == 9 && isTrue# (w# `leWord#` 0xffffffff##) = False+  | otherwise                                      = True++{-# INLINE isIntCanonical #-}+isIntCanonical :: Int -> Int# -> Bool+isIntCanonical sz i#+  | isTrue# (i# <# 0#) = isWordCanonical sz (not# w#)+  | otherwise          = isWordCanonical sz       w#+  where+    w# = int2Word# i#++#if defined(ARCH_32bit)+{-# INLINE isWord64Canonical #-}+isWord64Canonical :: Int -> Word64# -> Bool+isWord64Canonical sz w#+  | sz == 2 && isTrue# (w# `leWord64#` 0x17##)       = False+  | sz == 3 && isTrue# (w# `leWord64#` 0xff##)       = False+  | sz == 5 && isTrue# (w# `leWord64#` 0xffff##)     = False+  | sz == 9 && isTrue# (w# `leWord64#` 0xffffffff##) = False+  | otherwise                                        = True++{-# INLINE isInt64Canonical #-}+isInt64Canonical :: Int -> Int64# -> Bool+isInt64Canonical sz i#+  | isTrue# (i# <# 0#) = isWord64Canonical sz (not64# w#)+  | otherwise          = isWord64Canonical sz         w#+  where+    w# = int64ToWord64# i#+#endif+ -- TODO FIXME: check with 7.10 and file ticket: -- a case analysis against 0x00 .. 0xff :: Word8 turns into a huge chain -- of >= tests. It could use a jump table, or at least it could use a binary@@ -1060,64 +1518,89 @@ tryConsumeInteger hdr !bs = case fromIntegral hdr :: Word of    -- Positive integers (type 0)-  0x00 -> DecodedToken 1 (BigIntToken 0)-  0x01 -> DecodedToken 1 (BigIntToken 1)-  0x02 -> DecodedToken 1 (BigIntToken 2)-  0x03 -> DecodedToken 1 (BigIntToken 3)-  0x04 -> DecodedToken 1 (BigIntToken 4)-  0x05 -> DecodedToken 1 (BigIntToken 5)-  0x06 -> DecodedToken 1 (BigIntToken 6)-  0x07 -> DecodedToken 1 (BigIntToken 7)-  0x08 -> DecodedToken 1 (BigIntToken 8)-  0x09 -> DecodedToken 1 (BigIntToken 9)-  0x0a -> DecodedToken 1 (BigIntToken 10)-  0x0b -> DecodedToken 1 (BigIntToken 11)-  0x0c -> DecodedToken 1 (BigIntToken 12)-  0x0d -> DecodedToken 1 (BigIntToken 13)-  0x0e -> DecodedToken 1 (BigIntToken 14)-  0x0f -> DecodedToken 1 (BigIntToken 15)-  0x10 -> DecodedToken 1 (BigIntToken 16)-  0x11 -> DecodedToken 1 (BigIntToken 17)-  0x12 -> DecodedToken 1 (BigIntToken 18)-  0x13 -> DecodedToken 1 (BigIntToken 19)-  0x14 -> DecodedToken 1 (BigIntToken 20)-  0x15 -> DecodedToken 1 (BigIntToken 21)-  0x16 -> DecodedToken 1 (BigIntToken 22)-  0x17 -> DecodedToken 1 (BigIntToken 23)-  0x18 -> DecodedToken 2 (BigIntToken (fromIntegral (eatTailWord8 bs)))-  0x19 -> DecodedToken 3 (BigIntToken (fromIntegral (eatTailWord16 bs)))-  0x1a -> DecodedToken 5 (BigIntToken (fromIntegral (eatTailWord32 bs)))-  0x1b -> DecodedToken 9 (BigIntToken (fromIntegral (eatTailWord64 bs)))+  0x00 -> DecodedToken 1 (BigIntToken True 0)+  0x01 -> DecodedToken 1 (BigIntToken True 1)+  0x02 -> DecodedToken 1 (BigIntToken True 2)+  0x03 -> DecodedToken 1 (BigIntToken True 3)+  0x04 -> DecodedToken 1 (BigIntToken True 4)+  0x05 -> DecodedToken 1 (BigIntToken True 5)+  0x06 -> DecodedToken 1 (BigIntToken True 6)+  0x07 -> DecodedToken 1 (BigIntToken True 7)+  0x08 -> DecodedToken 1 (BigIntToken True 8)+  0x09 -> DecodedToken 1 (BigIntToken True 9)+  0x0a -> DecodedToken 1 (BigIntToken True 10)+  0x0b -> DecodedToken 1 (BigIntToken True 11)+  0x0c -> DecodedToken 1 (BigIntToken True 12)+  0x0d -> DecodedToken 1 (BigIntToken True 13)+  0x0e -> DecodedToken 1 (BigIntToken True 14)+  0x0f -> DecodedToken 1 (BigIntToken True 15)+  0x10 -> DecodedToken 1 (BigIntToken True 16)+  0x11 -> DecodedToken 1 (BigIntToken True 17)+  0x12 -> DecodedToken 1 (BigIntToken True 18)+  0x13 -> DecodedToken 1 (BigIntToken True 19)+  0x14 -> DecodedToken 1 (BigIntToken True 20)+  0x15 -> DecodedToken 1 (BigIntToken True 21)+  0x16 -> DecodedToken 1 (BigIntToken True 22)+  0x17 -> DecodedToken 1 (BigIntToken True 23) +  0x18 -> let !w@(W# w#) = eatTailWord8 bs+              sz = 2+          in DecodedToken sz (BigIntToken (isWordCanonical sz w#)   (fromIntegral w))+  0x19 -> let !w@(W# w#) = eatTailWord16 bs+              sz = 3+          in DecodedToken sz (BigIntToken (isWordCanonical sz w#)   (fromIntegral w))+  0x1a -> let !w@(W# w#) = eatTailWord32 bs+              sz = 5+          in DecodedToken sz (BigIntToken (isWordCanonical sz w#)   (fromIntegral w))+  0x1b -> let !w@(W64# w#) = eatTailWord64 bs+              sz = 9+#if defined(ARCH_32bit)+          in DecodedToken sz (BigIntToken (isWord64Canonical sz w#) (fromIntegral w))+#else+          in DecodedToken sz (BigIntToken (isWordCanonical sz w#)   (fromIntegral w))+#endif+   -- Negative integers (type 1)-  0x20 -> DecodedToken 1 (BigIntToken (-1))-  0x21 -> DecodedToken 1 (BigIntToken (-2))-  0x22 -> DecodedToken 1 (BigIntToken (-3))-  0x23 -> DecodedToken 1 (BigIntToken (-4))-  0x24 -> DecodedToken 1 (BigIntToken (-5))-  0x25 -> DecodedToken 1 (BigIntToken (-6))-  0x26 -> DecodedToken 1 (BigIntToken (-7))-  0x27 -> DecodedToken 1 (BigIntToken (-8))-  0x28 -> DecodedToken 1 (BigIntToken (-9))-  0x29 -> DecodedToken 1 (BigIntToken (-10))-  0x2a -> DecodedToken 1 (BigIntToken (-11))-  0x2b -> DecodedToken 1 (BigIntToken (-12))-  0x2c -> DecodedToken 1 (BigIntToken (-13))-  0x2d -> DecodedToken 1 (BigIntToken (-14))-  0x2e -> DecodedToken 1 (BigIntToken (-15))-  0x2f -> DecodedToken 1 (BigIntToken (-16))-  0x30 -> DecodedToken 1 (BigIntToken (-17))-  0x31 -> DecodedToken 1 (BigIntToken (-18))-  0x32 -> DecodedToken 1 (BigIntToken (-19))-  0x33 -> DecodedToken 1 (BigIntToken (-20))-  0x34 -> DecodedToken 1 (BigIntToken (-21))-  0x35 -> DecodedToken 1 (BigIntToken (-22))-  0x36 -> DecodedToken 1 (BigIntToken (-23))-  0x37 -> DecodedToken 1 (BigIntToken (-24))-  0x38 -> DecodedToken 2 (BigIntToken (-1 - fromIntegral (eatTailWord8 bs)))-  0x39 -> DecodedToken 3 (BigIntToken (-1 - fromIntegral (eatTailWord16 bs)))-  0x3a -> DecodedToken 5 (BigIntToken (-1 - fromIntegral (eatTailWord32 bs)))-  0x3b -> DecodedToken 9 (BigIntToken (-1 - fromIntegral (eatTailWord64 bs)))+  0x20 -> DecodedToken 1 (BigIntToken True (-1))+  0x21 -> DecodedToken 1 (BigIntToken True (-2))+  0x22 -> DecodedToken 1 (BigIntToken True (-3))+  0x23 -> DecodedToken 1 (BigIntToken True (-4))+  0x24 -> DecodedToken 1 (BigIntToken True (-5))+  0x25 -> DecodedToken 1 (BigIntToken True (-6))+  0x26 -> DecodedToken 1 (BigIntToken True (-7))+  0x27 -> DecodedToken 1 (BigIntToken True (-8))+  0x28 -> DecodedToken 1 (BigIntToken True (-9))+  0x29 -> DecodedToken 1 (BigIntToken True (-10))+  0x2a -> DecodedToken 1 (BigIntToken True (-11))+  0x2b -> DecodedToken 1 (BigIntToken True (-12))+  0x2c -> DecodedToken 1 (BigIntToken True (-13))+  0x2d -> DecodedToken 1 (BigIntToken True (-14))+  0x2e -> DecodedToken 1 (BigIntToken True (-15))+  0x2f -> DecodedToken 1 (BigIntToken True (-16))+  0x30 -> DecodedToken 1 (BigIntToken True (-17))+  0x31 -> DecodedToken 1 (BigIntToken True (-18))+  0x32 -> DecodedToken 1 (BigIntToken True (-19))+  0x33 -> DecodedToken 1 (BigIntToken True (-20))+  0x34 -> DecodedToken 1 (BigIntToken True (-21))+  0x35 -> DecodedToken 1 (BigIntToken True (-22))+  0x36 -> DecodedToken 1 (BigIntToken True (-23))+  0x37 -> DecodedToken 1 (BigIntToken True (-24))+  0x38 -> let !w@(W# w#) = eatTailWord8 bs+              sz = 2+          in DecodedToken sz (BigIntToken (isWordCanonical sz w#)   (-1 - fromIntegral w))+  0x39 -> let !w@(W# w#) = eatTailWord16 bs+              sz = 3+          in DecodedToken sz (BigIntToken (isWordCanonical sz w#)   (-1 - fromIntegral w))+  0x3a -> let !w@(W# w#) = eatTailWord32 bs+              sz = 5+          in DecodedToken sz (BigIntToken (isWordCanonical sz w#)   (-1 - fromIntegral w))+  0x3b -> let !w@(W64# w#) = eatTailWord64 bs+              sz = 9+#if defined(ARCH_32bit)+          in DecodedToken sz (BigIntToken (isWord64Canonical sz w#) (-1 - fromIntegral w))+#else+          in DecodedToken sz (BigIntToken (isWordCanonical sz w#)   (-1 - fromIntegral w))+#endif    0xc2 -> readBigUInt bs   0xc3 -> readBigNInt bs@@ -1162,38 +1645,38 @@   {-# INLINE tryConsumeString #-}-tryConsumeString :: Word8 -> ByteString -> DecodedToken (LongToken T.Text)+tryConsumeString :: Word8 -> ByteString -> DecodedToken (LongToken BS.ByteString) tryConsumeString hdr !bs = case fromIntegral hdr :: Word of    -- Strings (type 3)-  0x60 -> readStringSmall 0 bs-  0x61 -> readStringSmall 1 bs-  0x62 -> readStringSmall 2 bs-  0x63 -> readStringSmall 3 bs-  0x64 -> readStringSmall 4 bs-  0x65 -> readStringSmall 5 bs-  0x66 -> readStringSmall 6 bs-  0x67 -> readStringSmall 7 bs-  0x68 -> readStringSmall 8 bs-  0x69 -> readStringSmall 9 bs-  0x6a -> readStringSmall 10 bs-  0x6b -> readStringSmall 11 bs-  0x6c -> readStringSmall 12 bs-  0x6d -> readStringSmall 13 bs-  0x6e -> readStringSmall 14 bs-  0x6f -> readStringSmall 15 bs-  0x70 -> readStringSmall 16 bs-  0x71 -> readStringSmall 17 bs-  0x72 -> readStringSmall 18 bs-  0x73 -> readStringSmall 19 bs-  0x74 -> readStringSmall 20 bs-  0x75 -> readStringSmall 21 bs-  0x76 -> readStringSmall 22 bs-  0x77 -> readStringSmall 23 bs-  0x78 -> readString8  bs-  0x79 -> readString16 bs-  0x7a -> readString32 bs-  0x7b -> readString64 bs+  0x60 -> readBytesSmall 0 bs+  0x61 -> readBytesSmall 1 bs+  0x62 -> readBytesSmall 2 bs+  0x63 -> readBytesSmall 3 bs+  0x64 -> readBytesSmall 4 bs+  0x65 -> readBytesSmall 5 bs+  0x66 -> readBytesSmall 6 bs+  0x67 -> readBytesSmall 7 bs+  0x68 -> readBytesSmall 8 bs+  0x69 -> readBytesSmall 9 bs+  0x6a -> readBytesSmall 10 bs+  0x6b -> readBytesSmall 11 bs+  0x6c -> readBytesSmall 12 bs+  0x6d -> readBytesSmall 13 bs+  0x6e -> readBytesSmall 14 bs+  0x6f -> readBytesSmall 15 bs+  0x70 -> readBytesSmall 16 bs+  0x71 -> readBytesSmall 17 bs+  0x72 -> readBytesSmall 18 bs+  0x73 -> readBytesSmall 19 bs+  0x74 -> readBytesSmall 20 bs+  0x75 -> readBytesSmall 21 bs+  0x76 -> readBytesSmall 22 bs+  0x77 -> readBytesSmall 23 bs+  0x78 -> readBytes8  bs+  0x79 -> readBytes16 bs+  0x7a -> readBytes32 bs+  0x7b -> readBytes64 bs   _    -> DecodeFailure  @@ -1782,78 +2265,6 @@     -- TODO FIXME: int overflow     n = fromIntegral (eatTailWord64 bs) --readStringSmall :: Int -> ByteString -> DecodedToken (LongToken T.Text)-readStringSmall n bs-  -- if n <= bound then ok return it all-  | n + hdrsz <= BS.length bs-  = DecodedToken (n+hdrsz) $ Fits $-      T.decodeUtf8 (BS.unsafeTake n (BS.unsafeDrop hdrsz bs))-      -- TODO FIXME: utf8 validation--  -- if n > bound then slow path, multi-chunk-  | otherwise-  = DecodedToken hdrsz $ TooLong n-  where-    hdrsz = 1--readString8, readString16,- readString32, readString64 :: ByteString -> DecodedToken (LongToken T.Text)-readString8 bs-  | n <= BS.length bs - hdrsz-  = DecodedToken (n+hdrsz) $ Fits $-      T.decodeUtf8 (BS.unsafeTake n (BS.unsafeDrop hdrsz bs))-      -- TODO FIXME: utf8 validation--  -- if n > bound then slow path, multi-chunk-  | otherwise-  = DecodedToken hdrsz $ TooLong n-  where-    hdrsz = 2-    n = fromIntegral (eatTailWord8 bs)--readString16 bs-  | n <= BS.length bs - hdrsz-  = DecodedToken (n+hdrsz) $ Fits $-      T.decodeUtf8 (BS.unsafeTake n (BS.unsafeDrop hdrsz bs))-      -- TODO FIXME: utf8 validation--  -- if n > bound then slow path, multi-chunk-  | otherwise-  = DecodedToken hdrsz $ TooLong n-  where-    hdrsz = 3-    n = fromIntegral (eatTailWord16 bs)--readString32 bs-  | n <= BS.length bs - hdrsz-  = DecodedToken (n+hdrsz) $ Fits $-      T.decodeUtf8 (BS.unsafeTake n (BS.unsafeDrop hdrsz bs))-      -- TODO FIXME: utf8 validation--  -- if n > bound then slow path, multi-chunk-  | otherwise-  = DecodedToken hdrsz $ TooLong n-  where-    hdrsz = 5-    -- TODO FIXME: int overflow-    n = fromIntegral (eatTailWord32 bs)--readString64 bs-  | n <= BS.length bs - hdrsz-  = DecodedToken (n+hdrsz) $ Fits $-      T.decodeUtf8 (BS.unsafeTake n (BS.unsafeDrop hdrsz bs))-      -- TODO FIXME: utf8 validation--  -- if n > bound then slow path, multi-chunk-  | otherwise-  = DecodedToken hdrsz $ TooLong n-  where-    hdrsz = 5-    -- TODO FIXME: int overflow-    n = fromIntegral (eatTailWord64 bs)-- ------------------------------------------------------------------------------ -- Reading big integers --@@ -1883,7 +2294,7 @@ -- decoding a big int across an input buffer boundary ought to be rare, and -- allocating a new continuation closure isn't that expensive. -data BigIntToken a = BigIntToken Integer+data BigIntToken a = BigIntToken Bool {- canonical? -} Integer                    | BigUIntNeedBody Int                    | BigNIntNeedBody Int                    | BigUIntNeedHeader@@ -1930,7 +2341,8 @@     , BS.length bs' >= tokenSize hdr     = case tryConsumeBytes hdr bs' of         DecodeFailure                 -> DecodeFailure-        DecodedToken sz (Fits bstr)   -> DecodedToken (1+sz) (BigIntToken (uintegerFromBytes bstr))+        DecodedToken sz (Fits bstr)   -> DecodedToken (1+sz)+          (BigIntToken (isBigIntRepCanonical bstr) (uintegerFromBytes bstr))         DecodedToken sz (TooLong len) -> DecodedToken (1+sz) (BigUIntNeedBody len)      | otherwise@@ -1945,8 +2357,16 @@     , BS.length bs' >= tokenSize hdr     = case tryConsumeBytes hdr bs' of         DecodeFailure                 -> DecodeFailure-        DecodedToken sz (Fits bstr)   -> DecodedToken (1+sz) (BigIntToken (nintegerFromBytes bstr))+        DecodedToken sz (Fits bstr)   -> DecodedToken (1+sz)+          (BigIntToken (isBigIntRepCanonical bstr) (nintegerFromBytes bstr))         DecodedToken sz (TooLong len) -> DecodedToken (1+sz) (BigNIntNeedBody len)      | otherwise     = DecodedToken 1 BigNIntNeedHeader++-- Binary representation of a big integer is canonical if it's at least 9 bytes+-- long (as for smaller values the canonical representation is the same one as+-- for Int) and the leading byte is not zero (meaning that it's the smallest+-- representation for the number in question).+isBigIntRepCanonical :: ByteString -> Bool+isBigIntRepCanonical bstr = BS.length bstr > 8 && BS.unsafeHead bstr /= 0x00
src/Codec/CBOR/Write.hs view
@@ -50,6 +50,7 @@ #endif #endif +import qualified Codec.CBOR.ByteArray.Sliced           as BAS import           Codec.CBOR.Encoding import           Codec.CBOR.Magic @@ -93,7 +94,9 @@                TkBytes    x vs' -> BI.runBuilderWith (bytesMP  x) (step vs' k) (BI.BufferRange op ope0)               TkBytesBegin vs' -> PI.runB bytesBeginMP  () op >>= go vs'+              TkByteArray x vs' -> BI.runBuilderWith (byteArrayMP x) (step vs' k) (BI.BufferRange op ope0) +              TkUtf8ByteArray x vs' -> BI.runBuilderWith (utf8ByteArrayMP x) (step vs' k) (BI.BufferRange op ope0)               TkString   x vs' -> BI.runBuilderWith (stringMP x) (step vs' k) (BI.BufferRange op ope0)               TkStringBegin vs'-> PI.runB stringBeginMP () op >>= go vs' @@ -351,6 +354,10 @@     condB (<= 0xffff)     (fromIntegral >$< withConstHeader 0x59 P.word16BE) $     condB (<= 0xffffffff) (fromIntegral >$< withConstHeader 0x5a P.word32BE) $                           (fromIntegral >$< withConstHeader 0x5b P.word64BE)+byteArrayMP :: BAS.SlicedByteArray -> B.Builder+byteArrayMP ba =+    P.primBounded bytesLenMP n <> BAS.toBuilder ba+  where n = fromIntegral $ BAS.sizeofSlicedByteArray ba  bytesBeginMP :: P.BoundedPrim () bytesBeginMP = constHeader 0x5f@@ -388,6 +395,12 @@ stringBeginMP :: P.BoundedPrim () stringBeginMP = constHeader 0x7f +utf8ByteArrayMP :: BAS.SlicedByteArray -> B.Builder+utf8ByteArrayMP t =+    P.primBounded stringLenMP n <> BAS.toBuilder t+  where+    n = fromIntegral $ BAS.sizeofSlicedByteArray t+ {-    Major type 4:  an array of data items.  Arrays are also called lists,       sequences, or tuples.  The array's length follows the rules for@@ -518,15 +531,22 @@ undefMP :: P.BoundedPrim () undefMP = constHeader 0xf7 +-- Canonical encoding of a NaN as per RFC 7049, section 3.9.+canonicalNaN :: PI.BoundedPrim a+canonicalNaN = P.liftFixedToBounded $ const (0xf9, (0x7e, 0x00))+                                   >$< P.word8 >*< P.word8 >*< P.word8+ halfMP :: P.BoundedPrim Float-halfMP = floatToWord16 >$<-         withConstHeader 0xf9 P.word16BE+halfMP = condB isNaN canonicalNaN+                     (floatToWord16 >$< withConstHeader 0xf9 P.word16BE)  floatMP :: P.BoundedPrim Float-floatMP = withConstHeader 0xfa P.floatBE+floatMP = condB isNaN canonicalNaN+                      (withConstHeader 0xfa P.floatBE)  doubleMP :: P.BoundedPrim Double-doubleMP = withConstHeader 0xfb P.doubleBE+doubleMP = condB isNaN canonicalNaN+                       (withConstHeader 0xfb P.doubleBE)  breakMP :: P.BoundedPrim () breakMP = constHeader 0xff