-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathEncode.purs
401 lines (345 loc) · 13.9 KB
/
Encode.purs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
-- | Primitive builders for encoding Google Protocol Buffers.
-- |
-- | You almost never need to import this module.
-- | See package README for explanation.
module Protobuf.Encode
( encodeDoubleField
, encodeDouble
, encodeFloatField
, encodeFloat
, encodeInt32Field
, encodeInt32
, encodeInt64Field
, encodeInt64
, encodeUint32Field
, encodeUint32
, encodeUint64Field
, encodeUint64
, encodeSint32Field
, encodeSint32
, encodeSint64Field
, encodeSint64
, encodeFixed32Field
, encodeFixed32
, encodeFixed64Field
, encodeFixed64
, encodeSfixed32Field
, encodeSfixed32
, encodeSfixed64Field
, encodeSfixed64
, encodeBoolField
, encodeBool
, encodeStringField
, encodeBytesField
, encodeBuilder
, encodeVarint32
, encodeZigzag32
, encodeTag32
, encodeVarint64
, encodeZigzag64
) where
import Prelude
import Effect.Class (class MonadEffect)
import Control.Monad.Writer.Trans (tell)
import Data.Float32 (Float32)
import Data.ArrayBuffer.Builder as Builder
import Data.UInt (UInt)
import Data.UInt as UInt
import Data.TextEncoding (encodeUtf8)
import Data.ArrayBuffer.Typed as AT
import Data.ArrayBuffer.ArrayBuffer as AB
import Protobuf.Common (FieldNumber, WireType(..), Bytes(..))
import Data.Enum (fromEnum)
import Data.Long.Internal (Long, Unsigned, Signed)
import Data.Long as Long
import Data.Long.Internal as Long.Internal
encodeDoubleField :: forall m. MonadEffect m => FieldNumber -> Number -> Builder.PutM m Unit
-- https://developers.google.com/protocol-buffers/docs/encoding#non-varint_numbers
encodeDoubleField fieldNumber n = do
encodeTag32 fieldNumber Bits64
encodeDouble n
-- | __double__
-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
encodeDouble :: forall m. MonadEffect m => Number -> Builder.PutM m Unit
-- https://developers.google.com/protocol-buffers/docs/encoding#non-varint_numbers
encodeDouble n = do
Builder.putFloat64le n
encodeFloatField :: forall m. MonadEffect m => FieldNumber -> Float32 -> Builder.PutM m Unit
encodeFloatField fieldNumber n = do
encodeTag32 fieldNumber Bits32
encodeFloat n
-- | __float__
-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
encodeFloat :: forall m. MonadEffect m => Float32 -> Builder.PutM m Unit
encodeFloat n = do
Builder.putFloat32le n
encodeInt32Field :: forall m. MonadEffect m => FieldNumber -> Int -> Builder.PutM m Unit
-- “If you use int32 or int64 as the type for a negative number, the resulting
-- varint is always ten bytes long”
-- https://developers.google.com/protocol-buffers/docs/encoding#signed_integers
encodeInt32Field fieldNumber n = do
encodeTag32 fieldNumber VarInt
encodeInt32 n
-- | __int32__
-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
encodeInt32 :: forall m. MonadEffect m => Int -> Builder.PutM m Unit
-- “If you use int32 or int64 as the type for a negative number, the resulting
-- varint is always ten bytes long”
-- https://developers.google.com/protocol-buffers/docs/encoding#signed_integers
encodeInt32 n = do
encodeVarint64 $ Long.Internal.signedToUnsigned $ Long.Internal.signedLongFromInt n
encodeInt64Field :: forall m. MonadEffect m => FieldNumber -> Long Signed -> Builder.PutM m Unit
-- “If you use int32 or int64 as the type for a negative number, the resulting
-- varint is always ten bytes long”
-- https://developers.google.com/protocol-buffers/docs/encoding#signed_integers
encodeInt64Field fieldNumber n = do
encodeTag32 fieldNumber VarInt
encodeInt64 n
-- | __int64__
-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
encodeInt64 :: forall m. MonadEffect m => Long Signed -> Builder.PutM m Unit
-- “If you use int32 or int64 as the type for a negative number, the resulting
-- varint is always ten bytes long”
-- https://developers.google.com/protocol-buffers/docs/encoding#signed_integers
encodeInt64 n = do
encodeVarint64 $ Long.toUnsigned n
encodeUint32Field :: forall m. MonadEffect m => FieldNumber -> UInt -> Builder.PutM m Unit
encodeUint32Field fieldNumber n = do
encodeTag32 fieldNumber VarInt
encodeUint32 n
-- | __uint32__
-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
encodeUint32 :: forall m. MonadEffect m => UInt -> Builder.PutM m Unit
encodeUint32 n = do
encodeVarint32 n
encodeUint64Field :: forall m. MonadEffect m => FieldNumber -> Long Unsigned -> Builder.PutM m Unit
encodeUint64Field fieldNumber n = do
encodeTag32 fieldNumber VarInt
encodeUint64 n
-- | __uint64__
-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
encodeUint64 :: forall m. MonadEffect m => Long Unsigned -> Builder.PutM m Unit
encodeUint64 n = do
encodeVarint64 n
encodeSint32Field :: forall m. MonadEffect m => FieldNumber -> Int -> Builder.PutM m Unit
encodeSint32Field fieldNumber n = do
encodeTag32 fieldNumber VarInt
encodeSint32 n
-- | __sint32__
-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
encodeSint32 :: forall m. MonadEffect m => Int -> Builder.PutM m Unit
encodeSint32 n = do
encodeVarint32 $ encodeZigzag32 n
encodeSint64Field :: forall m. MonadEffect m => FieldNumber -> Long Signed -> Builder.PutM m Unit
encodeSint64Field fieldNumber n = do
encodeTag32 fieldNumber VarInt
encodeSint64 n
-- | __sint64__
-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
encodeSint64 :: forall m. MonadEffect m => Long Signed -> Builder.PutM m Unit
encodeSint64 n = do
encodeVarint64 $ encodeZigzag64 n
encodeFixed32Field :: forall m. MonadEffect m => FieldNumber -> UInt -> Builder.PutM m Unit
-- https://developers.google.com/protocol-buffers/docs/encoding#non-varint_numbers
encodeFixed32Field fieldNumber n = do
encodeTag32 fieldNumber Bits32
encodeFixed32 n
-- | __fixed32__
-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
encodeFixed32 :: forall m. MonadEffect m => UInt -> Builder.PutM m Unit
-- https://developers.google.com/protocol-buffers/docs/encoding#non-varint_numbers
encodeFixed32 n = do
Builder.putUint32le n
encodeFixed64Field :: forall m. MonadEffect m => FieldNumber -> Long Unsigned -> Builder.PutM m Unit
encodeFixed64Field fieldNumber n = do
encodeTag32 fieldNumber Bits64
encodeFixed64 n
-- | __fixed64__
-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
encodeFixed64 :: forall m. MonadEffect m => Long Unsigned -> Builder.PutM m Unit
encodeFixed64 n = do
Builder.putInt32le $ Long.Internal.lowBits n
Builder.putInt32le $ Long.Internal.highBits n
encodeSfixed32Field :: forall m. MonadEffect m => FieldNumber -> Int -> Builder.PutM m Unit
encodeSfixed32Field fieldNumber n = do
encodeTag32 fieldNumber Bits32
encodeSfixed32 n
-- | __sfixed32__
-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
encodeSfixed32 :: forall m. MonadEffect m => Int -> Builder.PutM m Unit
encodeSfixed32 n = do
Builder.putInt32le n
encodeSfixed64Field :: forall m. MonadEffect m => FieldNumber -> Long Signed -> Builder.PutM m Unit
encodeSfixed64Field fieldNumber n = do
encodeTag32 fieldNumber Bits64
encodeSfixed64 n
-- | __sfixed64__
-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
encodeSfixed64 :: forall m. MonadEffect m => Long Signed -> Builder.PutM m Unit
encodeSfixed64 n = do
Builder.putInt32le $ Long.lowBits n
Builder.putInt32le $ Long.highBits n
encodeBoolField :: forall m. MonadEffect m => FieldNumber -> Boolean -> Builder.PutM m Unit
encodeBoolField fieldNumber n = do
encodeTag32 fieldNumber VarInt
encodeBool n
-- | __bool__
-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
encodeBool :: forall m. MonadEffect m => Boolean -> Builder.PutM m Unit
encodeBool n = do
if n then Builder.putInt8 1 else Builder.putInt8 0
-- | __string__
-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
encodeStringField :: forall m. MonadEffect m => FieldNumber -> String -> Builder.PutM m Unit
-- https://developers.google.com/protocol-buffers/docs/encoding#strings
encodeStringField fieldNumber s = do
encodeTag32 fieldNumber LenDel
let
stringbuf = AT.buffer $ encodeUtf8 s
encodeVarint32 $ UInt.fromInt $ AB.byteLength stringbuf
Builder.putArrayBuffer stringbuf
-- | __bytes__
-- | [Scalar Value Type](https://developers.google.com/protocol-buffers/docs/proto3#scalar)
encodeBytesField :: forall m. MonadEffect m => FieldNumber -> Bytes -> Builder.PutM m Unit
encodeBytesField fieldNumber (Bytes s) = do
encodeTag32 fieldNumber LenDel
encodeVarint32 $ UInt.fromInt $ AB.byteLength s
Builder.putArrayBuffer s
-- | `tell` with a tag and a length delimit.
encodeBuilder :: forall m. MonadEffect m => FieldNumber -> Builder.Builder -> Builder.PutM m Unit
encodeBuilder fieldNumber s = do
encodeTag32 fieldNumber LenDel
encodeVarint32 $ UInt.fromInt $ Builder.length s
tell s
-- | https://developers.google.com/protocol-buffers/docs/encoding#signed_integers
encodeZigzag32 :: Int -> UInt
encodeZigzag32 n = let n' = UInt.fromInt n in (n' `UInt.shl` (UInt.fromInt 1)) `UInt.xor` (n' `UInt.shr` (UInt.fromInt 31))
-- | https://developers.google.com/protocol-buffers/docs/encoding#structure
encodeTag32 :: forall m. MonadEffect m => FieldNumber -> WireType -> Builder.PutM m Unit
encodeTag32 fieldNumber wireType = encodeVarint32 $ (fieldNumber `UInt.shl` (UInt.fromInt 3)) `UInt.or` (UInt.fromInt $ fromEnum wireType)
-- | There is no `varint32` in the Protbuf spec, this is
-- | just a performance-improving assumption we make
-- | in cases where only a deranged lunatic would use a value
-- | bigger than 32 bits, such as in field numbers.
-- | We think this is worth the risk because `UInt` is
-- | represented as a native Javascript Number whereas
-- | `Long` is a composite library type, so we expect the
-- | performance difference to be significant.
-- |
-- | https://developers.google.com/protocol-buffers/docs/encoding#varints
encodeVarint32 :: forall m. MonadEffect m => UInt -> Builder.PutM m Unit
encodeVarint32 n_0 = do
let
group_0 = n_0 `UInt.and` u0x7F
n_1 = n_0 `UInt.zshr` u7
if n_1 == u0 then
Builder.putUint8 group_0
else do
Builder.putUint8 $ u0x80 `UInt.or` group_0
let
group_1 = n_1 `UInt.and` u0x7F
n_2 = n_1 `UInt.zshr` u7
if n_2 == u0 then
Builder.putUint8 group_1
else do
Builder.putUint8 $ u0x80 `UInt.or` group_1
let
group_2 = n_2 `UInt.and` u0x7F
n_3 = n_2 `UInt.zshr` u7
if n_3 == u0 then
Builder.putUint8 group_2
else do
Builder.putUint8 $ u0x80 `UInt.or` group_2
let
group_3 = n_3 `UInt.and` u0x7F
n_4 = n_3 `UInt.zshr` u7
if n_4 == u0 then
Builder.putUint8 group_3
else do
Builder.putUint8 $ u0x80 `UInt.or` group_3
Builder.putUint8 n_4
where
u0 = UInt.fromInt 0
u7 = UInt.fromInt 7
u0x7F = UInt.fromInt 0x7F
u0x80 = UInt.fromInt 0x80
-- | https://developers.google.com/protocol-buffers/docs/encoding#signed_integers
encodeZigzag64 :: Long Signed -> Long Unsigned
encodeZigzag64 n = Long.toUnsigned $ (n `Long.Internal.shl` (Long.Internal.unsafeFromInt 1)) `Long.Internal.xor` (n `Long.Internal.shr` (Long.Internal.unsafeFromInt 63))
encodeVarint64 :: forall m. MonadEffect m => Long Unsigned -> Builder.PutM m Unit
encodeVarint64 n_0 = do
let
group_0 = takeGroup n_0
n_1 = n_0 `Long.Internal.zshr` u7
if n_1 == u0 then
Builder.putUint8 group_0
else do
Builder.putUint8 $ contGroup group_0
let
group_1 = takeGroup n_1
n_2 = n_1 `Long.Internal.zshr` u7
if n_2 == u0 then
Builder.putUint8 group_1
else do
Builder.putUint8 $ contGroup group_1
let
group_2 = takeGroup n_2
n_3 = n_2 `Long.Internal.zshr` u7
if n_3 == u0 then
Builder.putUint8 group_2
else do
Builder.putUint8 $ contGroup group_2
let
group_3 = takeGroup n_3
n_4 = n_3 `Long.Internal.zshr` u7
if n_4 == u0 then
Builder.putUint8 group_3
else do
Builder.putUint8 $ contGroup group_3
let
group_4 = takeGroup n_4
n_5 = n_4 `Long.Internal.zshr` u7
if n_5 == u0 then
Builder.putUint8 group_4
else do
Builder.putUint8 $ contGroup group_4
let
group_5 = takeGroup n_5
n_6 = n_5 `Long.Internal.zshr` u7
if n_6 == u0 then
Builder.putUint8 group_5
else do
Builder.putUint8 $ contGroup group_5
let
group_6 = takeGroup n_6
n_7 = n_6 `Long.Internal.zshr` u7
if n_7 == u0 then
Builder.putUint8 group_6
else do
Builder.putUint8 $ contGroup group_6
let
group_7 = takeGroup n_7
n_8 = n_7 `Long.Internal.zshr` u7
if n_8 == u0 then
Builder.putUint8 group_7
else do
Builder.putUint8 $ contGroup group_7
let
group_8 = takeGroup n_8
n_9 = n_8 `Long.Internal.zshr` u7
if n_9 == u0 then
Builder.putUint8 group_8
else do
Builder.putUint8 $ contGroup group_8
Builder.putUint8 $ takeGroup n_9
where
-- copy the low seven bits group from a Long
takeGroup :: Long Unsigned -> UInt
takeGroup n = (UInt.fromInt $ Long.Internal.lowBits n) `UInt.and` u0x7F
-- Set the high eigth continuation bit of a group
contGroup :: UInt -> UInt
contGroup n = u0x80 `UInt.or` n
u0 = Long.Internal.unsafeFromInt 0 :: Long Unsigned
u7 = Long.Internal.unsafeFromInt 7 :: Long Unsigned
u0x7F = UInt.fromInt 0x7F
u0x80 = UInt.fromInt 0x80