git.iko.soy/rowtype-yoga/purescript-protobuf
on proto3-upgrade1 help sign in
174 lines 6.4 kB history raw
1-- | Primitive parsers for decoding Google Protocol Buffers.
2-- |
3-- | You almost never need to import this module.
4-- | See package README for explanation.
5module 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
25import Prelude
27import Control.Monad.Trans.Class (lift)
28import Data.ArrayBuffer.ArrayBuffer as AB
29import Data.ArrayBuffer.DataView as DV
30import Data.ArrayBuffer.Typed as AT
31import Data.ArrayBuffer.Types (DataView, Uint8Array)
32import Data.Either (Either(..))
33import Data.Float32 (Float32)
34import Data.Int (Radix, radix, toStringAs)
35import Data.Long.Internal (Long, Signed, Unsigned, fromLowHighBits, highBits, lowBits, unsignedToSigned)
36import Data.Long.Internal as Long
37import Data.Maybe (Maybe(..), fromJust)
38import Data.TextDecoding (decodeUtf8)
39import Data.UInt (UInt)
40import Data.UInt as UInt
41import Effect (Effect)
42import Effect.Class (class MonadEffect, liftEffect)
43import Partial.Unsafe (unsafePartial)
44import Protobuf.Common (Bytes(..))
45import Protobuf.Decode32 (varint32, zigzag32, tag32)
46import Protobuf.Decode64 (varint64, zigzag64)
47import Text.Parsing.Parser (ParserT, fail)
48import Text.Parsing.Parser.DataView as Parse
50-- | __double__
51-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
52double :: forall m. MonadEffect m => ParserT DataView m Number
53double = Parse.anyFloat64le
55-- | __float__
56-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
57float :: forall m. MonadEffect m => ParserT DataView m Float32
58float = Parse.anyFloat32le
60-- | __int32__
61-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
62int32 :: forall m. MonadEffect m => ParserT DataView m Int
63int32 = do
64 n <- varint64
65 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
83-- | __int64__
84-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
85int64 :: forall m. MonadEffect m => ParserT DataView m (Long Signed)
86int64 = unsignedToSigned <$> varint64
88-- | __uint32__
89-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
90uint32 :: forall m. MonadEffect m => ParserT DataView m UInt
91uint32 = do
92 n <- varint64
93 pure $ UInt.fromInt $ lowBits n
95-- | __uint64__
96-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
97uint64 :: forall m. MonadEffect m => ParserT DataView m (Long Unsigned)
98uint64 = varint64
100-- | __sint32__
101-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
102sint32 :: forall m. MonadEffect m => ParserT DataView m Int
103sint32 = do
104 n <- zigzag32 <$> varint32
105 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
113-- | __sint64__
114-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
115sint64 :: forall m. MonadEffect m => ParserT DataView m (Long Signed)
116sint64 = zigzag64 <$> varint64
118-- | __fixed32__
119-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
120fixed32 :: forall m. MonadEffect m => ParserT DataView m UInt
121fixed32 = Parse.anyUint32le
123-- | __fixed64__
124-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
125fixed64 :: forall m. MonadEffect m => ParserT DataView m (Long Unsigned)
126fixed64 = fromLowHighBits <$> Parse.anyInt32le <*> Parse.anyInt32le
128-- | __sfixed32__
129-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
130sfixed32 :: forall m. MonadEffect m => ParserT DataView m Int
131sfixed32 = Parse.anyInt32le
133-- | __sfixed64__
134-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
135sfixed64 :: forall m. MonadEffect m => ParserT DataView m (Long Signed)
136sfixed64 = fromLowHighBits <$> Parse.anyInt32le <*> Parse.anyInt32le
138-- | __bool__
139-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
140bool :: forall m. MonadEffect m => ParserT DataView m Boolean
141bool = do
142 x <- varint64
143 if lowBits x == 0 && highBits x == 0
144 then pure false
145 else pure true
147-- | __string__
148-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
149string :: forall m. MonadEffect m => ParserT DataView m String
150string = do
151 stringview <- varint32 >>= UInt.toInt >>> Parse.takeN
152 stringarray <- lift $ liftEffect $ mkTypedArray stringview
153 case decodeUtf8 stringarray of
154 Left err -> fail $ "string decodeUtf8 failed. " <> show err
155 Right s -> pure s
156 where
157 mkTypedArray :: DataView -> Effect Uint8Array
158 mkTypedArray dv = do
159 let buffer = DV.buffer dv
160 byteOffset = DV.byteOffset dv
161 byteLength = DV.byteLength dv
162 AT.part buffer byteOffset byteLength
164-- | __bytes__
165-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
166bytes :: forall m. MonadEffect m => ParserT DataView m Bytes
167bytes = do
168 len <- UInt.toInt <$> varint32
169 dv <- Parse.takeN len
170 let ab = DV.buffer dv
171 let begin = DV.byteOffset dv
172 pure $ Bytes $ AB.slice begin (begin + len) ab