mirror of
https://github.com/Microsoft/vscode
synced 2024-07-07 19:50:26 +00:00
ipc: use vql for uint types (#167407)
* ipc: use vql for uint types On the plane I was reverse-engineering ipc.ts to implement it in Rust and see if we could have a "service mode" for the CLI that we could interact with like any other vscode process. In doing so, I noticed that numbers in the protocol--which are used at least twice in the message header and ID--were encoded as JSON. I was curious what benefits we'd get from encoding them as variable-length integers instead. It makes the message shorter, as expected. Encode/decode time are very, very slightly lower. I'm not sure it's worth the extra complexity, but I have included it here for your consideration. * fixup tests
This commit is contained in:
parent
07956026d7
commit
0899758dae
|
@ -164,7 +164,50 @@ interface IWriter {
|
|||
write(buffer: VSBuffer): void;
|
||||
}
|
||||
|
||||
class BufferReader implements IReader {
|
||||
|
||||
/**
|
||||
* @see https://en.wikipedia.org/wiki/Variable-length_quantity
|
||||
*/
|
||||
function readIntVQL(reader: IReader) {
|
||||
let value = 0;
|
||||
for (let n = 0; ; n += 7) {
|
||||
const next = reader.read(1);
|
||||
value |= (next.buffer[0] & 0b01111111) << n;
|
||||
if (!(next.buffer[0] & 0b10000000)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const vqlZero = createOneByteBuffer(0);
|
||||
|
||||
/**
|
||||
* @see https://en.wikipedia.org/wiki/Variable-length_quantity
|
||||
*/
|
||||
function writeInt32VQL(writer: IWriter, value: number) {
|
||||
if (value === 0) {
|
||||
writer.write(vqlZero);
|
||||
return;
|
||||
}
|
||||
|
||||
let len = 0;
|
||||
for (let v2 = value; v2 !== 0; v2 = v2 >>> 7) {
|
||||
len++;
|
||||
}
|
||||
|
||||
const scratch = VSBuffer.alloc(len);
|
||||
for (let i = 0; value !== 0; i++) {
|
||||
scratch.buffer[i] = value & 0b01111111;
|
||||
value = value >>> 7;
|
||||
if (value > 0) {
|
||||
scratch.buffer[i] |= 0b10000000;
|
||||
}
|
||||
}
|
||||
|
||||
writer.write(scratch);
|
||||
}
|
||||
|
||||
export class BufferReader implements IReader {
|
||||
|
||||
private pos = 0;
|
||||
|
||||
|
@ -177,7 +220,7 @@ class BufferReader implements IReader {
|
|||
}
|
||||
}
|
||||
|
||||
class BufferWriter implements IWriter {
|
||||
export class BufferWriter implements IWriter {
|
||||
|
||||
private buffers: VSBuffer[] = [];
|
||||
|
||||
|
@ -196,17 +239,8 @@ enum DataType {
|
|||
Buffer = 2,
|
||||
VSBuffer = 3,
|
||||
Array = 4,
|
||||
Object = 5
|
||||
}
|
||||
|
||||
function createSizeBuffer(size: number): VSBuffer {
|
||||
const result = VSBuffer.alloc(4);
|
||||
result.writeUInt32BE(size, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
function readSizeBuffer(reader: IReader): number {
|
||||
return reader.read(4).readUInt32BE(0);
|
||||
Object = 5,
|
||||
Int = 6
|
||||
}
|
||||
|
||||
function createOneByteBuffer(value: number): VSBuffer {
|
||||
|
@ -222,53 +256,58 @@ const BufferPresets = {
|
|||
VSBuffer: createOneByteBuffer(DataType.VSBuffer),
|
||||
Array: createOneByteBuffer(DataType.Array),
|
||||
Object: createOneByteBuffer(DataType.Object),
|
||||
Uint: createOneByteBuffer(DataType.Int),
|
||||
};
|
||||
|
||||
declare const Buffer: any;
|
||||
const hasBuffer = (typeof Buffer !== 'undefined');
|
||||
|
||||
function serialize(writer: IWriter, data: any): void {
|
||||
export function serialize(writer: IWriter, data: any): void {
|
||||
if (typeof data === 'undefined') {
|
||||
writer.write(BufferPresets.Undefined);
|
||||
} else if (typeof data === 'string') {
|
||||
const buffer = VSBuffer.fromString(data);
|
||||
writer.write(BufferPresets.String);
|
||||
writer.write(createSizeBuffer(buffer.byteLength));
|
||||
writeInt32VQL(writer, buffer.byteLength);
|
||||
writer.write(buffer);
|
||||
} else if (hasBuffer && Buffer.isBuffer(data)) {
|
||||
const buffer = VSBuffer.wrap(data);
|
||||
writer.write(BufferPresets.Buffer);
|
||||
writer.write(createSizeBuffer(buffer.byteLength));
|
||||
writeInt32VQL(writer, buffer.byteLength);
|
||||
writer.write(buffer);
|
||||
} else if (data instanceof VSBuffer) {
|
||||
writer.write(BufferPresets.VSBuffer);
|
||||
writer.write(createSizeBuffer(data.byteLength));
|
||||
writeInt32VQL(writer, data.byteLength);
|
||||
writer.write(data);
|
||||
} else if (Array.isArray(data)) {
|
||||
writer.write(BufferPresets.Array);
|
||||
writer.write(createSizeBuffer(data.length));
|
||||
writeInt32VQL(writer, data.length);
|
||||
|
||||
for (const el of data) {
|
||||
serialize(writer, el);
|
||||
}
|
||||
} else if (typeof data === 'number' && (data | 0) === data) {
|
||||
// write a vql if it's a number that we can do bitwise operations on
|
||||
writer.write(BufferPresets.Uint);
|
||||
writeInt32VQL(writer, data);
|
||||
} else {
|
||||
const buffer = VSBuffer.fromString(JSON.stringify(data));
|
||||
writer.write(BufferPresets.Object);
|
||||
writer.write(createSizeBuffer(buffer.byteLength));
|
||||
writeInt32VQL(writer, buffer.byteLength);
|
||||
writer.write(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
function deserialize(reader: IReader): any {
|
||||
export function deserialize(reader: IReader): any {
|
||||
const type = reader.read(1).readUInt8(0);
|
||||
|
||||
switch (type) {
|
||||
case DataType.Undefined: return undefined;
|
||||
case DataType.String: return reader.read(readSizeBuffer(reader)).toString();
|
||||
case DataType.Buffer: return reader.read(readSizeBuffer(reader)).buffer;
|
||||
case DataType.VSBuffer: return reader.read(readSizeBuffer(reader));
|
||||
case DataType.String: return reader.read(readIntVQL(reader)).toString();
|
||||
case DataType.Buffer: return reader.read(readIntVQL(reader)).buffer;
|
||||
case DataType.VSBuffer: return reader.read(readIntVQL(reader));
|
||||
case DataType.Array: {
|
||||
const length = readSizeBuffer(reader);
|
||||
const length = readIntVQL(reader);
|
||||
const result: any[] = [];
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
|
@ -277,7 +316,8 @@ function deserialize(reader: IReader): any {
|
|||
|
||||
return result;
|
||||
}
|
||||
case DataType.Object: return JSON.parse(reader.read(readSizeBuffer(reader)).toString());
|
||||
case DataType.Object: return JSON.parse(reader.read(readIntVQL(reader)).toString());
|
||||
case DataType.Int: return readIntVQL(reader);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import { canceled } from 'vs/base/common/errors';
|
|||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ClientConnectionEvent, IChannel, IMessagePassingProtocol, IPCClient, IPCServer, IServerChannel, ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { BufferReader, BufferWriter, ClientConnectionEvent, deserialize, IChannel, IMessagePassingProtocol, IPCClient, IPCServer, IServerChannel, ProxyChannel, serialize } from 'vs/base/parts/ipc/common/ipc';
|
||||
|
||||
class QueueProtocol implements IMessagePassingProtocol {
|
||||
|
||||
|
@ -319,6 +319,22 @@ suite('Base IPC', function () {
|
|||
const r = await ipcService.buffersLength([VSBuffer.alloc(2), VSBuffer.alloc(3)]);
|
||||
return assert.strictEqual(r, 5);
|
||||
});
|
||||
|
||||
test('round trips numbers', () => {
|
||||
const input = [
|
||||
0,
|
||||
1,
|
||||
-1,
|
||||
12345,
|
||||
-12345,
|
||||
42.6,
|
||||
123412341234
|
||||
];
|
||||
|
||||
const writer = new BufferWriter();
|
||||
serialize(writer, input);
|
||||
assert.deepStrictEqual(deserialize(new BufferReader(writer.buffer)), input);
|
||||
});
|
||||
});
|
||||
|
||||
suite('one to one (proxy)', function () {
|
||||
|
|
|
@ -27,7 +27,7 @@ Unit tests from layers `common` and `browser` are run inside `chromium`, `webkit
|
|||
|
||||
## Run (with node)
|
||||
|
||||
yarn run mocha --ui tdd --run src/vs/editor/test/browser/controller/cursor.test.ts
|
||||
yarn test-node --run src/vs/editor/test/browser/controller/cursor.test.ts
|
||||
|
||||
## Coverage
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user