git.iko.soy/rowtype-yoga/purescript-protobuf
on master help sign in
Parse repeated fields to arrays
commit0755558b22b20dd4c283beb1330ab199befda38e authorViktor Kronvall <viktor.kronvall@gmail.com> dateTue 18 May 2021, 02:11 (5 years ago) parent44635ae
5 files changed+263−101
src/Protobuf/Decode.js+13 src/Protobuf/Decode.purs+177−57 src/ProtocPlugin/Main.purs+6−6 test/Bench.purs+37−34 test/Main.purs+30−4
src/Protobuf/Decode.jsnew file+13
@@ -0,0 +1,13 @@
1+exports._decodeArray = function(f) {
2+ return function(n) {
3+ return function () {
4+ var a = new Array(n);
5+ var l = a.length;
6+ var i;
7+ for (i=0;i<l;i+=1) {
8+ a[i] = f(i)();
9+ }
10+ return a;
11+ };
12+ };
13+}
src/Protobuf/Decode.purs+177−57
@@ -3,39 +3,52 @@
33-- | You almost never need to import this module.
44-- | See package README for explanation.
55module Protobuf.Decode
6−( double
7−, float
8−, int32
9−, int64
10−, uint32
11−, uint64
12−, sint32
13−, sint64
14−, fixed32
15−, fixed64
16−, sfixed32
17−, sfixed64
18−, bool
19−, string
20−, bytes
21−, module Protobuf.Decode32
22−, module Protobuf.Decode64
23−) where
6+ ( double
7+ , doubleArray
8+ , float
9+ , floatArray
10+ , int32
11+ , int64
12+ , uint32
13+ , uint64
14+ , sint32
15+ , sint64
16+ , fixed32
17+ , fixed32Array
18+ , fixed64
19+ , fixed64Array
20+ , sfixed32
21+ , sfixed32Array
22+ , sfixed64
23+ , sfixed64Array
24+ , bool
25+ , string
26+ , bytes
27+ , module Protobuf.Decode32
28+ , module Protobuf.Decode64
29+ ) where
2430
2531import Prelude
26−
32+import Control.Apply (lift2)
33+import Control.Monad.Except (ExceptT(..))
34+import Control.Monad.State (StateT(..))
2735import Control.Monad.Trans.Class (lift)
2836import Data.ArrayBuffer.ArrayBuffer as AB
37+import Data.ArrayBuffer.DataView (AProxy(..), getFloat32le, getFloat64le, getInt32le, getUint32le)
2938import Data.ArrayBuffer.DataView as DV
3039import Data.ArrayBuffer.Typed as AT
31−import Data.ArrayBuffer.Types (DataView, Uint8Array)
40+import Data.ArrayBuffer.Types (ByteLength, ByteOffset, DataView, Uint8Array, kind ArrayViewType)
41+import Data.ArrayBuffer.Types as ArrayTypes
42+import Data.ArrayBuffer.ValueMapping (class BinaryValue, class BytesPerValue, class ShowArrayViewType)
3243import Data.Either (Either(..))
3344import Data.Float32 (Float32)
34−import Data.Int (Radix, radix, toStringAs)
3545import Data.Long.Internal (Long, Signed, Unsigned, fromLowHighBits, highBits, lowBits, unsignedToSigned)
36−import Data.Long.Internal as Long
37−import Data.Maybe (Maybe(..), fromJust)
46+import Data.Maybe (Maybe, fromJust)
47+import Data.String (joinWith)
48+import Data.Symbol (class IsSymbol, SProxy(..), reflectSymbol)
3849import Data.TextDecoding (decodeUtf8)
50+import Data.Tuple (Tuple(..))
51+import Data.Typelevel.Num (class Nat, D8, toInt')
3952import Data.UInt (UInt)
4053import Data.UInt as UInt
4154import Effect (Effect)
@@ -44,8 +57,10 @@ import Partial.Unsafe (unsafePartial)
4457import Protobuf.Common (Bytes(..))
4558import Protobuf.Decode32 (varint32, zigzag32, tag32)
4659import Protobuf.Decode64 (varint64, zigzag64)
47−import Text.Parsing.Parser (ParserT, fail)
60+import Text.Parsing.Parser (ParseError(..), ParseState(..), ParserT(..), fail)
4861import Text.Parsing.Parser.DataView as Parse
62+import Text.Parsing.Parser.Pos (Position(..))
63+import Type.Proxy (Proxy(..))
4964
5065-- | __double__
5166-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
@@ -57,28 +72,135 @@ double = Parse.anyFloat64le
5772float :: forall m. MonadEffect m => ParserT DataView m Float32
5873float = Parse.anyFloat32le
5974
75+-- | __repeated packed float__
76+floatArray :: forall m. MonadEffect m => Int -> ParserT DataView m (Array Float32)
77+floatArray = typedArray (AProxy :: AProxy ArrayTypes.Float32) getFloat32le
78+
79+-- | __repeated packed double__
80+doubleArray :: forall m. MonadEffect m => Int -> ParserT DataView m (Array Number)
81+doubleArray = typedArray (AProxy :: AProxy ArrayTypes.Float64) getFloat64le
82+
83+-- | __repeated packed sfixed32__
84+sfixed32Array :: forall m. MonadEffect m => Int -> ParserT DataView m (Array Int)
85+sfixed32Array = typedArray (AProxy :: AProxy ArrayTypes.Int32) getInt32le
86+
87+foreign import data BigInt64 :: ArrayViewType
88+
89+instance bytesPerValueBigInt64 :: BytesPerValue BigInt64 D8
90+
91+instance binaryValueBigInt64 :: BinaryValue BigInt64 (Long Signed)
92+
93+instance showArrayViewBigInt64 :: ShowArrayViewType BigInt64 "long signed"
94+
95+getLongle :: DataView -> Int -> Effect (Maybe (Long Signed))
96+getLongle dv idx = do
97+ lo <- getInt32le dv idx
98+ hi <- getInt32le dv (idx + 4)
99+ pure $ lift2 fromLowHighBits lo hi
100+
101+-- | __repeated packed sfixed64__
102+sfixed64Array :: forall m. MonadEffect m => Int -> ParserT DataView m (Array (Long Signed))
103+sfixed64Array = typedArray (AProxy :: AProxy BigInt64) getLongle
104+
105+foreign import data BigUInt64 :: ArrayViewType
106+
107+instance bytesPerValueBigUInt64 :: BytesPerValue BigUInt64 D8
108+
109+instance binaryValueBigUInt64 :: BinaryValue BigUInt64 (Long Unsigned)
110+
111+instance showArrayViewBigUInt64 :: ShowArrayViewType BigUInt64 "long unsigned"
112+
113+getULongle :: DataView -> Int -> Effect (Maybe (Long Unsigned))
114+getULongle dv idx = do
115+ lo <- getInt32le dv idx
116+ hi <- getInt32le dv (idx + 4)
117+ pure $ lift2 fromLowHighBits lo hi
118+
119+-- | __repeated packed fixed32__
120+fixed32Array :: forall m. MonadEffect m => Int -> ParserT DataView m (Array UInt)
121+fixed32Array = typedArray (AProxy :: AProxy ArrayTypes.Uint32) getUint32le
122+
123+-- | __repeated packed fixed64__
124+fixed64Array :: forall m. MonadEffect m => Int -> ParserT DataView m (Array (Long Unsigned))
125+fixed64Array = typedArray (AProxy :: AProxy BigUInt64) getULongle
126+
127+foreign import _decodeArray ::
128+ forall t. (ByteOffset -> Effect t) -> Int -> Effect (Array t)
129+
130+-- | Parse a slice of the DataView to an Array given a parser for the
131+-- | individual elements
132+typedArray ::
133+ forall a m b t name.
134+ BinaryValue a t =>
135+ BytesPerValue a b =>
136+ ShowArrayViewType a name =>
137+ IsSymbol name =>
138+ Nat b =>
139+ MonadEffect m =>
140+ AProxy a ->
141+ (DataView -> ByteOffset -> Effect (Maybe t)) ->
142+ ByteLength ->
143+ ParserT DataView m (Array t)
144+typedArray _ decodeValue n =
145+ let
146+ byteSize = toInt' (Proxy :: Proxy b)
147+
148+ name = reflectSymbol (SProxy :: SProxy name)
149+ in
150+ do
151+ unless
152+ (n `mod` byteSize == 0)
153+ ( pure unit
154+ >>= \_ ->
155+ fail
156+ $ joinWith " "
157+ [ "byte length not a multiple of"
158+ , show byteSize
159+ , "when parsing"
160+ , name
161+ , "array"
162+ ]
163+ )
164+ ParserT $ ExceptT
165+ $ StateT \state@(ParseState s pos@(Position { line, column }) _) ->
166+ let
167+ pos' = Position { line, column: column + n }
168+
169+ -- we assume all indexing into the dataview will be valid given the
170+ -- check against the byteLength
171+ unsafeDecodeValue i =
172+ unsafePartial
173+ $ map fromJust
174+ $ decodeValue s ((i * byteSize) + column - 1)
175+ in
176+ if column + n - 1 > DV.byteLength s then
177+ pure (Tuple (Left (ParseError "index out of bounds" pos')) state)
178+ else do
179+ x <- liftEffect (_decodeArray unsafeDecodeValue (n `div` byteSize))
180+ pure (Tuple (pure x) (ParseState s pos' true))
181+
60182-- | __int32__
61183-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
62184int32 :: forall m. MonadEffect m => ParserT DataView m Int
63185int32 = do
64186 n <- varint64
187+ -- But this is a problem with the Protobuf spec?
188+ -- “If you use int32 or int64 as the type for a negative number, the resulting
189+ -- varint is always ten bytes long”
190+ -- https://developers.google.com/protocol-buffers/docs/encoding#signed_integers
191+ -- So what are we supposed to do if the field type is int32 but the
192+ -- decoded varint is too big? There is no guarantee that either negative
193+ -- or positive numbers encoded as varints will be in range.
194+ -- I suppose we could check that the high bits are all 0
195+ -- for positive or all 1 for negative.
196+ -- The conformance tests seem to like it if we just throw away
197+ -- the hight bits. So that's what we'll do.
198+ --
199+ -- conformance checker hates this:
200+ -- case Long.toInt n of
201+ -- Nothing -> fail "int32 value out of range."
202+ -- Just x -> pure x
65203 pure $ lowBits n
66− -- But this is a problem with the Protobuf spec?
67− -- “If you use int32 or int64 as the type for a negative number, the resulting
68− -- varint is always ten bytes long”
69− -- https://developers.google.com/protocol-buffers/docs/encoding#signed_integers
70− -- So what are we supposed to do if the field type is int32 but the
71− -- decoded varint is too big? There is no guarantee that either negative
72− -- or positive numbers encoded as varints will be in range.
73− -- I suppose we could check that the high bits are all 0
74− -- for positive or all 1 for negative.
75− -- The conformance tests seem to like it if we just throw away
76− -- the hight bits. So that's what we'll do.
77− --
78− -- conformance checker hates this:
79− -- case Long.toInt n of
80− -- Nothing -> fail "int32 value out of range."
81− -- Just x -> pure x
82204
83205-- | __int64__
84206-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
@@ -103,12 +225,6 @@ sint32 :: forall m. MonadEffect m => ParserT DataView m Int
103225sint32 = do
104226 n <- zigzag32 <$> varint32
105227 pure n
106− -- n <- zigzag64 <$> varint64
107− -- -- pure $ lowBits n
108− -- case Long.toInt n of
109− -- -- Nothing -> fail "sint32 value out of range."
110− -- Nothing -> fail $ "\nsint32 value out of range.\n lowBits " <> toStringAs (unsafePartial $ fromJust $ radix 16) (lowBits n) <> "\n highBits " <> toStringAs (unsafePartial $ fromJust $ radix 16) (highBits n)
111− -- Just x -> pure x
112228
113229-- | __sint64__
114230-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
@@ -140,9 +256,10 @@ sfixed64 = fromLowHighBits <$> Parse.anyInt32le <*> Parse.anyInt32le
140256bool :: forall m. MonadEffect m => ParserT DataView m Boolean
141257bool = do
142258 x <- varint64
143− if lowBits x == 0 && highBits x == 0
144− then pure false
145− else pure true
259+ if lowBits x == 0 && highBits x == 0 then
260+ pure false
261+ else
262+ pure true
146263
147264-- | __string__
148265-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
@@ -153,12 +270,15 @@ string = do
153270 case decodeUtf8 stringarray of
154271 Left err -> fail $ "string decodeUtf8 failed. " <> show err
155272 Right s -> pure s
156− where
273+ where
157274 mkTypedArray :: DataView -> Effect Uint8Array
158275 mkTypedArray dv = do
159− let buffer = DV.buffer dv
160− byteOffset = DV.byteOffset dv
161− byteLength = DV.byteLength dv
276+ let
277+ buffer = DV.buffer dv
278+
279+ byteOffset = DV.byteOffset dv
280+
281+ byteLength = DV.byteLength dv
162282 AT.part buffer byteOffset byteLength
163283
164284-- | __bytes__
@@ -167,8 +287,8 @@ bytes :: forall m. MonadEffect m => ParserT DataView m Bytes
167287bytes = do
168288 len <- UInt.toInt <$> varint32
169289 dv <- Parse.takeN len
170− let ab = DV.buffer dv
171− let begin = DV.byteOffset dv
290+ let
291+ ab = DV.buffer dv
292+ let
293+ begin = DV.byteOffset dv
172294 pure $ Bytes $ AB.slice begin (begin + len) ab
173−
174−
src/ProtocPlugin/Main.purs+6−6
@@ -540,7 +540,7 @@ genFile proto_file (FileDescriptorProto
540540 , " x <- Decode.double"
541541 , " pure $ Record.Builder.modify (Symbol.SProxy :: Symbol.SProxy \"" <> fname <> "\") $ Function.flip Array.snoc x"
542542 , " parseField " <> show fnumber <> " Common.LenDel = Runtime.label \"" <> name' <> " / \" $ do"
543− , " x <- Runtime.parseLenDel $ Runtime.manyLength Decode.double"
543+ , " x <- Runtime.parseLenDel $ Decode.doubleArray"
544544 , " pure $ Record.Builder.modify (Symbol.SProxy :: Symbol.SProxy \"" <> fname <> "\") $ Function.flip Semigroup.append x"
545545 ]
546546 go _ FieldDescriptorProto_Label_LABEL_REPEATED FieldDescriptorProto_Type_TYPE_FLOAT _ = Right $ String.joinWith "\n"
@@ -548,7 +548,7 @@ genFile proto_file (FileDescriptorProto
548548 , " x <- Decode.float"
549549 , " pure $ Record.Builder.modify (Symbol.SProxy :: Symbol.SProxy \"" <> fname <> "\") $ Function.flip Array.snoc x"
550550 , " parseField " <> show fnumber <> " Common.LenDel = Runtime.label \"" <> name' <> " / \" $ do"
551− , " x <- Runtime.parseLenDel $ Runtime.manyLength Decode.float"
551+ , " x <- Runtime.parseLenDel $ Decode.floatArray"
552552 , " pure $ Record.Builder.modify (Symbol.SProxy :: Symbol.SProxy \"" <> fname <> "\") $ Function.flip Semigroup.append x"
553553 ]
554554 go _ FieldDescriptorProto_Label_LABEL_REPEATED FieldDescriptorProto_Type_TYPE_INT64 _ = Right $ String.joinWith "\n"
@@ -580,7 +580,7 @@ genFile proto_file (FileDescriptorProto
580580 , " x <- Decode.fixed64"
581581 , " pure $ Record.Builder.modify (Symbol.SProxy :: Symbol.SProxy \"" <> fname <> "\") $ Function.flip Array.snoc x"
582582 , " parseField " <> show fnumber <> " Common.LenDel = Runtime.label \"" <> name' <> " / \" $ do"
583− , " x <- Runtime.parseLenDel $ Runtime.manyLength Decode.fixed64"
583+ , " x <- Runtime.parseLenDel $ Decode.fixed64Array"
584584 , " pure $ Record.Builder.modify (Symbol.SProxy :: Symbol.SProxy \"" <> fname <> "\") $ Function.flip Semigroup.append x"
585585 ]
586586 go _ FieldDescriptorProto_Label_LABEL_REPEATED FieldDescriptorProto_Type_TYPE_FIXED32 _ = Right $ String.joinWith "\n"
@@ -588,7 +588,7 @@ genFile proto_file (FileDescriptorProto
588588 , " x <- Decode.fixed32"
589589 , " pure $ Record.Builder.modify (Symbol.SProxy :: Symbol.SProxy \"" <> fname <> "\") $ Function.flip Array.snoc x"
590590 , " parseField " <> show fnumber <> " Common.LenDel = Runtime.label \"" <> name' <> " / \" $ do"
591− , " x <- Runtime.parseLenDel $ Runtime.manyLength Decode.fixed32"
591+ , " x <- Runtime.parseLenDel $ Decode.fixed32Array"
592592 , " pure $ Record.Builder.modify (Symbol.SProxy :: Symbol.SProxy \"" <> fname <> "\") $ Function.flip Semigroup.append x"
593593 ]
594594 go _ FieldDescriptorProto_Label_LABEL_REPEATED FieldDescriptorProto_Type_TYPE_BOOL _ = Right $ String.joinWith "\n"
@@ -635,7 +635,7 @@ genFile proto_file (FileDescriptorProto
635635 , " x <- Decode.sfixed32"
636636 , " pure $ Record.Builder.modify (Symbol.SProxy :: Symbol.SProxy \"" <> fname <> "\") $ Function.flip Array.snoc x"
637637 , " parseField " <> show fnumber <> " Common.LenDel = Runtime.label \"" <> name' <> " / \" $ do"
638− , " x <- Runtime.parseLenDel $ Runtime.manyLength Decode.sfixed32"
638+ , " x <- Runtime.parseLenDel $ Decode.sfixed32Array"
639639 , " pure $ Record.Builder.modify (Symbol.SProxy :: Symbol.SProxy \"" <> fname <> "\") $ Function.flip Semigroup.append x"
640640 ]
641641 go _ FieldDescriptorProto_Label_LABEL_REPEATED FieldDescriptorProto_Type_TYPE_SFIXED64 _ = Right $ String.joinWith "\n"
@@ -643,7 +643,7 @@ genFile proto_file (FileDescriptorProto
643643 , " x <- Decode.sfixed64"
644644 , " pure $ Record.Builder.modify (Symbol.SProxy :: Symbol.SProxy \"" <> fname <> "\") $ Function.flip Array.snoc x"
645645 , " parseField " <> show fnumber <> " Common.LenDel = Runtime.label \"" <> name' <> " / \" $ do"
646− , " x <- Runtime.parseLenDel $ Runtime.manyLength Decode.sfixed64"
646+ , " x <- Runtime.parseLenDel $ Decode.sfixed64Array"
647647 , " pure $ Record.Builder.modify (Symbol.SProxy :: Symbol.SProxy \"" <> fname <> "\") $ Function.flip Semigroup.append x"
648648 ]
649649 go _ FieldDescriptorProto_Label_LABEL_REPEATED FieldDescriptorProto_Type_TYPE_SINT32 _ = Right $ String.joinWith "\n"
test/Bench.purs+37−34
@@ -1,8 +1,7 @@
11module Test.Bench where
22
33import Prelude
4−
5−import Data.Array (range, replicate)
4+import Data.Array (range)
65import Data.ArrayBuffer.ArrayBuffer (byteLength, empty)
76import Data.ArrayBuffer.Builder (execPut)
87import Data.ArrayBuffer.DataView (whole)
@@ -10,53 +9,57 @@ import Data.ArrayBuffer.Typed as Typed
109import Data.ArrayBuffer.Types (Float32Array)
1110import Data.Float32 (fromNumber')
1211import Data.Foldable (for_)
13−import Data.Unfoldable (replicateA)
1412import Effect (Effect, forE)
1513import Effect.Console (log)
1614import Effect.Unsafe (unsafePerformEffect)
1715import Performance.Minibench (bench, benchWith)
1816import Protobuf.Decode as Decode
1917import Protobuf.Encode as Encode
20−import Protobuf.Runtime (manyLength)
18+import Protobuf.Runtime as Runtime
2119import Text.Parsing.Parser (runParserT)
2220
2321main :: Effect Unit
2422main = do
25−
2623 log "\nWarmup bench"
2724 bench \_ -> forE 0 10000 (\_ -> pure unit)
28−
29− buf10e3 <- execPut do
30− -- void $ replicateA 10000 $ Encode.float' (fromNumber' 1.0)
31− for_ (range 1 999) $ \_ -> Encode.float' (fromNumber' 1.0)
32−
33− log "\nmanyLength float 10e3"
34− benchWith 100 $ \_ -> void $ unsafePerformEffect $ runParserT (whole buf10e3) do
35− manyLength (Decode.float) (byteLength buf10e3)
36−
37− -- buf5000 <- execPut do
38− -- for_ (range 1 5000) $ \_ -> Encode.float' (fromNumber' 1.0)
39−
40− -- log "\nmanyLength float 5000"
41− -- bench $ \_ -> void $ unsafePerformEffect $ runParserT (whole buf5000) do
42− -- manyLength (Decode.float) (byteLength buf5000)
43−
44− buf10e4 <- empty (4*10000)
25+ buf10e3 <-
26+ execPut do
27+ for_ (range 1 999) $ \_ -> Encode.float' (fromNumber' 1.0)
28+ log "\nfloatArray 10e3"
29+ benchWith 100
30+ $ \_ ->
31+ void $ unsafePerformEffect
32+ $ runParserT (whole buf10e3) (Decode.floatArray (byteLength buf10e3))
33+ buf10e4 <- empty (4 * 10000)
4534 buf10e4Float :: Float32Array <- Typed.whole buf10e4
4635 Typed.fill (fromNumber' 1.0) 0 9999 buf10e4Float
47−
36+ log "\nfloatArray 10e4"
37+ benchWith 100
38+ $ \_ ->
39+ void $ unsafePerformEffect
40+ $ runParserT (whole buf10e4) (Decode.floatArray (byteLength buf10e4))
4841 log "\nmanyLength float 10e4"
49− benchWith 100 $ \_ -> void $ unsafePerformEffect $ runParserT (whole buf10e4) do
50− manyLength (Decode.float) (byteLength buf10e4)
51−
52− buf10e5 <- empty (4*100000)
42+ benchWith 100
43+ $ \_ ->
44+ void $ unsafePerformEffect
45+ $ runParserT (whole buf10e4) (Runtime.manyLength Decode.float (byteLength buf10e4))
46+ buf10e5 <- empty (4 * 100000)
5347 buf10e5Float :: Float32Array <- Typed.whole buf10e5
5448 Typed.fill (fromNumber' 1.0) 0 99999 buf10e5Float
55− -- buf10e5
56− -- void $ replicateA 10000 $ Encode.float' (fromNumber' 1.0)
57− -- for_ (range 1 1000) $ \_ -> Encode.float' (fromNumber' 1.0)
58− -- forE 0 99999 $ \_ ->
59−
49+ log "\nfloatArray 10e5"
50+ benchWith 100
51+ $ \_ ->
52+ void $ unsafePerformEffect
53+ $ runParserT (whole buf10e5) (Decode.floatArray (byteLength buf10e5))
6054 log "\nmanyLength float 10e5"
61− benchWith 100 $ \_ -> void $ unsafePerformEffect $ runParserT (whole buf10e5) do
62− manyLength (Decode.float) (byteLength buf10e5)
55+ benchWith 100
56+ $ \_ ->
57+ void $ unsafePerformEffect
58+ $ runParserT (whole buf10e5) (Runtime.manyLength Decode.float (byteLength buf10e5))
59+ buf10e6 <- empty (4 * 1000000)
60+ buf10e6Float :: Float32Array <- Typed.whole buf10e6
61+ Typed.fill (fromNumber' 7.0) 0 1000000 buf10e6Float
62+ log "\nfloatArray 1e6"
63+ benchWith 100 \_ ->
64+ unsafePerformEffect do
65+ void $ runParserT (whole buf10e6) (Decode.floatArray (byteLength buf10e6))
test/Main.purs+30−4
@@ -10,6 +10,7 @@ import Data.Either (Either(..))
1010import Data.Float32 as Float32
1111import Data.Long.Internal as Long
1212import Data.Maybe (Maybe(..))
13+import Data.TextEncoding (encodeUtf8)
1314import Data.UInt as UInt
1415import Data.Unfoldable (replicate)
1516import Effect (Effect)
@@ -18,10 +19,27 @@ import Pack.Msg2 as Pack2
1819import Pack3.Msg3 as Pack3
1920import Pack4.Msg4 as Pack4
2021import Pack5.Msg5 as Pack5
22+import Protobuf.Common (Bytes(..))
23+import Protobuf.Decode (floatArray)
2124import Test.Assert (assert')
2225import Text.Parsing.Parser (runParserT)
23−import Data.TextEncoding (encodeUtf8)
24−import Protobuf.Common (Bytes(..))
26+import Control.Apply (lift2)
27+import Data.Array (mapMaybe, range)
28+import Data.ArrayBuffer.ArrayBuffer (byteLength, empty)
29+import Data.ArrayBuffer.Builder (execPut)
30+import Data.ArrayBuffer.DataView (part, whole)
31+import Data.ArrayBuffer.Typed as Typed
32+import Data.ArrayBuffer.Types (Float32Array)
33+import Data.Float32 (fromNumber, fromNumber')
34+import Data.Foldable (for_)
35+import Data.Tuple (Tuple(..))
36+import Effect (Effect, forE)
37+import Effect.Console (log)
38+import Effect.Unsafe (unsafePerformEffect)
39+import Performance.Minibench (bench, benchWith)
40+import Protobuf.Decode as Decode
41+import Protobuf.Encode as Encode
42+import Text.Parsing.Parser (runParserT)
2543
2644billion' :: Int
2745billion' = -1000000000
@@ -111,5 +129,13 @@ main = do
111129 case parseResult3 of
112130 Left err -> assert' ("msg3 parse " <> show err) false
113131 Right msg3' -> assert' "msg3 roundtrip" $ msg3 == msg3'
114−
115−
132+ buf <- Typed.fromArray (mapMaybe fromNumber [ 1.0, 0.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0 ]) :: Effect Float32Array
133+ parseResult4 <- runParserT (whole (Typed.buffer buf)) (lift2 Tuple (Decode.floatArray 8) (Decode.floatArray 4))
134+ case parseResult4 of
135+ Left err -> assert' ("floatArray" <> show err) false
136+ Right x -> assert' "floatArray roundtrip" $ x == Tuple (mapMaybe fromNumber [1.0,0.0]) (mapMaybe fromNumber [3.0])
137+ trimmedcustom <- part (Typed.buffer buf) 3 16
138+ parseResult5 <- runParserT trimmedcustom (lift2 Tuple (Decode.floatArray 8) (Decode.floatArray 4))
139+ case parseResult5 of
140+ Left err -> assert' ("floatArray unaligned " <> show err) false
141+ Right x -> assert' "floatArray unaligned roundtrip" $ x == Tuple (mapMaybe fromNumber [8.828180325246348e-44,2.0]) (mapMaybe fromNumber [-8.96831017167883e-44])