buffer-builder 0.2.4.0 → 0.2.4.1
raw patch · 8 files changed
+103/−104 lines, 8 filesdep +HTFdep −HUnitdep −tastydep −tasty-hunitPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: HTF
Dependencies removed: HUnit, tasty, tasty-hunit, tasty-quickcheck, tasty-th
API changes (from Hackage documentation)
- Data.BufferBuilder: initialCapacity :: Options -> !Int
- Data.BufferBuilder: instance Applicative BufferBuilder
- Data.BufferBuilder: instance Exception BufferOutOfMemoryError
- Data.BufferBuilder: instance Functor BufferBuilder
- Data.BufferBuilder: instance Monad BufferBuilder
- Data.BufferBuilder: instance Show BufferOutOfMemoryError
- Data.BufferBuilder: instance Typeable BufferOutOfMemoryError
- Data.BufferBuilder: trimFinalBuffer :: Options -> !Bool
- Data.BufferBuilder.Json: instance (Prim a, ToJson a) => ToJson (Vector a)
- Data.BufferBuilder.Json: instance (Storable a, ToJson a) => ToJson (Vector a)
- Data.BufferBuilder.Json: instance (ToJsonString k, ToJson v) => ToJson (HashMap k v)
- Data.BufferBuilder.Json: instance (Vector Vector a, ToJson a) => ToJson (Vector a)
- Data.BufferBuilder.Json: instance Monoid ObjectBuilder
- Data.BufferBuilder.Json: instance ToJson Bool
- Data.BufferBuilder.Json: instance ToJson Double
- Data.BufferBuilder.Json: instance ToJson Int
- Data.BufferBuilder.Json: instance ToJson JsonString
- Data.BufferBuilder.Json: instance ToJson ObjectBuilder
- Data.BufferBuilder.Json: instance ToJson Text
- Data.BufferBuilder.Json: instance ToJson Value
- Data.BufferBuilder.Json: instance ToJson a => ToJson (Maybe a)
- Data.BufferBuilder.Json: instance ToJson a => ToJson (Vector a)
- Data.BufferBuilder.Json: instance ToJson a => ToJson [a]
- Data.BufferBuilder.Json: instance ToJsonString JsonString
- Data.BufferBuilder.Json: instance ToJsonString Text
- Data.BufferBuilder.Utf8: instance Applicative Utf8Builder
- Data.BufferBuilder.Utf8: instance Functor Utf8Builder
- Data.BufferBuilder.Utf8: instance Monad Utf8Builder
+ Data.BufferBuilder: [initialCapacity] :: Options -> !Int
+ Data.BufferBuilder: [trimFinalBuffer] :: Options -> !Bool
+ Data.BufferBuilder: instance GHC.Base.Applicative Data.BufferBuilder.BufferBuilder
+ Data.BufferBuilder: instance GHC.Base.Functor Data.BufferBuilder.BufferBuilder
+ Data.BufferBuilder: instance GHC.Base.Monad Data.BufferBuilder.BufferBuilder
+ Data.BufferBuilder: instance GHC.Exception.Exception Data.BufferBuilder.BufferOutOfMemoryError
+ Data.BufferBuilder: instance GHC.Show.Show Data.BufferBuilder.BufferOutOfMemoryError
+ Data.BufferBuilder.Json: instance (Data.BufferBuilder.Json.ToJsonString k, Data.BufferBuilder.Json.ToJson v) => Data.BufferBuilder.Json.ToJson (Data.HashMap.Base.HashMap k v)
+ Data.BufferBuilder.Json: instance (Data.Primitive.Types.Prim a, Data.BufferBuilder.Json.ToJson a) => Data.BufferBuilder.Json.ToJson (Data.Vector.Primitive.Vector a)
+ Data.BufferBuilder.Json: instance (Data.Vector.Generic.Base.Vector Data.Vector.Unboxed.Base.Vector a, Data.BufferBuilder.Json.ToJson a) => Data.BufferBuilder.Json.ToJson (Data.Vector.Unboxed.Base.Vector a)
+ Data.BufferBuilder.Json: instance (Foreign.Storable.Storable a, Data.BufferBuilder.Json.ToJson a) => Data.BufferBuilder.Json.ToJson (Data.Vector.Storable.Vector a)
+ Data.BufferBuilder.Json: instance Data.BufferBuilder.Json.ToJson Data.BufferBuilder.Json.JsonString
+ Data.BufferBuilder.Json: instance Data.BufferBuilder.Json.ToJson Data.BufferBuilder.Json.ObjectBuilder
+ Data.BufferBuilder.Json: instance Data.BufferBuilder.Json.ToJson Data.BufferBuilder.Json.Value
+ Data.BufferBuilder.Json: instance Data.BufferBuilder.Json.ToJson Data.Text.Internal.Text
+ Data.BufferBuilder.Json: instance Data.BufferBuilder.Json.ToJson GHC.Types.Bool
+ Data.BufferBuilder.Json: instance Data.BufferBuilder.Json.ToJson GHC.Types.Double
+ Data.BufferBuilder.Json: instance Data.BufferBuilder.Json.ToJson GHC.Types.Int
+ Data.BufferBuilder.Json: instance Data.BufferBuilder.Json.ToJson a => Data.BufferBuilder.Json.ToJson (Data.Vector.Vector a)
+ Data.BufferBuilder.Json: instance Data.BufferBuilder.Json.ToJson a => Data.BufferBuilder.Json.ToJson (GHC.Base.Maybe a)
+ Data.BufferBuilder.Json: instance Data.BufferBuilder.Json.ToJson a => Data.BufferBuilder.Json.ToJson [a]
+ Data.BufferBuilder.Json: instance Data.BufferBuilder.Json.ToJsonString Data.BufferBuilder.Json.JsonString
+ Data.BufferBuilder.Json: instance Data.BufferBuilder.Json.ToJsonString Data.Text.Internal.Text
+ Data.BufferBuilder.Json: instance GHC.Base.Monoid Data.BufferBuilder.Json.ObjectBuilder
+ Data.BufferBuilder.Utf8: instance GHC.Base.Applicative Data.BufferBuilder.Utf8.Utf8Builder
+ Data.BufferBuilder.Utf8: instance GHC.Base.Functor Data.BufferBuilder.Utf8.Utf8Builder
+ Data.BufferBuilder.Utf8: instance GHC.Base.Monad Data.BufferBuilder.Utf8.Utf8Builder
Files
- bench/JsonBench.hs +5/−4
- buffer-builder.cabal +2/−7
- changelog.md +4/−0
- src/Data/BufferBuilder.hs +1/−1
- test/BufferTest.hs +52/−35
- test/JsonTest.hs +22/−29
- test/Main.hs +7/−12
- test/Utf8Test.hs +10/−16
bench/JsonBench.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings, RecordWildCards #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE MagicHash #-}+{-# LANGUAGE MagicHash, DeriveGeneric #-} module Main (main) where @@ -10,6 +10,7 @@ import Data.Aeson ((.:)) import Data.Monoid ((<>)) import Data.Text (Text)+import GHC.Generics (Generic) import qualified Data.Aeson as Aeson import qualified Data.BufferBuilder.Json as Json import qualified Data.ByteString as BS@@ -19,11 +20,11 @@ import qualified Data.Vector.Unboxed as UnboxedVector data EyeColor = Green | Blue | Brown- deriving (Eq, Show)+ deriving (Eq, Show, Generic) data Gender = Male | Female- deriving (Eq, Show)+ deriving (Eq, Show, Generic) data Fruit = Apple | Strawberry | Banana- deriving (Eq, Show)+ deriving (Eq, Show, Generic) data Friend = Friend { fId :: !Int , fName :: !Text
buffer-builder.cabal view
@@ -1,5 +1,5 @@ name: buffer-builder-version: 0.2.4.0+version: 0.2.4.1 synopsis: Library for efficiently building up buffers, one piece at a time description: @@ -81,13 +81,8 @@ build-depends: base ==4.* , buffer-builder , text- , tasty- , tasty-hunit- , tasty-th- , tasty-quickcheck , quickcheck-instances- , HUnit- , text+ , HTF , vector , bytestring , attoparsec
changelog.md view
@@ -1,3 +1,7 @@+0.2.4.1++* Fix a bug with appendEscapedJsonText and Text buffers with nonzero offsets. (Thanks Joe Lee!)+ 0.2.4.0 * Add the ability to calculate the output length of a BufferBuilder without allocating or writing bytes
src/Data/BufferBuilder.hs view
@@ -352,7 +352,7 @@ appendEscapedJsonText !(Text arr ofs len) = let byteArray = aBA arr in withHandle $ \h ->- bw_append_json_escaped_utf16 h len (Ptr (byteArrayContents# byteArray) `plusPtr` ofs)+ bw_append_json_escaped_utf16 h len (Ptr (byteArrayContents# byteArray) `plusPtr` (2 * ofs)) {-# INLINE appendEscapedJsonText #-} -- | Append a percent-encoded ByteString. All characters except for
test/BufferTest.hs view
@@ -1,73 +1,96 @@+{-# OPTIONS_GHC -F -pgmF htfpp #-} {-# LANGUAGE MagicHash, OverloadedStrings, TemplateHaskell, FlexibleInstances #-} {-# OPTIONS_GHC -fno-warn-orphans #-} -module BufferTest (tests, main) where+module BufferTest (htf_thisModulesTests) where +import Test.Framework+ import qualified Data.ByteString.Char8 as BSC-import Test.Tasty-import Test.Tasty.TH-import Test.Tasty.QuickCheck-import Test.Tasty.HUnit import Data.BufferBuilder import Test.QuickCheck.Instances () -case_append_bytes :: Assertion-case_append_bytes = do+import qualified Data.Text as T+import qualified Data.Text.Internal as TI++getTextOffset :: T.Text -> Int+getTextOffset (TI.Text _array offset _length) = offset++test_append_bytes :: IO ()+test_append_bytes = do let result = runBufferBuilder $ do appendChar8 'f' appendChar8 'o' appendChar8 'o'- assertEqual "matches" "foo" result+ assertEqual "foo" result -case_append_string :: Assertion-case_append_string = do+test_append_string :: IO ()+test_append_string = do let result = runBufferBuilder $ do appendChar8 'f' appendChar8 'o' appendChar8 'o' appendBS "bar"- assertEqual "matches" "foobar" result+ assertEqual "foobar" result -case_append_literals :: Assertion-case_append_literals = do+test_append_json_escaped_text_with_zero_offset :: IO ()+test_append_json_escaped_text_with_zero_offset = do+ let t = "foo"+ assertEqual 0 (getTextOffset t) let result = runBufferBuilder $ do+ appendEscapedJsonText t+ appendBS "bar"+ assertEqual "\"foo\"bar" result++test_append_json_escaped_text_with_nonzero_offset :: IO ()+test_append_json_escaped_text_with_nonzero_offset = do+ let [_, t] = T.split (== '-') "some_prefix-foo"+ assertNotEqual 0 (getTextOffset t)+ let result = runBufferBuilder $ do+ appendEscapedJsonText t+ appendBS "bar"+ assertEqual "\"foo\"bar" result++test_append_literals :: IO ()+test_append_literals = do+ let result = runBufferBuilder $ do appendLiteral "foo"# appendLiteral "bar"#- assertEqual "matches" "foobar" result+ assertEqual "foobar" result -case_append_url_encoded_safe :: Assertion-case_append_url_encoded_safe = do+test_append_url_encoded_safe :: IO ()+test_append_url_encoded_safe = do let safe = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" let result = runBufferBuilder $ do appendUrlEncoded safe- assertEqual "matches" safe result+ assertEqual safe result -case_append_url_encoded_unsafe :: Assertion-case_append_url_encoded_unsafe = do+test_append_url_encoded_unsafe :: IO ()+test_append_url_encoded_unsafe = do let safe = "\0\1 /\\$%" let result = runBufferBuilder $ do appendUrlEncoded safe- assertEqual "matches" "%00%01%20%2f%5c%24%25" result+ assertEqual "%00%01%20%2f%5c%24%25" result -case_query_current_length :: Assertion-case_query_current_length = do+test_query_current_length :: IO ()+test_query_current_length = do let ((f, s), result) = runBufferBuilder' $ do appendBS "foo" first <- currentLength appendBS "bar" second <- currentLength return (first, second)- assertEqual "first length" 3 f- assertEqual "second length" 6 s- assertEqual "result" "foobar" result+ assertEqual 3 f+ assertEqual 6 s+ assertEqual "foobar" result -case_calculate_length :: Assertion-case_calculate_length = do+test_calculate_length :: IO ()+test_calculate_length = do let first = appendBS "foo" let second = first >> appendBS "bar" - assertEqual "first length" 3 $ calculateLength first- assertEqual "second length" 6 $ calculateLength second+ assertEqual 3 $ calculateLength first+ assertEqual 6 $ calculateLength second prop_match_intDec :: Int -> Bool prop_match_intDec i = runBufferBuilder (appendDecimalSignedInt i) == (BSC.pack $ show i)@@ -98,9 +121,3 @@ prop_length_calculation_matches_actual_length bb = let cc = bb >> return () in BSC.length (runBufferBuilder cc) == calculateLength cc--tests :: TestTree-tests = $(testGroupGenerator)--main :: IO ()-main = defaultMain tests
test/JsonTest.hs view
@@ -1,13 +1,12 @@+{-# OPTIONS_GHC -F -pgmF htfpp #-} {-# LANGUAGE MagicHash, OverloadedStrings, TemplateHaskell, ExistentialQuantification #-} -module JsonTest where+module JsonTest (htf_thisModulesTests) where +import Test.Framework+ import Data.Foldable (foldMap)-import Test.Tasty-import Test.Tasty.TH import qualified Data.Attoparsec.ByteString as Atto-import Test.Tasty.HUnit-import Test.Tasty.QuickCheck import Data.Monoid ((<>), Monoid (mconcat, mempty)) import Data.String (IsString (..)) import Data.BufferBuilder.Json@@ -19,28 +18,28 @@ import qualified Data.Text as Text import qualified Data.Vector as Vector -ae :: (IsString a, Show a, Eq a) => String -> a -> Assertion-ae expected actual = assertEqual expected (fromString expected) actual+ae :: (IsString a, Show a, Eq a) => String -> a -> IO ()+ae expected actual = assertEqual (fromString expected) actual -case_encode_int :: IO ()-case_encode_int = do+test_encode_int :: IO ()+test_encode_int = do ae "-42" (encodeJson (-42::Int)) ae "9" (encodeJson (9::Int)) -case_encode_bool :: IO ()-case_encode_bool = do+test_encode_bool :: IO ()+test_encode_bool = do ae "true" (encodeJson True) ae "false" (encodeJson False) -case_encode_text :: IO ()-case_encode_text = do+test_encode_text :: IO ()+test_encode_text = do ae "\"hello\"" (encodeJson ("hello" :: Text)) ae "\"\"" (encodeJson ("" :: Text)) ae "\"\\\"\\\\\\n\\r\\t\"" (encodeJson (fromString ['\"', '\\', '\n', '\r', '\t'] :: Text)) -case_encode_object :: IO ()-case_encode_object = do+test_encode_object :: IO ()+test_encode_object = do ae "{\"key\":\"value\"}" (encodeJson ("key" .= ("value" :: Text))) ae "{\"key\":\"value\",\"key2\":[5,6,7]}" (encodeJson ("key" .= ("value" :: Text) <> "key2" .= ([5,6,7] :: [Int])))@@ -64,21 +63,21 @@ instance ToJsonString CustomThing where toJsonString = unsafeStringUtf8Builder . encodeCustomThing -case_encode_object_with_custom_typeclasses :: IO ()-case_encode_object_with_custom_typeclasses = do+test_encode_object_with_custom_typeclasses :: IO ()+test_encode_object_with_custom_typeclasses = do ae "{\"key\":\"value\"}" $ encodeJson $ ("key" :: Text) `row` ("value" :: Text) ae "{\"foo/bar/baz\":\"foo/bar/baz\"}" $ encodeJson $ CustomThing `row` CustomThing -case_monoid_laws :: IO ()-case_monoid_laws = do+test_monoid_laws :: IO ()+test_monoid_laws = do -- TODO QuickCheck let a = "key" .= ("value" :: Text) b = "key2" .= (999 :: Int) c = "key3" .= ([1,2,3] :: [Int])- assertEqual "Left identity" (encodeJson a) (encodeJson (mempty <> a))- assertEqual "Right identity" (encodeJson a) (encodeJson (a <> mempty))- assertEqual "Associativity" (encodeJson (a <> (b <> c))) (encodeJson ((a <> b) <> c))- assertEqual "mconcat" (encodeJson (mconcat [a, b, c])) (encodeJson (a <> b <> c))+ assertEqual {-Left identity-} (encodeJson a) (encodeJson (mempty <> a))+ assertEqual {-Right identity-} (encodeJson a) (encodeJson (a <> mempty))+ assertEqual {-Associativity-} (encodeJson (a <> (b <> c))) (encodeJson ((a <> b) <> c))+ assertEqual {-mconcat-} (encodeJson (mconcat [a, b, c])) (encodeJson (a <> b <> c)) data JsonValue = forall a. ToJson a => JsonValue a @@ -137,9 +136,3 @@ case decodeJsonFragment $ encodeJson jv :: Maybe Aeson.Value of Just _ -> True Nothing -> False--tests :: TestTree-tests = $(testGroupGenerator)--main :: IO ()-main = defaultMain tests
test/Main.hs view
@@ -1,17 +1,12 @@-{-# LANGUAGE TemplateHaskell, OverloadedStrings, MagicHash #-}+{-# OPTIONS_GHC -F -pgmF htfpp #-} -import Test.Tasty+module Main where -import qualified BufferTest-import qualified Utf8Test-import qualified JsonTest+import Test.Framework -allTests :: TestTree-allTests = testGroup "all tests"- [ BufferTest.tests- , Utf8Test.tests- , JsonTest.tests- ]+import {-@ HTF_TESTS @-} BufferTest+import {-@ HTF_TESTS @-} Utf8Test+import {-@ HTF_TESTS @-} JsonTest main :: IO ()-main = defaultMain allTests+main = htfMain htf_importedTests
test/Utf8Test.hs view
@@ -1,30 +1,24 @@+{-# OPTIONS_GHC -F -pgmF htfpp #-} {-# LANGUAGE MagicHash, OverloadedStrings, TemplateHaskell #-} -module Utf8Test (tests, main) where+module Utf8Test (htf_thisModulesTests) where -import Test.Tasty-import Test.Tasty.TH-import Test.Tasty.HUnit+import Test.Framework+ import Data.BufferBuilder.Utf8 import qualified Data.Text as Text import qualified Data.Text.Encoding as TE -case_append_string :: Assertion-case_append_string = do+test_append_string :: IO ()+test_append_string = do let str = "foo\NUL\1234" let bb = runUtf8Builder $ do appendString str- assertEqual "matches" (Text.pack str) $ TE.decodeUtf8 bb+ assertEqual (Text.pack str) $ TE.decodeUtf8 bb -case_append_chars :: Assertion-case_append_chars = do+test_append_chars :: IO ()+test_append_chars = do let bb = runUtf8Builder $ do appendChar '\NUL' appendChar '\x1234'- assertEqual "matches" (TE.encodeUtf8 "\NUL\x1234") bb--tests :: TestTree-tests = $(testGroupGenerator)--main :: IO ()-main = defaultMain tests+ assertEqual (TE.encodeUtf8 "\NUL\x1234") bb