compact-encoding 2.16.1 → 2.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +8 -1
  2. package/index.js +153 -0
  3. package/package.json +1 -1
  4. package/test.js +124 -0
package/README.md CHANGED
@@ -76,7 +76,7 @@ The following encodings are bundled as they are primitives that can be used
76
76
  to build others on top. Feel free to PR more that are missing.
77
77
 
78
78
  * `cenc.raw` - Pass through encodes a buffer, i.e. a basic copy.
79
- * `cenc.uint` - Encodes a uint using [compact-uint](https://github.com/mafintosh/compact-uint).
79
+ * `cenc.uint` - Encodes a uint using the smallest fixed size encoding with a prefix to signal which one. Useful for uints that can be a wide range of values.
80
80
  * `cenc.uint8` - Encodes a fixed size uint8.
81
81
  * `cenc.uint16` - Encodes a fixed size uint16. Useful for things like ports.
82
82
  * `cenc.uint24` - Encodes a fixed size uint24. Useful for message framing.
@@ -151,6 +151,13 @@ to build others on top. Feel free to PR more that are missing.
151
151
  * `cenc.ndjson` - Encodes a JSON value as newline delimited utf-8.
152
152
  * `cenc.raw.ndjson` - Encodes a JSON value as newline delimited utf-8 without a length prefixed.
153
153
  * `cenc.any` - Encodes any JSON representable value into a self described buffer. Like JSON + buffer, but using compact types. Useful for schemaless codecs.
154
+ * `cenc.port` - Encodes a port number for network addresses.
155
+ * `cenc.ipv4` - Encodes an IPv4 network address.
156
+ * `cenc.ipv4Address` Encodes an IPv4 network address and a port number.
157
+ * `cenc.ipv6` - Encodes an IPv6 network address.
158
+ * `cenc.ipv6Address` Encodes an IPv6 network address and a port number.
159
+ * `cenc.ip` - Encodes a dual IPv4/6 network address.
160
+ * `cenc.ipAddress` Encodes a dual IPv4/6 network address and a port number.
154
161
  * `cenc.from(enc)` - Makes a compact encoder from a [codec](https://github.com/mafintosh/codecs) or [abstract-encoding](https://github.com/mafintosh/abstract-encoding).
155
162
  * `cenc.none` - Helper for when you want to just express nothing
156
163
 
package/index.js CHANGED
@@ -625,6 +625,159 @@ const any = exports.any = {
625
625
  }
626
626
  }
627
627
 
628
+ const port = exports.port = uint16
629
+
630
+ const address = (host, family) => {
631
+ return {
632
+ preencode (state, m) {
633
+ host.preencode(state, m.host)
634
+ port.preencode(state, m.port)
635
+ },
636
+ encode (state, m) {
637
+ host.encode(state, m.host)
638
+ port.encode(state, m.port)
639
+ },
640
+ decode (state) {
641
+ return {
642
+ host: host.decode(state),
643
+ family,
644
+ port: port.decode(state)
645
+ }
646
+ }
647
+ }
648
+ }
649
+
650
+ const ipv4 = exports.ipv4 = {
651
+ preencode (state) {
652
+ state.end += 4
653
+ },
654
+ encode (state, string) {
655
+ const start = state.start
656
+ const end = start + 4
657
+
658
+ let i = 0
659
+
660
+ while (i < string.length) {
661
+ let n = 0
662
+ let c
663
+
664
+ while (i < string.length && (c = string.charCodeAt(i++)) !== /* . */ 0x2e) {
665
+ n = n * 10 + (c - /* 0 */ 0x30)
666
+ }
667
+
668
+ state.buffer[state.start++] = n
669
+ }
670
+
671
+ state.start = end
672
+ },
673
+ decode (state) {
674
+ if (state.end - state.start < 4) throw new Error('Out of bounds')
675
+ return (
676
+ state.buffer[state.start++] + '.' +
677
+ state.buffer[state.start++] + '.' +
678
+ state.buffer[state.start++] + '.' +
679
+ state.buffer[state.start++]
680
+ )
681
+ }
682
+ }
683
+
684
+ exports.ipv4Address = address(ipv4, 4)
685
+
686
+ const ipv6 = exports.ipv6 = {
687
+ preencode (state) {
688
+ state.end += 16
689
+ },
690
+ encode (state, string) {
691
+ const start = state.start
692
+ const end = start + 16
693
+
694
+ let i = 0
695
+ let split = null
696
+
697
+ while (i < string.length) {
698
+ let n = 0
699
+ let c
700
+
701
+ while (i < string.length && (c = string.charCodeAt(i++)) !== /* : */ 0x3a) {
702
+ if (c >= 0x30 && c <= 0x39) n = n * 0x10 + (c - /* 0 */ 0x30)
703
+ else if (c >= 0x41 && c <= 0x46) n = n * 0x10 + (c - /* A */ 0x41 + 10)
704
+ else if (c >= 0x61 && c <= 0x66) n = n * 0x10 + (c - /* a */ 0x61 + 10)
705
+ }
706
+
707
+ state.buffer[state.start++] = n >>> 8
708
+ state.buffer[state.start++] = n
709
+
710
+ if (i < string.length && string.charCodeAt(i) === /* : */ 0x3a) {
711
+ i++
712
+ split = state.start
713
+ }
714
+ }
715
+
716
+ if (split !== null) {
717
+ const offset = end - state.start
718
+ state.buffer
719
+ .copyWithin(split + offset, split)
720
+ .fill(0, split, split + offset)
721
+ }
722
+
723
+ state.start = end
724
+ },
725
+ decode (state) {
726
+ if (state.end - state.start < 16) throw new Error('Out of bounds')
727
+ return (
728
+ (state.buffer[state.start++] * 256 + state.buffer[state.start++]).toString(16) + ':' +
729
+ (state.buffer[state.start++] * 256 + state.buffer[state.start++]).toString(16) + ':' +
730
+ (state.buffer[state.start++] * 256 + state.buffer[state.start++]).toString(16) + ':' +
731
+ (state.buffer[state.start++] * 256 + state.buffer[state.start++]).toString(16) + ':' +
732
+ (state.buffer[state.start++] * 256 + state.buffer[state.start++]).toString(16) + ':' +
733
+ (state.buffer[state.start++] * 256 + state.buffer[state.start++]).toString(16) + ':' +
734
+ (state.buffer[state.start++] * 256 + state.buffer[state.start++]).toString(16) + ':' +
735
+ (state.buffer[state.start++] * 256 + state.buffer[state.start++]).toString(16)
736
+ )
737
+ }
738
+ }
739
+
740
+ exports.ipv6Address = address(ipv6, 6)
741
+
742
+ const ip = exports.ip = {
743
+ preencode (state, string) {
744
+ const family = string.includes(':') ? 6 : 4
745
+ uint8.preencode(state, family)
746
+ if (family === 4) ipv4.preencode(state)
747
+ else ipv6.preencode(state)
748
+ },
749
+ encode (state, string) {
750
+ const family = string.includes(':') ? 6 : 4
751
+ uint8.encode(state, family)
752
+ if (family === 4) ipv4.encode(state, string)
753
+ else ipv6.encode(state, string)
754
+ },
755
+ decode (state) {
756
+ const family = uint8.decode(state)
757
+ if (family === 4) return ipv4.decode(state)
758
+ else return ipv6.decode(state)
759
+ }
760
+ }
761
+
762
+ exports.ipAddress = {
763
+ preencode (state, m) {
764
+ ip.preencode(state, m.host)
765
+ port.preencode(state, m.port)
766
+ },
767
+ encode (state, m) {
768
+ ip.encode(state, m.host)
769
+ port.encode(state, m.port)
770
+ },
771
+ decode (state) {
772
+ const family = uint8.decode(state)
773
+ return {
774
+ host: family === 4 ? ipv4.decode(state) : ipv6.decode(state),
775
+ family,
776
+ port: port.decode(state)
777
+ }
778
+ }
779
+ }
780
+
628
781
  function getType (o) {
629
782
  if (o === null || o === undefined) return 0
630
783
  if (typeof o === 'boolean') return 1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "compact-encoding",
3
- "version": "2.16.1",
3
+ "version": "2.17.0",
4
4
  "description": "A series of compact encoding schemes for building small and fast parsers and serializers",
5
5
  "main": "index.js",
6
6
  "dependencies": {
package/test.js CHANGED
@@ -841,3 +841,127 @@ test('framed', function (t) {
841
841
  t.alike(enc.encode(e, 4200), b4a.from([0x03, 0xfd, 0x68, 0x10]))
842
842
  t.alike(enc.decode(e, b4a.from([0x03, 0xfd, 0x68, 0x10])), 4200)
843
843
  })
844
+
845
+ test('port', function (t) {
846
+ const p = 0x1234
847
+ const buf = Buffer.from([0x34, 0x12])
848
+
849
+ t.alike(enc.encode(enc.port, p), buf)
850
+ t.alike(enc.decode(enc.port, buf), p)
851
+ })
852
+
853
+ test('ipv4', function (t) {
854
+ const ip = '1.2.3.4'
855
+ const buf = Buffer.from([1, 2, 3, 4])
856
+
857
+ t.alike(enc.encode(enc.ipv4, ip), buf)
858
+ t.alike(enc.decode(enc.ipv4, buf), ip)
859
+ })
860
+
861
+ test('ipv4 + port', function (t) {
862
+ const host = '1.2.3.4'
863
+ const port = 1234
864
+
865
+ t.alike(enc.decode(enc.ipv4Address, enc.encode(enc.ipv4Address, { host, port })), {
866
+ host,
867
+ family: 4,
868
+ port
869
+ })
870
+ })
871
+
872
+ test('ipv6', function (t) {
873
+ const ip = '1:2:3:4:5:6:7:8'
874
+ const buf = Buffer.from([0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8])
875
+
876
+ t.alike(enc.encode(enc.ipv6, ip), buf)
877
+ t.alike(enc.decode(enc.ipv6, buf), ip)
878
+
879
+ t.test('abbreviated', function (t) {
880
+ const buf = Buffer.from([0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 8])
881
+
882
+ t.alike(enc.encode(enc.ipv6, '1:2::7:8'), buf)
883
+ t.alike(enc.decode(enc.ipv6, buf), '1:2:0:0:0:0:7:8')
884
+ })
885
+
886
+ t.test('prefix abbreviated', function (t) {
887
+ const buf = Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 6, 0, 7, 0, 8])
888
+
889
+ t.alike(enc.encode(enc.ipv6, '::5:6:7:8'), buf)
890
+ t.alike(enc.decode(enc.ipv6, buf), '0:0:0:0:5:6:7:8')
891
+ })
892
+
893
+ t.test('suffix abbreviated', function (t) {
894
+ const buf = Buffer.from([0, 1, 0, 2, 0, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0])
895
+
896
+ t.alike(enc.encode(enc.ipv6, '1:2:3:4::'), buf)
897
+ t.alike(enc.decode(enc.ipv6, buf), '1:2:3:4:0:0:0:0')
898
+ })
899
+
900
+ t.test('any', function (t) {
901
+ const buf = Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
902
+
903
+ t.alike(enc.encode(enc.ipv6, '::'), buf)
904
+ t.alike(enc.decode(enc.ipv6, buf), '0:0:0:0:0:0:0:0')
905
+ })
906
+
907
+ t.test('lowercase hex', function (t) {
908
+ const buf = Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xab, 0xcd])
909
+
910
+ t.alike(enc.encode(enc.ipv6, '::abcd'), buf)
911
+ t.alike(enc.decode(enc.ipv6, buf), '0:0:0:0:0:0:0:abcd')
912
+ })
913
+
914
+ t.test('uppercase hex', function (t) {
915
+ const buf = Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xab, 0xcd])
916
+
917
+ t.alike(enc.encode(enc.ipv6, '::ABCD'), buf)
918
+ t.alike(enc.decode(enc.ipv6, buf), '0:0:0:0:0:0:0:abcd')
919
+ })
920
+ })
921
+
922
+ test('ipv6 + port', function (t) {
923
+ const host = '1:2:3:4:5:6:7:8'
924
+ const port = 1234
925
+
926
+ t.alike(enc.decode(enc.ipv6Address, enc.encode(enc.ipv6Address, { host, port })), {
927
+ host,
928
+ family: 6,
929
+ port
930
+ })
931
+ })
932
+
933
+ test('dual ip', function (t) {
934
+ {
935
+ const host = '1.2.3.4'
936
+
937
+ t.alike(enc.decode(enc.ip, enc.encode(enc.ip, host)), host, 'ipv4')
938
+ }
939
+ {
940
+ const host = '1:2:3:4:5:6:7:8'
941
+
942
+ t.alike(enc.decode(enc.ip, enc.encode(enc.ip, host)), host, 'ipv6')
943
+ }
944
+ })
945
+
946
+ test('dual ip + port', function (t) {
947
+ const port = 1234
948
+
949
+ {
950
+ const host = '1.2.3.4'
951
+
952
+ t.alike(enc.decode(enc.ipAddress, enc.encode(enc.ipAddress, { host, port })), {
953
+ host,
954
+ family: 4,
955
+ port
956
+ }, 'ipv4')
957
+ }
958
+ {
959
+ const host = '1:2:3:4:5:6:7:8'
960
+
961
+ t.alike(enc.decode(enc.ipAddress, enc.encode(enc.ipAddress, { host, port })), {
962
+ host,
963
+ family: 6,
964
+ port
965
+ }, 'ipv6')
966
+ }
967
+ })