Add Compact{16,32,64,128,256} (#219)
* Add Compact{16,32,64,128,256}
* Pass Compact encoding/decoding length, set default
* Align default length naming with Compact
This commit is contained in:
committed by
Amaury Martiny
parent
6661cc1b81
commit
dc887977cd
@@ -4,7 +4,7 @@
|
||||
|
||||
import u8aConcat from '@polkadot/util/u8a/concat';
|
||||
|
||||
import Compact from './codec/Compact';
|
||||
import Compact, { DEFAULT_LENGTH_BITS } from './codec/Compact';
|
||||
import U8a from './codec/U8a';
|
||||
|
||||
// A Bytes. The significant difference between this and a normal Uint8Array is that
|
||||
@@ -16,11 +16,11 @@ export default class Bytes extends U8a {
|
||||
}
|
||||
|
||||
byteLength (): number {
|
||||
return this.length + Compact.encode(this.length).length;
|
||||
return this.length + Compact.encodeU8a(this.length, DEFAULT_LENGTH_BITS).length;
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): Bytes {
|
||||
const [offset, length] = Compact.decode(input);
|
||||
const [offset, length] = Compact.decodeU8a(input, DEFAULT_LENGTH_BITS);
|
||||
|
||||
super.fromU8a(input.subarray(offset, offset + length.toNumber()));
|
||||
|
||||
@@ -31,7 +31,7 @@ export default class Bytes extends U8a {
|
||||
return isBare
|
||||
? this.raw
|
||||
: u8aConcat(
|
||||
Compact.encode(this.length),
|
||||
Compact.encodeU8a(this.length, DEFAULT_LENGTH_BITS),
|
||||
this.raw
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright 2017-2018 @polkadot/types authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import { AnyNumber } from './types';
|
||||
|
||||
import Compact from './codec/Compact';
|
||||
|
||||
export default class Compact128 extends Compact {
|
||||
constructor (value?: AnyNumber) {
|
||||
super(value, 128);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright 2017-2018 @polkadot/types authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import { AnyNumber } from './types';
|
||||
|
||||
import Compact from './codec/Compact';
|
||||
|
||||
export default class Compact16 extends Compact {
|
||||
constructor (value?: AnyNumber) {
|
||||
super(value, 16, false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright 2017-2018 @polkadot/types authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import { AnyNumber } from './types';
|
||||
|
||||
import Compact from './codec/Compact';
|
||||
|
||||
export default class Compact256 extends Compact {
|
||||
constructor (value?: AnyNumber) {
|
||||
super(value, 256);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright 2017-2018 @polkadot/types authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import { AnyNumber } from './types';
|
||||
|
||||
import Compact from './codec/Compact';
|
||||
|
||||
export default class Compact32 extends Compact {
|
||||
constructor (value?: AnyNumber) {
|
||||
super(value, 32, false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright 2017-2018 @polkadot/types authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import { AnyNumber } from './types';
|
||||
|
||||
import Compact from './codec/Compact';
|
||||
|
||||
export default class Compact64 extends Compact {
|
||||
constructor (value?: AnyNumber) {
|
||||
super(value, 64, false);
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import hexToU8a from '@polkadot/util/hex/toU8a';
|
||||
import u8aConcat from '@polkadot/util/u8a/concat';
|
||||
import u8aToHex from '@polkadot/util/u8a/toHex';
|
||||
|
||||
import Compact from './codec/Compact';
|
||||
import Compact, { DEFAULT_LENGTH_BITS } from './codec/Compact';
|
||||
import Struct from './codec/Struct';
|
||||
import ExtrinsicSignature from './ExtrinsicSignature';
|
||||
import Hash from './Hash';
|
||||
@@ -79,7 +79,7 @@ export default class Extrinsic extends Struct {
|
||||
byteLength (): number {
|
||||
const length = this.length;
|
||||
|
||||
return length + Compact.encode(length).length;
|
||||
return length + Compact.encodeU8a(length, DEFAULT_LENGTH_BITS).length;
|
||||
}
|
||||
|
||||
fromJSON (input: any): Extrinsic {
|
||||
@@ -89,7 +89,7 @@ export default class Extrinsic extends Struct {
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): Extrinsic {
|
||||
const [offset, length] = Compact.decode(input);
|
||||
const [offset, length] = Compact.decodeU8a(input, DEFAULT_LENGTH_BITS);
|
||||
|
||||
super.fromU8a(input.subarray(offset, offset + length.toNumber()));
|
||||
|
||||
@@ -108,7 +108,7 @@ export default class Extrinsic extends Struct {
|
||||
return isBare
|
||||
? encoded
|
||||
: u8aConcat(
|
||||
Compact.encode(encoded.length),
|
||||
Compact.encodeU8a(encoded.length, DEFAULT_LENGTH_BITS),
|
||||
encoded
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import u8aToUtf8 from '@polkadot/util/u8a/toUtf8';
|
||||
import u8aConcat from '@polkadot/util/u8a/concat';
|
||||
|
||||
import Base from './codec/Base';
|
||||
import Compact from './codec/Compact';
|
||||
import Compact, { DEFAULT_LENGTH_BITS } from './codec/Compact';
|
||||
|
||||
// This is a string wrapper, along with the length. It is used both for strings as well
|
||||
// as stuff like documentation.
|
||||
@@ -31,11 +31,11 @@ export default class Text extends Base<string> {
|
||||
}
|
||||
|
||||
byteLength (): number {
|
||||
return this.length + Compact.encode(this.length).length;
|
||||
return this.length + Compact.encodeU8a(this.length, DEFAULT_LENGTH_BITS).length;
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): Text {
|
||||
const [offset, length] = Compact.decode(input);
|
||||
const [offset, length] = Compact.decodeU8a(input, DEFAULT_LENGTH_BITS);
|
||||
|
||||
this.raw = u8aToUtf8(input.subarray(offset, offset + length.toNumber()));
|
||||
|
||||
@@ -62,7 +62,7 @@ export default class Text extends Base<string> {
|
||||
return isBare
|
||||
? encoded
|
||||
: u8aConcat(
|
||||
Compact.encode(this.length),
|
||||
Compact.encodeU8a(this.length, DEFAULT_LENGTH_BITS),
|
||||
encoded
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,10 +8,10 @@ import Compact from './Compact';
|
||||
import UInt from './UInt';
|
||||
|
||||
describe('Compact', () => {
|
||||
describe('encode', () => {
|
||||
describe('encodeU8a', () => {
|
||||
it('encodes short u8', () => {
|
||||
expect(
|
||||
Compact.encode(18)
|
||||
Compact.encodeU8a(18, 8)
|
||||
).toEqual(
|
||||
new Uint8Array([18 << 2])
|
||||
);
|
||||
@@ -19,7 +19,7 @@ describe('Compact', () => {
|
||||
|
||||
it('encodes max u8 values', () => {
|
||||
expect(
|
||||
Compact.encode(new UInt(63))
|
||||
Compact.encodeU8a(new UInt(63), 16)
|
||||
).toEqual(
|
||||
new Uint8Array([0b11111100])
|
||||
);
|
||||
@@ -27,7 +27,7 @@ describe('Compact', () => {
|
||||
|
||||
it('encodes basic u16 value', () => {
|
||||
expect(
|
||||
Compact.encode(511)
|
||||
Compact.encodeU8a(511, 32)
|
||||
).toEqual(
|
||||
new Uint8Array([0b11111101, 0b00000111])
|
||||
);
|
||||
@@ -35,7 +35,7 @@ describe('Compact', () => {
|
||||
|
||||
it('encodes basic ua6 (not at edge)', () => {
|
||||
expect(
|
||||
Compact.encode(111)
|
||||
Compact.encodeU8a(111, 32)
|
||||
).toEqual(
|
||||
new Uint8Array([0xbd, 0x01])
|
||||
);
|
||||
@@ -43,7 +43,7 @@ describe('Compact', () => {
|
||||
|
||||
it('encodes basic u32 values (short)', () => {
|
||||
expect(
|
||||
Compact.encode(0xffff)
|
||||
Compact.encodeU8a(0xffff, 32)
|
||||
).toEqual(
|
||||
new Uint8Array([254, 255, 3, 0])
|
||||
);
|
||||
@@ -51,36 +51,60 @@ describe('Compact', () => {
|
||||
|
||||
it('encodes basic u32 values (full)', () => {
|
||||
expect(
|
||||
Compact.encode(0xfffffff9)
|
||||
Compact.encodeU8a(0xfffffff9, 32)
|
||||
).toEqual(
|
||||
new Uint8Array([3, 249, 255, 255, 255])
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('decode', () => {
|
||||
describe('decodeU8a', () => {
|
||||
it('decoded u8 value', () => {
|
||||
expect(
|
||||
Compact.decode(new Uint8Array([0b11111100]))
|
||||
Compact.decodeU8a(new Uint8Array([0b11111100]), 32)
|
||||
).toEqual([1, new BN(63)]);
|
||||
});
|
||||
|
||||
it('decodes from same u16 encoded value', () => {
|
||||
expect(
|
||||
Compact.decode(new Uint8Array([0b11111101, 0b00000111]))
|
||||
Compact.decodeU8a(new Uint8Array([0b11111101, 0b00000111]), 32)
|
||||
).toEqual([2, new BN(511)]);
|
||||
});
|
||||
|
||||
it('decodes from same u32 encoded value (short)', () => {
|
||||
expect(
|
||||
Compact.decode(new Uint8Array([254, 255, 3, 0]))
|
||||
Compact.decodeU8a(new Uint8Array([254, 255, 3, 0]), 32)
|
||||
).toEqual([4, new BN(0xffff)]);
|
||||
});
|
||||
|
||||
it('decodes from same u32 encoded value (full)', () => {
|
||||
expect(
|
||||
Compact.decode(new Uint8Array([3, 249, 255, 255, 255]))
|
||||
Compact.decodeU8a(new Uint8Array([3, 249, 255, 255, 255]), 32)
|
||||
).toEqual([5, new BN(0xfffffff9)]);
|
||||
});
|
||||
|
||||
it('decodes from same u32 as u64 encoded value (full, default)', () => {
|
||||
expect(
|
||||
Compact.decodeU8a(new Uint8Array([3, 249, 255, 255, 255]), 64)
|
||||
).toEqual([9, new BN(0xfffffff9)]);
|
||||
});
|
||||
});
|
||||
|
||||
it('has the correct byteLength for constructor values (default)', () => {
|
||||
expect(
|
||||
new Compact(0xfffffff9).byteLength()
|
||||
).toEqual(9);
|
||||
});
|
||||
|
||||
it('has the correct byteLength for constructor values (u32)', () => {
|
||||
expect(
|
||||
new Compact(0xfffffff9, 32).byteLength()
|
||||
).toEqual(5);
|
||||
});
|
||||
|
||||
it('constructs properly via fromU8a', () => {
|
||||
expect(
|
||||
new Compact().fromU8a(new Uint8Array([254, 255, 3, 0])).raw
|
||||
).toEqual(new BN(0xffff));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,15 +9,13 @@ import u8aConcat from '@polkadot/util/u8a/concat';
|
||||
import u8aToBn from '@polkadot/util/u8a/toBn';
|
||||
import toU8a from '@polkadot/util/u8a/toU8a';
|
||||
|
||||
import UInt from './UInt';
|
||||
import UInt, { UIntBitLength } from './UInt';
|
||||
|
||||
// TODO Could allow for 64 & 128 https://github.com/paritytech/parity-codec/pull/6
|
||||
type BitLength = 32;
|
||||
export const DEFAULT_LENGTH_BITS = 32;
|
||||
|
||||
const MAX_U8 = new BN(2).pow(new BN(8 - 2)).subn(1);
|
||||
const MAX_U16 = new BN(2).pow(new BN(16 - 2)).subn(1);
|
||||
const MAX_U32 = new BN(2).pow(new BN(32 - 2)).subn(1);
|
||||
const DEFAULT_BITLENGTH = 32;
|
||||
|
||||
// A new compact length-encoding algorithm. It performs the same function as Length, however
|
||||
// differs in that it uses a variable number of bytes to do the actual encoding. From the Rust
|
||||
@@ -34,8 +32,8 @@ const DEFAULT_BITLENGTH = 32;
|
||||
// nn nn nn 11 [ / zz zz zz zz ]{4 + n}
|
||||
//
|
||||
// Note: we use *LOW BITS* of the LSB in LE encoding to encode the 2 bit key.
|
||||
export default class Compact {
|
||||
static decode (_input: Uint8Array | string, bitLength: BitLength = DEFAULT_BITLENGTH): [number, BN] {
|
||||
export default class Compact extends UInt {
|
||||
static decodeU8a (_input: Uint8Array | string, bitLength: UIntBitLength): [number, BN] {
|
||||
const input = toU8a(_input);
|
||||
const flag = input[0] & 0b11;
|
||||
|
||||
@@ -52,24 +50,40 @@ export default class Compact {
|
||||
return [byteLength + 1, u8aToBn(input.subarray(1, 1 + byteLength), true)];
|
||||
}
|
||||
|
||||
static encode (_length: UInt | BN | number, bitLength: BitLength = DEFAULT_BITLENGTH): Uint8Array {
|
||||
const length = _length instanceof UInt
|
||||
? _length.toBn()
|
||||
: bnToBn(_length);
|
||||
static encodeU8a (_value: UInt | BN | number, bitLength: UIntBitLength): Uint8Array {
|
||||
const value = _value instanceof UInt
|
||||
? _value.toBn()
|
||||
: bnToBn(_value);
|
||||
|
||||
if (length.lte(MAX_U8)) {
|
||||
return new Uint8Array([length.toNumber() << 2]);
|
||||
} else if (length.lte(MAX_U16)) {
|
||||
return bnToU8a(length.shln(2).addn(0b01), 16, true);
|
||||
} else if (length.lte(MAX_U32)) {
|
||||
return bnToU8a(length.shln(2).addn(0b10), 32, true);
|
||||
if (value.lte(MAX_U8)) {
|
||||
return new Uint8Array([value.toNumber() << 2]);
|
||||
} else if (value.lte(MAX_U16)) {
|
||||
return bnToU8a(value.shln(2).addn(0b01), 16, true);
|
||||
} else if (value.lte(MAX_U32)) {
|
||||
return bnToU8a(value.shln(2).addn(0b10), 32, true);
|
||||
}
|
||||
|
||||
return u8aConcat(
|
||||
new Uint8Array([
|
||||
0b11
|
||||
]),
|
||||
bnToU8a(length, bitLength, true)
|
||||
bnToU8a(value, bitLength, true)
|
||||
);
|
||||
}
|
||||
|
||||
byteLength (): number {
|
||||
return this.toU8a().length;
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): UInt {
|
||||
const [, value] = Compact.decodeU8a(input, this._bitLength);
|
||||
|
||||
this.raw = value;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
toU8a (isBare?: boolean): Uint8Array {
|
||||
return Compact.encodeU8a(this.raw, this._bitLength);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,9 @@ import u8aToBn from '@polkadot/util/u8a/toBn';
|
||||
|
||||
import Base from './Base';
|
||||
|
||||
type BitLength = 8 | 16 | 32 | 64 | 128 | 256;
|
||||
export type UIntBitLength = 8 | 16 | 32 | 64 | 128 | 256;
|
||||
|
||||
export const DEFAULT_UINT_BITS = 64;
|
||||
|
||||
// A generic number codec. For Substrate all numbers are LE encoded, this handles the encoding
|
||||
// and decoding of those numbers. Upon construction the bitLength is provided and any additional
|
||||
@@ -25,10 +27,10 @@ type BitLength = 8 | 16 | 32 | 64 | 128 | 256;
|
||||
// TODO:
|
||||
// - Apart from encoding/decoding we don't actuall keep check on the sizes, is this good enough?
|
||||
export default class UInt extends Base<BN> {
|
||||
private _bitLength: BitLength;
|
||||
protected _bitLength: UIntBitLength;
|
||||
private _isHexJson: boolean;
|
||||
|
||||
constructor (value: AnyNumber = 0, bitLength: BitLength = 64, isHexJson: boolean = true) {
|
||||
constructor (value: AnyNumber = 0, bitLength: UIntBitLength = DEFAULT_UINT_BITS, isHexJson: boolean = true) {
|
||||
super(
|
||||
UInt.decode(value)
|
||||
);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import u8aConcat from '@polkadot/util/u8a/concat';
|
||||
|
||||
import Base from './Base';
|
||||
import Compact from './Compact';
|
||||
import Compact, { DEFAULT_LENGTH_BITS } from './Compact';
|
||||
|
||||
// This manages codec arrays. Intrernally it keeps track of the length (as decoded) and allows
|
||||
// construction with the passed `Type` in the constructor. It aims to be an array-like structure,
|
||||
@@ -48,7 +48,7 @@ export default class Vector <
|
||||
byteLength (): number {
|
||||
return this.raw.reduce((total, raw) => {
|
||||
return total + raw.byteLength();
|
||||
}, Compact.encode(this.length).length);
|
||||
}, Compact.encodeU8a(this.length, DEFAULT_LENGTH_BITS).length);
|
||||
}
|
||||
|
||||
filter (fn: (item: T, index?: number) => any): Array<T> {
|
||||
@@ -72,7 +72,7 @@ export default class Vector <
|
||||
}
|
||||
|
||||
fromU8a (input: Uint8Array): Vector<T> {
|
||||
let [offset, _length] = Compact.decode(input);
|
||||
let [offset, _length] = Compact.decodeU8a(input, DEFAULT_LENGTH_BITS);
|
||||
const length = _length.toNumber();
|
||||
|
||||
this.raw = [];
|
||||
@@ -117,7 +117,7 @@ export default class Vector <
|
||||
return isBare
|
||||
? u8aConcat(...encoded)
|
||||
: u8aConcat(
|
||||
Compact.encode(this.length),
|
||||
Compact.encodeU8a(this.length, DEFAULT_LENGTH_BITS),
|
||||
...encoded
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,11 @@ export { default as Block } from './Block';
|
||||
export { default as BlockNumber } from './BlockNumber';
|
||||
export { default as Bool } from './Bool';
|
||||
export { default as Bytes } from './Bytes';
|
||||
export { default as Compact16 } from './Compact16';
|
||||
export { default as Compact32 } from './Compact32';
|
||||
export { default as Compact64 } from './Compact64';
|
||||
export { default as Compact128 } from './Compact128';
|
||||
export { default as Compact256 } from './Compact256';
|
||||
export { default as Extrinsic } from './Extrinsic';
|
||||
export { default as ExtrinsicEra } from './ExtrinsicEra';
|
||||
export { default as ExtrinsicSignature } from './ExtrinsicSignature';
|
||||
|
||||
Reference in New Issue
Block a user