Skip to content

Commit

Permalink
add encodeMessage util w/ tests and a little refactoring (jerryscript…
Browse files Browse the repository at this point in the history
…-project#11)

JerryScript-DCO-1.0-Signed-off-by: Geoff Gustafson [email protected]
  • Loading branch information
grgustaf authored and martijnthe committed Mar 19, 2018
1 parent 8b889ab commit 57ccd02
Show file tree
Hide file tree
Showing 2 changed files with 253 additions and 11 deletions.
136 changes: 135 additions & 1 deletion jerry-debugger/src/lib/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { getFormatSize, decodeMessage, cesu8ToString, assembleUint8Arrays } from '../utils';
import { getFormatSize, getUint32, setUint32, decodeMessage, encodeMessage,
cesu8ToString, assembleUint8Arrays } from '../utils';

const defConfig = {
cpointerSize: 2,
Expand Down Expand Up @@ -59,6 +60,43 @@ describe('getFormatSize', () => {
});
});

describe('getUint32', () => {
it('reads little endian values', () => {
const array = Uint8Array.from([0xef, 0xbe, 0xad, 0xde]);
expect(getUint32(true, array, 0)).toEqual(0xdeadbeef);
});

it('reads big endian values', () => {
const array = Uint8Array.from([0xde, 0xad, 0xbe, 0xef]);
expect(getUint32(false, array, 0)).toEqual(0xdeadbeef);
});

it('reads at an offset', () => {
const array = Uint8Array.from([0x00, 0x00, 0xde, 0xad, 0xbe, 0xef]);
expect(getUint32(false, array, 2)).toEqual(0xdeadbeef);
});
});

describe('setUint32', () => {
it('writes little endian values', () => {
const array = new Uint8Array(4);
setUint32(true, array, 0, 0xdeadbeef);
expect(array).toEqual(Uint8Array.from([0xef, 0xbe, 0xad, 0xde]));
});

it('writes big endian values', () => {
const array = new Uint8Array(4);
setUint32(false, array, 0, 0xdeadbeef);
expect(array).toEqual(Uint8Array.from([0xde, 0xad, 0xbe, 0xef]));
});

it('writes at an offset', () => {
const array = new Uint8Array(6);
setUint32(false, array, 2, 0xdeadbeef);
expect(array).toEqual(Uint8Array.from([0x00, 0x00, 0xde, 0xad, 0xbe, 0xef]));
});
});

describe('decodeMessage', () => {
it('throws if message too short', () => {
const array = Uint8Array.from([0, 1, 2]);
Expand Down Expand Up @@ -129,6 +167,102 @@ describe('decodeMessage', () => {
});
});

describe('encodeMessage', () => {
it('throws if value list too short', () => {
expect(() => {
encodeMessage(defConfig, 'BI', [42]);
}).toThrow();
});

it('throws on unexpected format character', () => {
expect(() => {
encodeMessage(defConfig, 'Q', [42]);
}).toThrow();
});

it('encodes a byte with B character', () => {
const array = encodeMessage(defConfig, 'B', [42]);
expect(array).toEqual(Uint8Array.from([42]));
});

it('throws on byte outside range', () => {
expect(() => {
encodeMessage(defConfig, 'B', [-1]);
}).toThrow();
expect(() => {
encodeMessage(defConfig, 'B', [0x100]);
}).toThrow();
});

it('encodes two bytes for C with default config', () => {
const array = encodeMessage(defConfig, 'C', [1 + (2 << 8)]);
expect(array).toEqual(Uint8Array.from([1, 2]));
});

it('encodes two bytes for C with big endian', () => {
const array = encodeMessage({
cpointerSize: 2,
littleEndian: false,
}, 'C', [(1 << 8) + 2]);
expect(array).toEqual(Uint8Array.from([1, 2]));
});

it('throws on two bytes outside range', () => {
expect(() => {
encodeMessage(defConfig, 'C', [-1]);
}).toThrow();
expect(() => {
encodeMessage(defConfig, 'C', [0x10000]);
}).toThrow();
});

it('encodes four bytes for C with default config', () => {
const array = encodeMessage(altConfig, 'C', [1 + (2 << 8) + (3 << 16) + (4 << 24)]);
expect(array).toEqual(Uint8Array.from([1, 2, 3, 4]));
});

it('encodes four bytes for C with big endian', () => {
const array = encodeMessage({
cpointerSize: 4,
littleEndian: false,
}, 'C', [(1 << 24) + (2 << 16) + (3 << 8) + 4]);
expect(array).toEqual(Uint8Array.from([1, 2, 3, 4]));
});

it('throws on float', () => {
expect(() => {
encodeMessage(defConfig, 'I', [4.2]);
}).toThrow();
});

it('handles multiple format characters', () => {
const array = encodeMessage(defConfig, 'IBC', [
1 + (2 << 8) + (3 << 16) + (4 << 24),
5,
6 + (7 << 8),
]);
expect(array).toEqual(Uint8Array.from([1, 2, 3, 4, 5, 6, 7]));
});

it('throws on byte outside range', () => {
expect(() => {
encodeMessage({
cpointerSize: 6,
littleEndian: true,
}, 'C', [42]);
}).toThrow();
});

it('throws on unexpected pointer size', () => {
expect(() => {
encodeMessage({
cpointerSize: 6,
littleEndian: true,
}, 'C', [42]);
}).toThrow();
});
});

describe('cesu8ToString', () => {
it('returns empty string for undefined input', () => {
expect(cesu8ToString(undefined)).toEqual('');
Expand Down
128 changes: 118 additions & 10 deletions jerry-debugger/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,61 @@ export function getFormatSize(config: ByteConfig, format: string) {
return length;
}

/**
* Returns a 32-bit integer read from array at offset, in either direction
*
* @param littleEndian Byte order to use
* @param array Array with length >= offset + 3
* @param offset Offset at which to start reading
*/
export function getUint32(littleEndian: boolean, array: Uint8Array, offset: number) {
let value = 0;
if (littleEndian) {
value = array[offset];
value |= array[offset + 1] << 8;
value |= array[offset + 2] << 16;
value |= array[offset + 3] << 24;
} else {
value = array[offset] << 24;
value |= array[offset + 1] << 16;
value |= array[offset + 2] << 8;
value |= array[offset + 3];
}
return value >>> 0;
}

/**
* Writes a 32-bit integer to array at offset, in either direction
*
* @param littleEndian Byte order to use
* @param array Array with length >= offset + 3
* @param offset Offset at which to start writing
* @param value Value to write in 32-bit integer range
*/
export function setUint32(littleEndian: boolean, array: Uint8Array, offset: number, value: number) {
if (littleEndian) {
array[offset] = value & 0xff;
array[offset + 1] = (value >> 8) & 0xff;
array[offset + 2] = (value >> 16) & 0xff;
array[offset + 3] = (value >> 24) & 0xff;
} else {
array[offset] = (value >> 24) & 0xff;
array[offset + 1] = (value >> 16) & 0xff;
array[offset + 2] = (value >> 8) & 0xff;
array[offset + 3] = value & 0xff;
}
}

/**
* Parses values out of message array matching format
*
* Throws if message not big enough for specified format or bogus format character
*
* @param config Byte order / size info
* @param format String of 'B', 'C', and 'I' characters
* @param message Array containing message
* @param offset Optional offset at which to start reading
*/
export function decodeMessage(config: ByteConfig, format: string, message: Uint8Array, offset = 0) {
// Format: B=byte I=int32 C=cpointer
// Returns an array of decoded numbers
Expand All @@ -56,8 +111,7 @@ export function decodeMessage(config: ByteConfig, format: string, message: Uint8

for (let i = 0; i < format.length; i++) {
if (format[i] === 'B') {
result.push(message[offset]);
offset++;
result.push(message[offset++]);
continue;
}

Expand All @@ -77,21 +131,75 @@ export function decodeMessage(config: ByteConfig, format: string, message: Uint8
throw new Error('unexpected decode request');
}

if (config.littleEndian) {
value = (message[offset] | (message[offset + 1] << 8)
| (message[offset + 2] << 16) | (message[offset + 3] << 24));
} else {
value = ((message[offset] << 24) | (message[offset + 1] << 16)
| (message[offset + 2] << 8) | message[offset + 3]);
}

value = getUint32(config.littleEndian, message, offset);
result.push(value);
offset += 4;
}

return result;
}

/**
* Packs values into new array according to format
*
* Throws if not enough values supplied or values exceed expected integer ranges
*
* @param config Byte order / size info
* @param format String of 'B', 'C', and 'I' characters
* @param values Array of values to format into message
*/
export function encodeMessage(config: ByteConfig, format: string, values: Array<number>) {
const length = getFormatSize(config, format);
const message = new Uint8Array(length);
let offset = 0;

if (values.length < format.length) {
throw new Error('not enough values supplied');
}

for (let i = 0; i < format.length; i++) {
const value = values[i];

if (format[i] === 'B') {
if ((value & 0xff) !== value) {
throw new Error('expected byte value');
}
message[offset++] = value;
continue;
}

if (format[i] === 'C' && config.cpointerSize === 2) {
if ((value & 0xffff) !== value) {
throw new Error('expected two-byte value');
}
const lowByte = value & 0xff;
const highByte = (value >> 8) & 0xff;

if (config.littleEndian) {
message[offset++] = lowByte;
message[offset++] = highByte;
} else {
message[offset++] = highByte;
message[offset++] = lowByte;
}
continue;
}

if (format[i] !== 'I' && (format[i] !== 'C' || config.cpointerSize !== 4)) {
throw new Error('unexpected encode request');
}

if ((value & 0xffffffff) !== value) {
throw new Error('expected four-byte value');
}

setUint32(config.littleEndian, message, offset, value);
offset += 4;
}

return message;
}

export function cesu8ToString(array: Uint8Array | undefined) {
if (!array) {
return '';
Expand Down

0 comments on commit 57ccd02

Please sign in to comment.