Skip to content

Commit

Permalink
Add Compact{16,32,64,128,256}
Browse files Browse the repository at this point in the history
  • Loading branch information
jacogr committed Oct 6, 2018
1 parent 682aee7 commit af7b247
Show file tree
Hide file tree
Showing 13 changed files with 153 additions and 45 deletions.
6 changes: 3 additions & 3 deletions packages/types/src/Bytes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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).length;
}

fromU8a (input: Uint8Array): Bytes {
const [offset, length] = Compact.decode(input);
const [offset, length] = Compact.decodeU8a(input);

super.fromU8a(input.subarray(offset, offset + length.toNumber()));

Expand All @@ -31,7 +31,7 @@ export default class Bytes extends U8a {
return isBare
? this.raw
: u8aConcat(
Compact.encode(this.length),
Compact.encodeU8a(this.length),
this.raw
);
}
Expand Down
13 changes: 13 additions & 0 deletions packages/types/src/Compact128.ts
Original file line number Diff line number Diff line change
@@ -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);
}
}
13 changes: 13 additions & 0 deletions packages/types/src/Compact16.ts
Original file line number Diff line number Diff line change
@@ -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);
}
}
13 changes: 13 additions & 0 deletions packages/types/src/Compact256.ts
Original file line number Diff line number Diff line change
@@ -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);
}
}
13 changes: 13 additions & 0 deletions packages/types/src/Compact32.ts
Original file line number Diff line number Diff line change
@@ -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);
}
}
13 changes: 13 additions & 0 deletions packages/types/src/Compact64.ts
Original file line number Diff line number Diff line change
@@ -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);
}
}
6 changes: 3 additions & 3 deletions packages/types/src/Extrinsic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export default class Extrinsic extends Struct {
byteLength (): number {
const length = this.length;

return length + Compact.encode(length).length;
return length + Compact.encodeU8a(length).length;
}

fromJSON (input: any): Extrinsic {
Expand All @@ -92,7 +92,7 @@ export default class Extrinsic extends Struct {
}

fromU8a (input: Uint8Array): Extrinsic {
const [offset, length] = Compact.decode(input);
const [offset, length] = Compact.decodeU8a(input);

super.fromU8a(input.subarray(offset, offset + length.toNumber()));

Expand All @@ -111,7 +111,7 @@ export default class Extrinsic extends Struct {
return isBare
? encoded
: u8aConcat(
Compact.encode(encoded.length),
Compact.encodeU8a(encoded.length),
encoded
);
}
Expand Down
6 changes: 3 additions & 3 deletions packages/types/src/Text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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).length;
}

fromU8a (input: Uint8Array): Text {
const [offset, length] = Compact.decode(input);
const [offset, length] = Compact.decodeU8a(input);

this.raw = u8aToUtf8(input.subarray(offset, offset + length.toNumber()));

Expand All @@ -62,7 +62,7 @@ export default class Text extends Base<string> {
return isBare
? encoded
: u8aConcat(
Compact.encode(this.length),
Compact.encodeU8a(this.length),
encoded
);
}
Expand Down
48 changes: 36 additions & 12 deletions packages/types/src/codec/Compact.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,79 +8,103 @@ 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)
).toEqual(
new Uint8Array([18 << 2])
);
});

it('encodes max u8 values', () => {
expect(
Compact.encode(new UInt(63))
Compact.encodeU8a(new UInt(63))
).toEqual(
new Uint8Array([0b11111100])
);
});

it('encodes basic u16 value', () => {
expect(
Compact.encode(511)
Compact.encodeU8a(511)
).toEqual(
new Uint8Array([0b11111101, 0b00000111])
);
});

it('encodes basic ua6 (not at edge)', () => {
expect(
Compact.encode(111)
Compact.encodeU8a(111)
).toEqual(
new Uint8Array([0xbd, 0x01])
);
});

it('encodes basic u32 values (short)', () => {
expect(
Compact.encode(0xffff)
Compact.encodeU8a(0xffff)
).toEqual(
new Uint8Array([254, 255, 3, 0])
);
});

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]))
).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]))
).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]))
).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]))
).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));
});
});
48 changes: 30 additions & 18 deletions packages/types/src/codec/Compact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,11 @@ 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';

// TODO Could allow for 64 & 128 https://github.com/paritytech/parity-codec/pull/6
type BitLength = 32;
import UInt, { UIntBitLength, DEFAULT_BITLENGTH } from './UInt';

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
Expand All @@ -34,8 +30,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 = DEFAULT_BITLENGTH): [number, BN] {
const input = toU8a(_input);
const flag = input[0] & 0b11;

Expand All @@ -52,24 +48,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 = DEFAULT_BITLENGTH): 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);
}
}
8 changes: 5 additions & 3 deletions packages/types/src/codec/UInt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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_BITLENGTH = 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
Expand All @@ -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_BITLENGTH, isHexJson: boolean = true) {
super(
UInt.decode(value)
);
Expand Down
6 changes: 3 additions & 3 deletions packages/types/src/codec/Vector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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).length);
}

filter (fn: (item: T, index?: number) => any): Array<T> {
Expand All @@ -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);
const length = _length.toNumber();

this.raw = [];
Expand Down Expand Up @@ -117,7 +117,7 @@ export default class Vector <
return isBare
? u8aConcat(...encoded)
: u8aConcat(
Compact.encode(this.length),
Compact.encodeU8a(this.length),
...encoded
);
}
Expand Down

0 comments on commit af7b247

Please sign in to comment.