packages feed

avro 0.3.5.0 → 0.3.5.1

raw patch · 7 files changed

+277/−120 lines, 7 filesPVP: minor bump suggested

API additions: PVP suggests at least a minor version bump

API changes (from Hackage documentation)

+ Data.Avro.Decode.Lazy: getContainerValuesBytes :: ByteString -> Either String (Schema, [Either String ByteString])
+ Data.Avro.Decode.Lazy: getContainerValuesBytes' :: ByteString -> Either String (Schema, [Either String (Value Type, ByteString)])
+ Data.Avro.Decode.Strict.Internal: getAvroOf :: Schema -> Get (Value Type)

Files

avro.cabal view
@@ -2,10 +2,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 83c35a316bced7d77c45fea0d0d8b228cdff0c26f57a07eedb698798addc4697+-- hash: df77e046e65bd987123d33da69b0e843f877bd76dea4406b0e1fd22d9fd5a8f8  name:           avro-version:        0.3.5.0+version:        0.3.5.1 synopsis:       Avro serialization support for Haskell description:    Avro serialization and deserialization support for Haskell category:       Data@@ -56,6 +56,8 @@       Data.Avro.Decode.Lazy.Convert       Data.Avro.Decode.Lazy.Deconflict       Data.Avro.Decode.Lazy.LazyValue+      Data.Avro.Decode.Strict+      Data.Avro.Decode.Strict.Internal       Data.Avro.DecodeRaw       Data.Avro.Deconflict       Data.Avro.Deriving@@ -122,6 +124,7 @@       Avro.Codec.ZigZagSpec       Avro.Decode.Lazy.ContainerSpec       Avro.Decode.Lazy.RawBlocksSpec+      Avro.Decode.Lazy.RawValuesSpec       Avro.Decode.Lazy.ValuesSpec       Avro.Deconflict.Reader       Avro.Deconflict.Writer
src/Data/Avro.hs view
@@ -88,34 +88,35 @@   , schemaOf   ) where -import           Control.Arrow           (first)-import qualified Data.Avro.Decode        as D-import           Data.Avro.Deconflict    as C-import qualified Data.Avro.Encode        as E-import           Data.Avro.Schema        as S-import           Data.Avro.Types         as T-import qualified Data.Binary.Get         as G-import qualified Data.Binary.Put         as P-import qualified Data.ByteString         as B-import           Data.ByteString.Lazy    (ByteString)-import qualified Data.ByteString.Lazy    as BL-import           Data.Foldable           (toList)-import qualified Data.HashMap.Strict     as HashMap+import           Control.Arrow         (first)+import qualified Data.Avro.Decode      as D+import qualified Data.Avro.Decode.Lazy as DL+import           Data.Avro.Deconflict  as C+import qualified Data.Avro.Encode      as E+import           Data.Avro.Schema      as S+import           Data.Avro.Types       as T+import qualified Data.Binary.Get       as G+import qualified Data.Binary.Put       as P+import qualified Data.ByteString       as B+import           Data.ByteString.Lazy  (ByteString)+import qualified Data.ByteString.Lazy  as BL+import           Data.Foldable         (toList)+import qualified Data.HashMap.Strict   as HashMap import           Data.Int-import           Data.List.NonEmpty      (NonEmpty (..))-import qualified Data.Map                as Map-import           Data.Monoid             ((<>))+import           Data.List.NonEmpty    (NonEmpty (..))+import qualified Data.Map              as Map+import           Data.Monoid           ((<>)) import           Data.Tagged-import           Data.Text               (Text)-import qualified Data.Text               as Text-import qualified Data.Text.Lazy          as TL-import qualified Data.Vector             as V+import           Data.Text             (Text)+import qualified Data.Text             as Text+import qualified Data.Text.Lazy        as TL+import qualified Data.Vector           as V import           Data.Word-import           Prelude                 as P+import           Prelude               as P -import           Data.Avro.FromAvro-import           Data.Avro.HasAvroSchema-import           Data.Avro.ToAvro+import Data.Avro.FromAvro+import Data.Avro.HasAvroSchema+import Data.Avro.ToAvro  type Avro a = (FromAvro a, ToAvro a) 
src/Data/Avro/Decode.hs view
@@ -41,10 +41,12 @@  import           Data.Avro.Decode.Get import           Data.Avro.DecodeRaw-import           Data.Avro.Schema           as S-import qualified Data.Avro.Types            as T+import           Data.Avro.Schema     as S+import qualified Data.Avro.Types      as T import           Data.Avro.Zag +import Data.Avro.Decode.Strict.Internal+ -- |Decode bytes into a 'Value' as described by Schema. decodeAvro :: Schema -> BL.ByteString -> Either String (T.Value Type) decodeAvro sch = either (\(_,_,s) -> Left s) (\(_,_,a) -> Right a) . runGetOrFail (getAvroOf sch)@@ -92,60 +94,3 @@               | Just x <- code =                      fail ("Unrecognized codec: " <> BC.unpack x)               | otherwise = return return--{-# INLINABLE getAvroOf #-}-getAvroOf :: Schema -> Get (T.Value Type)-getAvroOf ty0 = go ty0- where- env = S.buildTypeEnvironment envFail ty0- envFail t = fail $ "Named type not in schema: " <> show t-- go :: Type -> Get (T.Value Type)- go ty =-  case ty of-    Null    -> return T.Null-    Boolean -> T.Boolean <$> getAvro-    Int     -> T.Int     <$> getAvro-    Long    -> T.Long    <$> getAvro-    Float   -> T.Float   <$> getAvro-    Double  -> T.Double  <$> getAvro-    Bytes   -> T.Bytes   <$> getAvro-    String  -> T.String  <$> getAvro-    Array t ->-      do vals <- getBlocksOf t-         return $ T.Array (V.fromList $ mconcat vals)-    Map  t  ->-      do kvs <- getKVBlocks t-         return $ T.Map (HashMap.fromList $ mconcat kvs)-    NamedType tn -> env tn >>= go-    Record {..} ->-      do let getField Field {..} = (fldName,) <$> go fldType-         T.Record ty . HashMap.fromList <$> mapM getField fields-    Enum {..} ->-      do val <- getLong-         let sym = fromMaybe "" (symbolLookup val) -- empty string for 'missing' symbols (alternative is an error or exception)-         pure (T.Enum ty (fromIntegral val) sym)-    Union ts unionLookup ->-      do i <- getLong-         case unionLookup i of-          Nothing -> fail $ "Decoded Avro tag is outside the expected range for a Union. Tag: " <> show i <> " union of: " <> show (P.map typeName $ NE.toList ts)-          Just t  -> T.Union ts t <$> go t-    Fixed {..} -> T.Fixed ty <$> G.getByteString (fromIntegral size)-- getKVBlocks :: Type -> Get [[(Text,T.Value Type)]]- getKVBlocks t =-  do blockLength <- abs <$> getLong-     if blockLength == 0-      then return []-      else do vs <- replicateM (fromIntegral blockLength) ((,) <$> getString <*> go t)-              (vs:) <$> getKVBlocks t- {-# INLINE getKVBlocks #-}-- getBlocksOf :: Type -> Get [[T.Value Type]]- getBlocksOf t =-  do blockLength <- abs <$> getLong-     if blockLength == 0-      then return []-      else do vs <- replicateM (fromIntegral blockLength) (go t)-              (vs:) <$> getBlocksOf t- {-# INLINE getBlocksOf #-}
src/Data/Avro/Decode/Lazy.hs view
@@ -19,50 +19,55 @@   -- * Lower level interface   , getContainerValues   , getContainerValuesWith+  , getContainerValuesBytes+  , getContainerValuesBytes'   , getAvroOf   , GetAvro(..)   ) where -import qualified Codec.Compression.Zlib           as Z-import           Control.Monad                    (foldM, replicateM, when)-import qualified Data.Aeson                       as A-import qualified Data.Array                       as Array-import           Data.Binary.Get                  (Get, runGetOrFail)-import qualified Data.Binary.Get                  as G-import           Data.Binary.IEEE754              as IEEE+import qualified Codec.Compression.Zlib     as Z+import           Control.Monad              (foldM, replicateM, when)+import qualified Data.Aeson                 as A+import qualified Data.Array                 as Array+import           Data.Binary.Get            (Get, runGetOrFail)+import qualified Data.Binary.Get            as G+import           Data.Binary.IEEE754        as IEEE import           Data.Bits-import           Data.ByteString                  (ByteString)-import qualified Data.ByteString.Lazy             as BL-import qualified Data.ByteString.Lazy.Char8       as BL-import           Data.Either                      (isRight)-import qualified Data.HashMap.Strict              as HashMap+import           Data.ByteString            (ByteString)+import qualified Data.ByteString.Lazy       as BL+import qualified Data.ByteString.Lazy.Char8 as BL+import           Data.Either                (isRight)+import qualified Data.HashMap.Strict        as HashMap import           Data.Int-import           Data.List                        (foldl', unfoldr)-import qualified Data.List.NonEmpty               as NE-import qualified Data.Map                         as Map+import           Data.List                  (foldl', unfoldr)+import qualified Data.List.NonEmpty         as NE+import qualified Data.Map                   as Map import           Data.Maybe-import           Data.Monoid                      ((<>))-import qualified Data.Set                         as Set-import           Data.Tagged                      (Tagged, untag)-import           Data.Text                        (Text)-import qualified Data.Text                        as Text-import qualified Data.Text.Encoding               as Text-import qualified Data.Vector                      as V-import           Prelude                          as P+import           Data.Monoid                ((<>))+import qualified Data.Set                   as Set+import           Data.Tagged                (Tagged, untag)+import           Data.Text                  (Text)+import qualified Data.Text                  as Text+import qualified Data.Text.Encoding         as Text+import qualified Data.Vector                as V+import           Prelude                    as P -import qualified Data.Avro.Decode.Lazy.LazyValue  as T+import qualified Data.Avro.Decode.Lazy.LazyValue as T import           Data.Avro.DecodeRaw-import           Data.Avro.HasAvroSchema          (schema)-import           Data.Avro.Schema                 as S+import           Data.Avro.HasAvroSchema         (schema)+import           Data.Avro.Schema                as S+import qualified Data.Avro.Types                 as TypesStrict import           Data.Avro.Zag -import           Data.Avro.Decode.Get-import           Data.Avro.Decode.Lazy.Convert    (toStrictValue)-import           Data.Avro.Decode.Lazy.Deconflict as C-import           Data.Avro.FromAvro+import qualified Data.Avro.Decode.Strict.Internal as DecodeStrict -import           Debug.Trace+import Data.Avro.Decode.Get+import Data.Avro.Decode.Lazy.Convert    (toStrictValue)+import Data.Avro.Decode.Lazy.Deconflict as C+import Data.Avro.FromAvro +import Debug.Trace+ -- | Decodes the container as a lazy list of values of the requested type. -- -- The schema for the requested type will be de-conflicted with the schema@@ -167,8 +172,8 @@     next syncBytes decompress bytes =       case getNextBlock syncBytes decompress bytes of         Right (Just (numObj, block, rest)) -> Just (Right (numObj, block), Just rest)-        Right Nothing                 -> Nothing-        Left err                      -> Just (Left err, Nothing)+        Right Nothing                      -> Nothing+        Left err                           -> Just (Left err, Nothing)  getNextBlock :: BL.ByteString              -> (BL.ByteString -> Get BL.ByteString)@@ -195,7 +200,7 @@     checkMarker sync bs =       case BL.splitAt nrSyncBytes bs of         (marker, _) | marker /= sync -> Left "Invalid marker, does not match sync bytes."-        (_, rest) -> Right rest+        (_, rest)   -> Right rest  getContainerValuesWith :: (Schema -> BL.ByteString -> (BL.ByteString, T.LazyValue Type))                  -> BL.ByteString@@ -218,6 +223,50 @@   in either (\(rest,_,s) -> (rest, T.Error s)) (\(rest,_,a) -> (rest, a)) res {-# INLINE decodeGet #-} +-- | Splits container into a list of individual avro-encoded values.+--+-- This is particularly useful when slicing up containers into one or more+-- smaller files.  By extracting the original bytestring it is possible to+-- avoid re-encoding data.+getContainerValuesBytes :: BL.ByteString -> Either String (Schema, [Either String BL.ByteString])+getContainerValuesBytes =+  extractContainerValues readBytes+  where+    readBytes sch = do+      start <- G.bytesRead+      end <- G.lookAhead (DecodeStrict.getAvroOf sch >> G.bytesRead)+      G.getLazyByteString (end-start)++-- | Splits container into a list of individual avro-encoded values.+-- This version provides both encoded and decoded values.+--+-- This is particularly useful when slicing up containers into one or more+-- smaller files.  By extracting the original bytestring it is possible to+-- avoid re-encoding data.+getContainerValuesBytes' :: BL.ByteString -> Either String (Schema, [Either String (TypesStrict.Value S.Type, BL.ByteString)])+getContainerValuesBytes' =+  extractContainerValues readBytes+  where+    readBytes sch = do+      start <- G.bytesRead+      (val, end) <- G.lookAhead (DecodeStrict.getAvroOf sch >>= (\v -> (v, ) <$> G.bytesRead))+      res <- G.getLazyByteString (end-start)+      pure (val, res)++extractContainerValues :: (Schema -> Get a) -> BL.ByteString -> Either String (Schema, [Either String a])+extractContainerValues f bs =+  case decodeRawBlocks bs of+    Left err            -> Left err+    Right (sch, blocks) -> Right (sch, blocks >>= decodeBlock sch)+  where+    decodeBlock _ (Left err)               = undefined+    decodeBlock sch (Right (nrObj, bytes)) = snd $ consumeN (fromIntegral nrObj) (decodeValue sch) bytes++    decodeValue sch bytes =+      case G.runGetOrFail (f sch) bytes of+        Left (bs', _, err)  -> (bs', Left err)+        Right (bs', _, res) -> (bs', Right res)+ consumeN :: Int64 -> (a -> (a, b)) -> a -> (a, [b]) consumeN n f a =   if n == 0@@ -262,7 +311,7 @@           Left (bs', _, err) -> (bs', T.Error err)           Right (bs', _, i)  ->             case symbolLookup i of-              Nothing -> (bs', T.Error ("Unknown value {" <> show i <> "} for enum " <> Text.unpack (typeName ty) ))+              Nothing  -> (bs', T.Error ("Unknown value {" <> show i <> "} for enum " <> Text.unpack (typeName ty) ))               Just sym -> (bs', T.Enum ty (fromIntegral i) sym)        Union ts unionLookup ->
+ src/Data/Avro/Decode/Strict.hs view
@@ -0,0 +1,3 @@+module Data.Avro.Decode.Strict+where+
+ src/Data/Avro/Decode/Strict/Internal.hs view
@@ -0,0 +1,96 @@+{-# LANGUAGE FlexibleInstances   #-}+{-# LANGUAGE MultiWayIf          #-}+{-# LANGUAGE OverloadedStrings   #-}+{-# LANGUAGE RecordWildCards     #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TupleSections       #-}+module Data.Avro.Decode.Strict.Internal+where++import qualified Codec.Compression.Zlib     as Z+import           Control.Monad              (replicateM, when)+import qualified Data.Aeson                 as A+import qualified Data.Array                 as Array+import           Data.Binary.Get            (Get, runGetOrFail)+import qualified Data.Binary.Get            as G+import           Data.Binary.IEEE754        as IEEE+import           Data.Bits+import           Data.ByteString            (ByteString)+import qualified Data.ByteString.Lazy       as BL+import qualified Data.ByteString.Lazy.Char8 as BC+import qualified Data.HashMap.Strict        as HashMap+import           Data.Int+import           Data.List                  (foldl')+import qualified Data.List.NonEmpty         as NE+import qualified Data.Map                   as Map+import           Data.Maybe+import           Data.Monoid                ((<>))+import qualified Data.Set                   as Set+import           Data.Text                  (Text)+import qualified Data.Text                  as Text+import qualified Data.Text.Encoding         as Text+import qualified Data.Vector                as V+import           Prelude                    as P++import           Data.Avro.Decode.Get+import           Data.Avro.DecodeRaw+import           Data.Avro.Schema     as S+import qualified Data.Avro.Types      as T+import           Data.Avro.Zag++{-# INLINABLE getAvroOf #-}+getAvroOf :: Schema -> Get (T.Value Type)+getAvroOf ty0 = go ty0+ where+ env = S.buildTypeEnvironment envFail ty0+ envFail t = fail $ "Named type not in schema: " <> show t++ go :: Type -> Get (T.Value Type)+ go ty =+  case ty of+    Null    -> return T.Null+    Boolean -> T.Boolean <$> getAvro+    Int     -> T.Int     <$> getAvro+    Long    -> T.Long    <$> getAvro+    Float   -> T.Float   <$> getAvro+    Double  -> T.Double  <$> getAvro+    Bytes   -> T.Bytes   <$> getAvro+    String  -> T.String  <$> getAvro+    Array t ->+      do vals <- getBlocksOf t+         return $ T.Array (V.fromList $ mconcat vals)+    Map  t  ->+      do kvs <- getKVBlocks t+         return $ T.Map (HashMap.fromList $ mconcat kvs)+    NamedType tn -> env tn >>= go+    Record {..} ->+      do let getField Field {..} = (fldName,) <$> go fldType+         T.Record ty . HashMap.fromList <$> mapM getField fields+    Enum {..} ->+      do val <- getLong+         let sym = fromMaybe "" (symbolLookup val) -- empty string for 'missing' symbols (alternative is an error or exception)+         pure (T.Enum ty (fromIntegral val) sym)+    Union ts unionLookup ->+      do i <- getLong+         case unionLookup i of+          Nothing -> fail $ "Decoded Avro tag is outside the expected range for a Union. Tag: " <> show i <> " union of: " <> show (P.map typeName $ NE.toList ts)+          Just t  -> T.Union ts t <$> go t+    Fixed {..} -> T.Fixed ty <$> G.getByteString (fromIntegral size)++ getKVBlocks :: Type -> Get [[(Text,T.Value Type)]]+ getKVBlocks t =+  do blockLength <- abs <$> getLong+     if blockLength == 0+      then return []+      else do vs <- replicateM (fromIntegral blockLength) ((,) <$> getString <*> go t)+              (vs:) <$> getKVBlocks t+ {-# INLINE getKVBlocks #-}++ getBlocksOf :: Type -> Get [[T.Value Type]]+ getBlocksOf t =+  do blockLength <- abs <$> getLong+     if blockLength == 0+      then return []+      else do vs <- replicateM (fromIntegral blockLength) (go t)+              (vs:) <$> getBlocksOf t+ {-# INLINE getBlocksOf #-}
+ test/Avro/Decode/Lazy/RawValuesSpec.hs view
@@ -0,0 +1,60 @@+{-# LANGUAGE DeriveGeneric       #-}+{-# LANGUAGE OverloadedStrings   #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell     #-}+module Avro.Decode.Lazy.RawValuesSpec+where++import Data.Avro                     as A+import Data.Avro.Decode.Lazy         as DL+import Data.Avro.Decode.Lazy.Convert as TC+import Data.Avro.Deriving+import Data.Avro.Encode              (packContainerBlocks, packContainerValues)+import Data.Avro.Schema              (resultToEither)+import Data.Either                   (isLeft, isRight, rights)+import Data.List                     (unfoldr)+import Data.Semigroup                ((<>))+import Data.Text                     (pack)++import Test.Hspec++{-# ANN module ("HLint: ignore Redundant do"        :: String) #-}++deriveAvro "test/data/small.avsc"++spec :: Spec+spec = describe "Avro.Decode.Lazy.RawValuesSpec" $ do++  it "should decode empty container" $ do+    empty <- A.encodeContainer ([] :: [[Endpoint]])+    DL.getContainerValuesBytes empty `shouldBe` Right (schema'Endpoint, [])++  it "should decode container with one block" $ do+    let msgs = mkEndpoint <$> [1, 2]+    container <- A.encodeContainer [msgs]+    let Right (sch, vals) = DL.getContainerValuesBytes container+    sch `shouldBe` schema'Endpoint+    let results = resultToEither . A.decode <$> rights vals+    rights results `shouldBe` msgs+++  it "should decode container with multiple blocks" $ do+    let msgs = mkEndpoint <$> [1..19]+    container <- A.encodeContainer (chunksOf 4 msgs)+    let Right (sch, vals) = DL.getContainerValuesBytes container+    sch `shouldBe` schema'Endpoint+    let results = resultToEither . A.decode <$> rights vals+    rights results `shouldBe` msgs++mkEndpoint :: Int -> Endpoint+mkEndpoint i =+  Endpoint+    { endpointIps         = ["192.168.1." <> pack (show i), "127.0.0." <> pack (show i)]+    , endpointPorts       = [PortRange 1 10, PortRange 11 20]+    , endpointOpaque      = Opaque "16-b-long-string"+    , endpointCorrelation = Opaque "opaq-correlation"+    , endpointTag         = Left (fromIntegral i)+    }++chunksOf :: Int -> [a] -> [[a]]+chunksOf n = takeWhile (not.null) . unfoldr (Just . splitAt n)