/
package.scala
1971 lines (1790 loc) · 82.4 KB
/
package.scala
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
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
package scodec
import scala.language.implicitConversions
import scala.math.{log10, floor}
import java.nio.charset.Charset
import java.security.cert.{ Certificate, X509Certificate }
import java.util.UUID
import java.util.zip.Deflater
import scodec.bits.{ BitVector, ByteOrdering, ByteVector }
import shapeless.{ HList, Nat, Sized }
import shapeless.syntax.sized._
/**
* Provides codecs for common types and combinators for building larger codecs.
*
* === Bits and Bytes Codecs ===
*
* The simplest of the provided codecs are those that encode/decode `BitVector`s and `ByteVectors` directly.
* These are provided by `bits` and `bytes` methods. These codecs encode all of the bits/bytes directly
* in to the result and decode *all* of the remaining bits/bytes in to the result value. That is, the result
* of `decode` always returns a empty bit vector for the remaining bits.
*
* Similarly, fixed size alternatives are provided by the `bits(size)` and `bytes(size)` methods, which
* encode a fixed number of bits/bytes (or error if not provided the correct size) and decoded a fixed number
* of bits/bytes (or error if that many bits/bytes are not available).
*
* There are more specialized codecs for working with bits, including `ignore` and `constant`.
*
*
* === Numeric Codecs ===
*
* There are built-in codecs for `Int`, `Long`, `Float`, and `Double`.
*
* There are a number of predefined integral codecs named using the form: {{{
[u]int$${size}[L]
}}}
* where `u` stands for unsigned, `size` is replaced by one of `8, 16, 24, 32, 64`, and `L` stands for little-endian.
* For each codec of that form, the type is `Codec[Int]` or `Codec[Long]` depending on the specified size.
* For example, `int32` supports 32-bit big-endian 2s complement signed integers, and uint16L supports 16-bit little-endian
* unsigned integers.
* Note: `uint64[L]` are not provided because a 64-bit unsigned integer does not fit in to a `Long`.
*
* Additionally, methods of the form `[u]int[L](size: Int)` and `[u]long[L](size: Int)` exist to build arbitrarily
* sized codecs, within the limitations of `Int` and `Long`.
*
* IEEE 754 floating point values are supported by the [[float]], [[floatL]], [[double]], and [[doubleL]] codecs.
*
*
* === Miscellaneous Value Codecs ===
*
* In addition to the numeric codecs, there are built-in codecs for `Boolean`, `String`, and `UUID`.
*
* Boolean values are supported by the `bool` codecs.
*
*
* === Combinators ===
*
* There are a number of methods provided that create codecs out of other codecs. These include simple combinators
* such as [[fixedSizeBits]] and [[variableSizeBits]] and advanced combinators such as [[discriminated]], which
* provides its own DSL for building a large codec out of many small codecs. For a list of all combinators,
* see the Combinators section below.
*
* === Cryptography Codecs ===
*
* There are codecs that support working with encrypted data ([[encrypted]]), digital signatures and checksums
* ([[fixedSizeSignature]] and [[variableSizeSignature]]). Additionally, support for `java.security.cert.Certificate`s
* is provided by [[certificate]] and [[x509Certificate]].
*
* @groupname bits Bits and Bytes Codecs
* @groupprio bits 0
*
* @groupname numbers Number Codecs
* @groupprio numbers 1
*
* @groupname values Miscellaneous Value Codecs
* @groupprio values 2
*
* @groupname combinators Combinators
* @groupprio combinators 3
*
* @groupname guards Guards
* @groupprio guards 4
*
* @groupname tuples Tuple Support
* @groupprio tuples 5
*
* @groupname logging Logging
* @groupprio logging 6
*
* @groupname crypto Cryptography
* @groupprio crypto 7
*/
package object codecs {
/**
* Encodes by returning supplied bit vector; decodes by taking all remaining bits in the supplied bit vector.
* @group bits
*/
def bits: Codec[BitVector] = BitVectorCodec.withToString("bits")
/**
* Encodes by returning the supplied bit vector if its length is `size` bits, padding with zeroes if smaller than `size` bits, returning error if greater;
* decodes by taking `size` bits from the supplied bit vector.
*
* @param size number of bits to encode/decode
* @group bits
*/
def bits(size: Long): Codec[BitVector] = new Codec[BitVector] {
private val codec = fixedSizeBits(size, BitVectorCodec)
def sizeBound = SizeBound.exact(size)
def encode(b: BitVector) = codec.encode(b)
def decode(b: BitVector) = codec.decode(b)
override def toString = s"bits($size)"
}
/**
* Encodes by returning the supplied bit vector if its length is `size` bits, otherwise returning error;
* decodes by taking `size` bits from the supplied bit vector.
*
* @param size number of bits to encode/decode
* @group bits
*/
def bitsStrict(size: Long): Codec[BitVector] = new Codec[BitVector] {
private val codec = new FixedSizeStrictCodec(size, BitVectorCodec)
def sizeBound = codec.sizeBound
def encode(b: BitVector) = codec.encode(b)
def decode(b: BitVector) = codec.decode(b)
override def toString = s"bitsStrict($size)"
}
/**
* Encodes by returning supplied byte vector as a bit vector; decodes by taking all remaining bits in supplied bit vector and converting to a byte vector.
* @group bits
*/
def bytes: Codec[ByteVector] = bits.xmap[ByteVector](_.toByteVector, _.toBitVector).withToString("bytes")
/**
* Encodes by returning the supplied byte vector if its length is `size` bytes, padding with zeroes if smaller than `size` bytes, returning error if greater;
* decodes by taking `size * 8` bits from the supplied bit vector and converting to a byte vector.
*
* @param size number of bits to encode/decode
* @group bits
*/
def bytes(size: Int): Codec[ByteVector] = new Codec[ByteVector] {
private val codec = fixedSizeBytes(size.toLong, BitVectorCodec).xmap[ByteVector](_.toByteVector, _.toBitVector)
def sizeBound = SizeBound.exact(size * 8L)
def encode(b: ByteVector) = codec.encode(b)
def decode(b: BitVector) = codec.decode(b)
override def toString = s"bytes($size)"
}
/**
* Encodes by returning the supplied byte vector if its length is `size` bytes, otherwise returning error;
* decodes by taking `size * 8` bits from the supplied bit vector and converting to a byte vector.
*
* @param size number of bits to encode/decode
* @group bits
*/
def bytesStrict(size: Int): Codec[ByteVector] = new Codec[ByteVector] {
private val codec = new FixedSizeStrictCodec(size * 8L, BitVectorCodec).xmap[ByteVector](_.toByteVector, _.toBitVector)
def sizeBound = codec.sizeBound
def encode(b: ByteVector) = codec.encode(b)
def decode(b: BitVector) = codec.decode(b)
override def toString = s"bytesStrict($size)"
}
/**
* Codec for 8-bit 2s complement bytes.
* @group numbers
*/
val byte: Codec[Byte] = new ByteCodec(8, true)
/**
* Codec for 8-bit unsigned bytes.
* @group numbers
*/
val ushort8: Codec[Short] = new ShortCodec(8, false, ByteOrdering.BigEndian)
/**
* Codec for 16-bit 2s complement big-endian shorts.
* @group numbers
*/
val short16: Codec[Short] = new ShortCodec(16, true, ByteOrdering.BigEndian)
/**
* Codec for 8-bit 2s complement big-endian integers.
* @group numbers
*/
val int8: Codec[Int] = new IntCodec(8, true, ByteOrdering.BigEndian)
/**
* Codec for 16-bit 2s complement big-endian integers.
* @group numbers
*/
val int16: Codec[Int] = new IntCodec(16, true, ByteOrdering.BigEndian)
/**
* Codec for 24-bit 2s complement big-endian integers.
* @group numbers
*/
val int24: Codec[Int] = new IntCodec(24, true, ByteOrdering.BigEndian)
/**
* Codec for 32-bit 2s complement big-endian integers.
* @group numbers
*/
val int32: Codec[Int] = new IntCodec(32, true, ByteOrdering.BigEndian)
/**
* Codec for 64-bit 2s complement big-endian integers.
* @group numbers
*/
val int64: Codec[Long] = new LongCodec(64, true, ByteOrdering.BigEndian)
/**
* Codec for 2-bit unsigned big-endian integers.
* @group numbers
*/
val uint2: Codec[Int] = new IntCodec(2, false, ByteOrdering.BigEndian)
/**
* Codec for 4-bit unsigned big-endian integers.
* @group numbers
*/
val uint4: Codec[Int] = new IntCodec(4, false, ByteOrdering.BigEndian)
/**
* Codec for 8-bit unsigned big-endian integers.
* @group numbers
*/
val uint8: Codec[Int] = new IntCodec(8, false, ByteOrdering.BigEndian)
/**
* Codec for 16-bit unsigned big-endian integers.
* @group numbers
*/
val uint16: Codec[Int] = new IntCodec(16, false, ByteOrdering.BigEndian)
/**
* Codec for 24-bit unsigned big-endian integers.
* @group numbers
*/
val uint24: Codec[Int] = new IntCodec(24, false, ByteOrdering.BigEndian)
/**
* Codec for 32-bit unsigned big-endian integers.
* @group numbers
*/
val uint32: Codec[Long] = new LongCodec(32, false, ByteOrdering.BigEndian)
/**
* Codec for 16-bit 2s complement little-endian shorts.
* @group numbers
*/
val short16L: Codec[Short] = new ShortCodec(16, true, ByteOrdering.LittleEndian)
/**
* Codec for 8-bit 2s complement little-endian integers.
* @group numbers
*/
val int8L: Codec[Int] = new IntCodec(8, true, ByteOrdering.LittleEndian)
/**
* Codec for 16-bit 2s complement little-endian integers.
* @group numbers
*/
val int16L: Codec[Int] = new IntCodec(16, true, ByteOrdering.LittleEndian)
/**
* Codec for 24-bit 2s complement little-endian integers.
* @group numbers
*/
val int24L: Codec[Int] = new IntCodec(24, true, ByteOrdering.LittleEndian)
/**
* Codec for 32-bit 2s complement little-endian integers.
* @group numbers
*/
val int32L: Codec[Int] = new IntCodec(32, true, ByteOrdering.LittleEndian)
/**
* Codec for 64-bit 2s complement little-endian integers.
* @group numbers
*/
val int64L: Codec[Long] = new LongCodec(64, true, ByteOrdering.LittleEndian)
/**
* Codec for 2-bit unsigned little-endian integers.
* @group numbers
*/
val uint2L: Codec[Int] = new IntCodec(2, false, ByteOrdering.LittleEndian)
/**
* Codec for 4-bit unsigned little-endian integers.
* @group numbers
*/
val uint4L: Codec[Int] = new IntCodec(4, false, ByteOrdering.LittleEndian)
/**
* Codec for 8-bit unsigned little-endian integers.
* @group numbers
*/
val uint8L: Codec[Int] = new IntCodec(8, false, ByteOrdering.LittleEndian)
/**
* Codec for 16-bit unsigned little-endian integers.
* @group numbers
*/
val uint16L: Codec[Int] = new IntCodec(16, false, ByteOrdering.LittleEndian)
/**
* Codec for 24-bit unsigned little-endian integers.
* @group numbers
*/
val uint24L: Codec[Int] = new IntCodec(24, false, ByteOrdering.LittleEndian)
/**
* Codec for 32-bit unsigned little-endian integers.
* @group numbers
*/
val uint32L: Codec[Long] = new LongCodec(32, false, ByteOrdering.LittleEndian)
/**
* Codec for variable-length big-endian integers.
* Encoding requires between 1 and 5 bytes, depending on the value.
* Smaller ints require less bytes. Negative values are always encoded with 5 bytes.
* @group numbers
*/
val vint: Codec[Int] = new VarIntCodec(ByteOrdering.BigEndian)
/**
* Codec for variable-length little-endian integers.
* Encoding requires between 1 and 5 bytes, depending on the value.
* Smaller ints require less bytes. Negative values are always encoded with 5 bytes.
* @group numbers
*/
val vintL: Codec[Int] = new VarIntCodec(ByteOrdering.LittleEndian)
/**
* Codec for variable-length big-endian longs.
* Encoding requires between 1 and 9 bytes, depending on the value.
* Smaller longs require less bytes.
* Negative values are not supported.
* @group numbers
*/
val vlong: Codec[Long] = new VarLongCodec(ByteOrdering.BigEndian)
/**
* Codec for variable-length packed decimal longs.
* Negative values are not supported.
* @group numbers
*/
val vpbcd: Codec[Long] = VarPackedDecimalCodec
/**
* Codec for variable-length little-endian longs.
* Encoding requires between 1 and 9 bytes, depending on the value.
* Smaller longs require less bytes.
* Negative values are not supported.
* @group numbers
*/
val vlongL: Codec[Long] = new VarLongCodec(ByteOrdering.LittleEndian)
/**
* Codec for n-bit 2s complement bytes.
* @param size number of bits (must be 0 < size <= 8)
* @group numbers
*/
def byte(size: Int): Codec[Byte] = new ByteCodec(size, true)
/**
* Codec for n-bit unsigned bytes.
* @param size number of bits (must be 0 < size <= 7)
* @group numbers
*/
def ubyte(size: Int): Codec[Byte] = new ByteCodec(size, false)
/**
* Codec for n-bit 2s complement big-endian shorts.
* @param size number of bits (must be 0 < size <= 16)
* @group numbers
*/
def short(size: Int): Codec[Short] = new ShortCodec(size, true, ByteOrdering.BigEndian)
/**
* Codec for n-bit unsigned big-endian shorts.
* @param size number of bits (must be 0 < size <= 15)
* @group numbers
*/
def ushort(size: Int): Codec[Short] = new ShortCodec(size, false, ByteOrdering.BigEndian)
/**
* Codec for n-bit 2s complement big-endian integers that are represented with `Int`.
* @param size number of bits (must be 0 < size <= 32)
* @group numbers
*/
def int(size: Int): Codec[Int] = new IntCodec(size, true, ByteOrdering.BigEndian)
/**
* Codec for n-bit unsigned big-endian integers that are represented with `Int`.
* @param bits number of bits (must be 0 < size <= 31)
* @group numbers
*/
def uint(bits: Int): Codec[Int] = new IntCodec(bits, false, ByteOrdering.BigEndian)
/**
* Codec for n-bit 2s complement big-endian integers that are represented with `Long`.
* @param bits number of bits (must be 0 < size <= 64)
* @group numbers
*/
def long(bits: Int): Codec[Long] = new LongCodec(bits, true, ByteOrdering.BigEndian)
/**
* Codec for n-bit unsigned big-endian integers that are represented with `Long`.
* @param bits number of bits (must be 0 < size <= 63)
* @group numbers
*/
def ulong(bits: Int): Codec[Long] = new LongCodec(bits, false, ByteOrdering.BigEndian)
/**
* Codec for n-bit 2s complement little-endian shorts.
* @param size number of bits (must be 0 < size <= 16)
* @group numbers
*/
def shortL(size: Int): Codec[Short] = new ShortCodec(size, true, ByteOrdering.LittleEndian)
/**
* Codec for n-bit unsigned little-endian shorts.
* @param size number of bits (must be 0 < size <= 15)
* @group numbers
*/
def ushortL(size: Int): Codec[Short] = new ShortCodec(size, false, ByteOrdering.LittleEndian)
/**
* Codec for n-bit 2s complement little-endian integers that are represented with `Int`.
* @param bits number of bits (must be 0 < size <= 32)
* @group numbers
*/
def intL(bits: Int): Codec[Int] = new IntCodec(bits, true, ByteOrdering.LittleEndian)
/**
* Codec for n-bit unsigned little-endian integers that are represented with `Int`.
* @param bits number of bits (must be 0 < size <= 31)
* @group numbers
*/
def uintL(bits: Int): Codec[Int] = new IntCodec(bits, false, ByteOrdering.LittleEndian)
/**
* Codec for n-bit 2s complement little-endian integers that are represented with `Long`.
* @param bits number of bits (must be 0 < size <= 64)
* @group numbers
*/
def longL(bits: Int): Codec[Long] = new LongCodec(bits, true, ByteOrdering.LittleEndian)
/**
* Codec for n-bit unsigned little-endian integers that are represented with `Long`.
* @param bits number of bits (must be 0 < size <= 63)
* @group numbers
*/
def ulongL(bits: Int): Codec[Long] = new LongCodec(bits, false, ByteOrdering.LittleEndian)
/**
* Codec for n-nibble packed decimal (BCD) integers that are represented with `Long`.
* @param nibbles number of nibbles (4-bit chunks)
* @group numbers
*/
def pbcd(nibbles: Int): Codec[Long] = new Codec[Long] {
private def width: Long = nibbles.toLong * 4L
def decode(bits: BitVector): Attempt[DecodeResult[Long]] =
bits.consumeThen(width)(e => Attempt failure Err(e),
(bits,rem) => vpbcd decodeValue bits map (a => DecodeResult(a,rem))
)
def encode(l : Long): Attempt[BitVector] = vpbcd encode l map (_ padLeft width)
def sizeBound = SizeBound exact width
}
/**
* Codec for n-nibble packed decimal (BCD) integers that are represented with `Long`.
* This codec, despite requiring the size in nibbles, is byte-size oriented.
* This means it expects to parse complete bytes (even if nibble size is
* odd). For encoding, this codec will pad 0s on the left while, for
* decoding, it will fetch the size in bytes round up.
* @param nibbles number of nibbles (4-bit chunks)
* @group numbers
*/
def lpbcd(nibbles: Int): Codec[Long] = new Codec[Long]{
val nsize = nibbles.toLong * 4
val bsize = nsize + nsize % 8
def sizeBound = SizeBound.exact(bsize)
def decode(b: BitVector) = fixedSizeBits(bsize, vpbcd).decode(b)
def encode(l: Long) = fixedSizeBits(nsize, vpbcd).encode(l).map{x =>
val size: Long = floor(log10(l.toDouble) + 1).toLong * 4
(BitVector.low(bsize) ++ x.take(size)).drop(size)
}
}
/**
* 32-bit big endian IEEE 754 floating point number.
* @group numbers
*/
val float: Codec[Float] = new FloatCodec(ByteOrdering.BigEndian)
/**
* 32-bit little endian IEEE 754 floating point number.
* @group numbers
*/
val floatL: Codec[Float] = new FloatCodec(ByteOrdering.LittleEndian)
/**
* 64-bit big endian IEEE 754 floating point number.
* @group numbers
*/
val double: Codec[Double] = new DoubleCodec(ByteOrdering.BigEndian)
/**
* 64-bit little endian IEEE 754 floating point number.
* @group numbers
*/
val doubleL: Codec[Double] = new DoubleCodec(ByteOrdering.LittleEndian)
/**
* 1-bit boolean codec, where false corresponds to bit value 0 and true corresponds to bit value 1.
* @group values
*/
val bool: Codec[Boolean] = BooleanCodec
/**
* n-bit boolean codec, where false corresponds to bit vector of all 0s and true corresponds to all other vectors.
* @group values
*/
def bool(n: Long): Codec[Boolean] = new Codec[Boolean] {
private val zeros = BitVector.low(n)
private val ones = BitVector.high(n)
private val codec = bits(n).xmap[Boolean](bits => !(bits == zeros), b => if (b) ones else zeros)
def sizeBound = SizeBound.exact(n)
def encode(b: Boolean) = codec.encode(b)
def decode(b: BitVector) = codec.decode(b)
override def toString = s"bool($n)"
}
/**
* String codec that uses the implicit `Charset` to perform encoding/decoding.
*
* This codec does not encode the size of the string in to the output. Hence, decoding
* a vector that has additional data after the encoded string will result in
* unexpected output. Instead, it is common to use this codec along with either
* [[fixedSizeBits]] or [[variableSizeBits]]. For example, a common encoding
* is a size field, say 2 bytes, followed by the encoded string. This can be
* accomplished with: {{{variableSizeBits(uint16, string)}}}
*
* @param charset charset to use to convert strings to/from binary
* @group values
*/
def string(implicit charset: Charset): Codec[String] = new StringCodec(charset)
/**
* String codec that uses the `US-ASCII` charset. See [[string]] for more information on `String` codecs.
* @group values
*/
val ascii = string(Platform.ascii)
/**
* String codec that uses the `UTF-8` charset. See [[string]] for more information on `String` codecs.
* @group values
*/
val utf8 = string(Platform.utf8)
/**
* String codec that uses the `US-ASCII` charset that encodes strings with a trailing `NUL` termination byte
* and decodes a string up to the next `NUL` termination byte.
* It fails to decode if the bit vector ends before a `NUL` termination byte can be found.
* @group values
*/
val cstring: Codec[String] = filtered(ascii, new Codec[BitVector] {
val nul = BitVector.lowByte
override def sizeBound: SizeBound = SizeBound.unknown
override def encode(bits: BitVector): Attempt[BitVector] = Attempt.successful(bits ++ nul)
override def decode(bits: BitVector): Attempt[DecodeResult[BitVector]] = {
bits.bytes.indexOfSlice(nul.bytes) match {
case -1 => Attempt.failure(Err("Does not contain a 'NUL' termination byte."))
case i => Attempt.successful(DecodeResult(bits.take(i * 8L), bits.drop(i * 8L + 8L)))
}
}
}).withToString("cstring")
/**
* String codec that uses the implicit `Charset` and prefixes the encoded string by the byte size
* in a 32-bit 2s complement big endian field.
*
* @param charset charset to use to convert strings to/from binary
* @group values
*/
def string32(implicit charset: Charset): Codec[String] =
variableSizeBytes(int32, string(charset)).withToString(s"string32(${charset.displayName})")
/**
* String codec that uses the implicit `Charset` and prefixes the encoded string by the byte size
* in a 32-bit 2s complement little endian field.
*
* @param charset charset to use to convert strings to/from binary
* @group values
*/
def string32L(implicit charset: Charset): Codec[String] =
variableSizeBytes(int32L, string(charset)).withToString(s"string32(${charset.displayName})")
/**
* String codec that uses the `US-ASCII` charset and prefixes the encoded string by the byte size
* in a 32-bit 2s complement big endian field.
* @group values
*/
val ascii32 = string32(Platform.ascii)
/**
* String codec that uses the `US-ASCII` charset and prefixes the encoded string by the byte size
* in a 32-bit 2s complement little endian field.
* @group values
*/
val ascii32L = string32L(Platform.ascii)
/**
* String codec that uses the `UTF-8` charset and prefixes the encoded string by the byte size
* in a 32-bit 2s complement big endian field.
* @group values
*/
val utf8_32 = string32(Platform.utf8)
/**
* String codec that uses the `UTF-8` charset and prefixes the encoded string by the byte size
* in a 32-bit 2s complement little endian field.
* @group values
*/
val utf8_32L = string32L(Platform.utf8)
/**
* Encodes/decodes `UUID`s as 2 64-bit big-endian longs, first the high 64-bits then the low 64-bits.
* @group values
*/
val uuid: Codec[UUID] = UuidCodec
/**
* Codec that always returns an empty vector from `encode` and always returns `(empty, value)` from `decode`.
* This is often useful when combined with other codecs (e.g., the [[discriminated]]).
* @param value value to return from decode
* @group combinators
*/
def provide[A](value: A): Codec[A] = new ProvideCodec(value)
/**
* Codec that always encodes `size` 0 bits and always decodes `size` bits and then discards them, returning `()` instead.
* @param size number of bits to ignore
* @group bits
*/
def ignore(size: Long): Codec[Unit] = new IgnoreCodec(size)
/**
* Codec that always encodes the specified bits and always decodes the specified bits, returning `()` if the actual bits match
* the specified bits and returning an error otherwise.
* @param bits constant bits
* @group bits
*/
def constant(bits: BitVector): Codec[Unit] = new ConstantCodec(bits)
/**
* Codec that always encodes the specified bytes and always decodes the specified bytes, returning `()` if the actual bytes match
* the specified bytes and returning an error otherwise.
* @param bytes constant bytes
* @group bits
*/
def constant(bytes: ByteVector): Codec[Unit] = constant(bytes.bits)
/**
* Codec that always encodes the specified bits and always decodes the specified bits, returning `()` if the actual bits match
* the specified bits and returning an error otherwise.
* @param bits constant bits
* @group bits
*/
def constant[A: Integral](bits: A*): Codec[Unit] = constant(BitVector(bits: _*))
/**
* Codec that always encodes the specified bits and always decodes n bits, returning `()`, where n is the length of the
* specified bits.
* @param bits constant bits
* @group bits
*/
def constantLenient(bits: BitVector): Codec[Unit] = new ConstantCodec(bits, false)
/**
* Codec that always encodes the specified bytes and always decodes n bytes, returning `()`, where n is the length of the
* specified bytes.
* @param bytes constant bytes
* @group bits
*/
def constantLenient(bytes: ByteVector): Codec[Unit] = constantLenient(bytes.bits)
/**
* Codec that always encodes the specified bits and always decodes n bits, returning `()`, where n is the length of the
* specified bits.
* @param bits constant bits
* @group bits
*/
def constantLenient[A: Integral](bits: A*): Codec[Unit] = constantLenient(BitVector(bits: _*))
/**
* Provides implicit conversions from literal types to constant codecs.
*
* For example, with `literals._` imported, `constant(0x47) ~> uint8`
* can be written as `0x47 ~> uint8`.
*
* Supports literal bytes, ints, `BitVector`s, and `ByteVector`s.
*
* @group bits
*/
object literals {
implicit def constantIntCodec(a: Int): Codec[Unit] = constant(a)
implicit def constantByteVectorCodec(a: ByteVector): Codec[Unit] = constant(a)
implicit def constantBitVectorCodec(a: BitVector): Codec[Unit] = constant(a)
}
/**
* Codec that limits the number of bits the specified codec works with.
*
* When encoding, if encoding with the specified codec
* results in less than the specified size, the vector is right padded with 0 bits. If the result is larger than the specified
* size, an encoding error is returned.
*
* When decoding, the specified codec is only given `size` bits. If the specified codec does not consume all the bits it was
* given, any remaining bits are discarded.
*
* @param size number of bits
* @param codec codec to limit
* @group combinators
*/
def fixedSizeBits[A](size: Long, codec: Codec[A]): Codec[A] = new FixedSizeCodec(size, codec)
/**
* Byte equivalent of [[fixedSizeBits]].
* @param size number of bytes
* @param codec codec to limit
* @group combinators
*/
def fixedSizeBytes[A](size: Long, codec: Codec[A]): Codec[A] = new Codec[A] {
private val fcodec = fixedSizeBits(size * 8, codec)
def sizeBound = fcodec.sizeBound
def encode(a: A) = fcodec.encode(a)
def decode(b: BitVector) = fcodec.decode(b)
override def toString = s"fixedSizeBytes($size, $codec)"
}
/**
* Codec that limits the number of bits the specified codec works with.
*
* If the encoded result is larger than the specified
* size, an encoding error is returned.
*
* If encoding with the specified codec
* results in less than the specified size, the vector is right padded by repeatedly encoding with padCodec.
* An encoding error is returned if the padCodec result does not precisely fill the remaining space.
*
* When decoding, the specified codec is only given `size` bits. If the specified codec does not consume all the bits it was
* given, all remaining bits are repeatedly decoded by padCodec. A decoding error is returned if any
* padCodec decode returns an error.
*
* @param size number of bits
* @param codec codec to limit
* @param padCodec codec to use for padding
* @group combinators
*/
def paddedFixedSizeBits[A](size: Long, codec: Codec[A], padCodec: Codec[Unit]): Codec[A] = new PaddedFixedSizeCodec(size, codec, _ => padCodec)
/**
* Codec that limits the number of bits the specified codec works with.
*
* If the encoded result is larger than the specified
* size, an encoding error is returned.
*
* If encoding with the specified codec
* results in less than the specified size, the vector is right padded by repeatedly encoding with the
* codec returned from `padCodec(numberOfPaddingBits)`.
* An encoding error is returned if the padCodec result does not precisely fill the remaining space.
*
* When decoding, the specified codec is only given `size` bits. If the specified codec does not consume all the bits it was
* given, all remaining bits are repeatedly decoded by the codec returned from `padCodec(remainingBitCount)`.
* A decoding error is returned if any padding decode iteration returns an error.
*
* @param size number of bits
* @param codec codec to limit
* @param padCodec function that provides the codec to use for padding
* @group combinators
*/
def paddedFixedSizeBitsDependent[A](size: Long, codec: Codec[A], padCodec: Long => Codec[Unit]): Codec[A] = new PaddedFixedSizeCodec(size, codec, padCodec)
/**
* Byte equivalent of [[paddedFixedSizeBits]].
* @param size number of bytes
* @param codec codec to limit
* @param padCodec codec to use for padding
* @group combinators
*/
def paddedFixedSizeBytes[A](size: Long, codec: Codec[A], padCodec: Codec[Unit]): Codec[A] = paddedFixedSizeBytesDependent(size, codec, _ => padCodec)
/**
* Byte equivalent of [[paddedFixedSizeBitsDependent]].
*
* The `padCodec` function is passed the number of *bits* of padding required -- not bytes.
*
* @param size number of bytes
* @param codec codec to limit
* @param padCodec function that provides the codec to use for padding
* @group combinators
*/
def paddedFixedSizeBytesDependent[A](size: Long, codec: Codec[A], padCodec: Long => Codec[Unit]): Codec[A] = new Codec[A] {
private val fcodec = paddedFixedSizeBitsDependent(size * 8, codec, padCodec)
def sizeBound = SizeBound.exact(size * 8)
def encode(a: A) = fcodec.encode(a)
def decode(b: BitVector) = fcodec.decode(b)
override def toString = s"paddedFixedSizeBytes($size, $codec)"
}
/**
* Codec that pads on a multiplier.
*
* Similar to ByteAligendCodec, but instead of only padding to 8 bits, pads to a variable size
*
* @param sizeCodec codec that determines the size
* @param valueCodec codec for encoding the payload
* @param multipleForPadding multiple to align the value to with padding
* @group combinators
*/
def paddedVarAlignedBits[A](sizeCodec: Codec[Long], valueCodec: Codec[A], multipleForPadding: Int) = new PaddedVarAlignedCodec(sizeCodec, valueCodec, multipleForPadding.toLong)
/**
* Byte equivalent of [[paddedVarAlignedBits]].
* @param sizeCodec codec that determines the size
* @param valueCodec coec for encoding the payload
* @param multipleForPadding multiple of bytes to align the value to with padding
* @group combinators
*/
def paddedVarAlignedBytes[A](sizeCodec: Codec[Int], valueCodec: Codec[A], multipleForPadding: Int) = new Codec[A] {
val sizedWiden = widenIntToLong(sizeCodec)
private val codec = new PaddedVarAlignedCodec(sizedWiden.widen[Long](_ * 8, bitsToBytesDivisible), valueCodec, multipleForPadding.toLong * 8)
override def encode(a: A) = codec.encode(a)
override def decode(buffer: BitVector) = codec.decode(buffer)
override def sizeBound = codec.sizeBound
override def toString = "PaddedVarAlignedBytesCodec"
}
/**
* Codec that limits the number of bits the specified codec works with.
*
* When encoding, if encoding with the specified codec
* results in less than the specified size, the vector is returned with no padding. If the result is larger than the specified
* size, an encoding error is returned. This differs from `fixedSizeBits` by not padding encoded vectors less than the specified
* size.
*
* When decoding, the specified codec is only given `size` bits. If the specified codec does not consume all the bits it was
* given, any remaining bits are returned with the overall remainder.
*
* @param size number of bits
* @param codec codec to limit
* @group combinators
*/
def limitedSizeBits[A](limit: Long, codec: Codec[A]): Codec[A] = new LimitedSizeCodec(limit, codec)
/**
* Byte equivalent of [[limitedSizeBits]].
* @param size number of bytes
* @param codec codec to limit
* @group combinators
*/
def limitedSizeBytes[A](limit: Long, codec: Codec[A]): Codec[A] = new Codec[A] {
private val fcodec = limitedSizeBits(limit * 8, codec)
def sizeBound = fcodec.sizeBound
def encode(a: A) = fcodec.encode(a)
def decode(b: BitVector) = fcodec.decode(b)
override def toString = s"limitedSizeBytes($limit, $codec)"
}
/**
* Codec that supports vectors of the form `size ++ value` where the `size` field decodes to the bit length of the `value` field.
*
* For example, encoding the string `"hello"` with `variableSizeBits(uint8, ascii)` yields a vector of 6 bytes -- the first byte being
* 0x28 and the next 5 bytes being the US-ASCII encoding of `"hello"`.
*
* The `size` field can be any `Int` codec. An optional padding can be applied to the size field. The `sizePadding` is added to
* the calculated size before encoding, and subtracted from the decoded size before decoding the value.
*
* For example, encoding `"hello"` with `variableSizeBits(uint8, ascii, 1)` yields a vector of 6 bytes -- the first byte being
* 0x29 and the next 5 bytes being the US-ASCII encoding of `"hello"`.
*
* @param size codec that encodes/decodes the size in bits
* @param value codec the encodes/decodes the value
* @param sizePadding number of bits to add to the size before encoding (and subtract from the size before decoding)
* @group combinators
*/
def variableSizeBits[A](size: Codec[Int], value: Codec[A], sizePadding: Int = 0): Codec[A] =
variableSizeBitsLong(widenIntToLong(size), value, sizePadding.toLong)
/**
* Byte equivalent of [[variableSizeBits]].
* @param size codec that encodes/decodes the size in bytes
* @param value codec the encodes/decodes the value
* @param sizePadding number of bytes to add to the size before encoding (and subtract from the size before decoding)
* @group combinators
*/
def variableSizeBytes[A](size: Codec[Int], value: Codec[A], sizePadding: Int = 0): Codec[A] =
variableSizeBytesLong(widenIntToLong(size), value, sizePadding.toLong)
private def widenIntToLong(c: Codec[Int]): Codec[Long] =
c.widen[Long](i => i.toLong, l => if (l > Int.MaxValue || l < Int.MinValue) Attempt.failure(Err(s"$l cannot be converted to an integer")) else Attempt.successful(l.toInt)).withToString(c.toString)
/**
* Codec that supports vectors of the form `size ++ value` where the `size` field decodes to the bit length of the `value` field.
*
* For example, encoding the string `"hello"` with `variableSizeBitsLong(uint32, ascii)` yields a vector of 9 bytes -- the first four bytes being
* 0x00000028 and the next 5 bytes being the US-ASCII encoding of `"hello"`.
*
* The `size` field can be any `Long` codec. An optional padding can be applied to the size field. The `sizePadding` is added to
* the calculated size before encoding, and subtracted from the decoded size before decoding the value.
*
* For example, encoding `"hello"` with `variableSizeBitsLong(uint32, ascii, 1)` yields a vector of 9 bytes -- the first 4 bytes being
* 0x00000029 and the next 5 bytes being the US-ASCII encoding of `"hello"`.
*
* @param size codec that encodes/decodes the size in bits
* @param value codec the encodes/decodes the value
* @param sizePadding number of bits to add to the size before encoding (and subtract from the size before decoding)
* @group combinators
*/
def variableSizeBitsLong[A](size: Codec[Long], value: Codec[A], sizePadding: Long = 0): Codec[A] =
new VariableSizeCodec(size, value, sizePadding)
/**
* Byte equivalent of [[variableSizeBitsLong]].
* @param size codec that encodes/decodes the size in bytes
* @param value codec the encodes/decodes the value
* @param sizePadding number of bytes to add to the size before encoding (and subtract from the size before decoding)
* @group combinators
*/
def variableSizeBytesLong[A](size: Codec[Long], value: Codec[A], sizePadding: Long = 0): Codec[A] = new Codec[A] {
private val codec = variableSizeBitsLong(size.widen[Long](_ * 8, bitsToBytesDivisible), value, sizePadding * 8)
def sizeBound = size.sizeBound + value.sizeBound
def encode(a: A) = codec.encode(a)
def decode(b: BitVector) = codec.decode(b)
override def toString = s"variableSizeBytes($size, $value)"
}
private def bitsToBytesDivisible(n: Long): Attempt[Long] =
if (n % 8 == 0) Attempt.successful(n / 8)
else Attempt.failure(Err(s"$n is not evenly divisible by 8"))
/**
* Codec that supports vectors of the form `value ++ delimiter` where the `delimiter` marks the end of the `value` field.
*
* @param size codec that encodes/decodes the delimiter
* @param value codec the encodes/decodes the value
* @group combinators
*/
def variableSizeDelimited[A](delimiterCodec: Codec[Unit], value: Codec[A]): Codec[A] =
new VariableSizeDelimitedCodec(delimiterCodec, value)
/**
* Codec that supports vectors of the form `value ++ delimiter` where the `delimiter` marks the end of the `value` field.
*
* @param size codec that encodes/decodes the delimiter
* @param value codec the encodes/decodes the value
* @param multipleValueSize the size or a mutiple size of the expected value
* @group combinators
*/
def variableSizeDelimited[A](delimiterCodec: Codec[Unit], value: Codec[A], multipleValueSize: Long): Codec[A] =
new VariableSizeDelimitedCodec(delimiterCodec, value, multipleValueSize)
/**
* Codec that supports vectors of the form `size ++ prefix ++ value` where the `size` field decodes to the bit length of the `value` field.
*
* For example, encoding `(3, "hello")` with `variableSizePrefixedBits(uint8, int32, ascii)` yields a vector of 10 bytes -- the first byte being
* 0x28, the next 4 bytes being 0x00000003, and the last 5 bytes being the US-ASCII encoding of `"hello"`.
*
* The `size` field can be any `Int` codec. An optional padding can be applied to the size field. The `sizePadding` is added to
* the calculated size before encoding, and subtracted from the decoded size before decoding the value.
*
* For example, encoding `(3, "hello")` with `variableSizePrefixedBits(uint8, int32, ascii, 1)` yields a vector of 10 bytes -- the first byte being
* 0x29, the next 4 bytes being 0x00000003, and the last 5 bytes being the US-ASCII encoding of `"hello"`.
*
* @param size codec that encodes/decodes the size in bits
* @param prefix codec that encodes/decodes the prefix
* @param value codec the encodes/decodes the value
* @param sizePadding number of bits to add to the size before encoding (and subtract from the size before decoding)
* @group combinators
*/
def variableSizePrefixedBits[A, B](size: Codec[Int], prefix: Codec[A], value: Codec[B], sizePadding: Int = 0): Codec[(A, B)] =
variableSizePrefixedBitsLong(widenIntToLong(size), prefix, value, sizePadding.toLong)
/**
* Byte equivalent of [[variableSizePrefixedBits]].
* @param size codec that encodes/decodes the size in bytes