Stop direct access to socket buffer

Use proper accessor functions instead of poking around in internal
buffers.
This commit is contained in:
Pierre Ossman 2023-05-11 23:06:34 +02:00
parent fb3c8f64e9
commit 0180bc81c1
5 changed files with 43 additions and 76 deletions

View File

@ -31,10 +31,7 @@ export default class HextileDecoder {
return false; return false;
} }
let rQ = sock.rQ; let subencoding = sock.rQpeek8();
let rQi = sock.rQi;
let subencoding = rQ[rQi]; // Peek
if (subencoding > 30) { // Raw if (subencoding > 30) { // Raw
throw new Error("Illegal hextile subencoding (subencoding: " + throw new Error("Illegal hextile subencoding (subencoding: " +
subencoding + ")"); subencoding + ")");
@ -65,7 +62,7 @@ export default class HextileDecoder {
return false; return false;
} }
let subrects = rQ[rQi + bytes - 1]; // Peek let subrects = sock.rQpeekBytes(bytes).at(-1);
if (subencoding & 0x10) { // SubrectsColoured if (subencoding & 0x10) { // SubrectsColoured
bytes += subrects * (4 + 2); bytes += subrects * (4 + 2);
} else { } else {
@ -79,7 +76,7 @@ export default class HextileDecoder {
} }
// We know the encoding and have a whole tile // We know the encoding and have a whole tile
rQi++; sock.rQshift8();
if (subencoding === 0) { if (subencoding === 0) {
if (this._lastsubencoding & 0x01) { if (this._lastsubencoding & 0x01) {
// Weird: ignore blanks are RAW // Weird: ignore blanks are RAW
@ -89,42 +86,36 @@ export default class HextileDecoder {
} }
} else if (subencoding & 0x01) { // Raw } else if (subencoding & 0x01) { // Raw
let pixels = tw * th; let pixels = tw * th;
let data = sock.rQshiftBytes(pixels * 4);
// Max sure the image is fully opaque // Max sure the image is fully opaque
for (let i = 0;i < pixels;i++) { for (let i = 0;i < pixels;i++) {
rQ[rQi + i * 4 + 3] = 255; data[i * 4 + 3] = 255;
} }
display.blitImage(tx, ty, tw, th, rQ, rQi); display.blitImage(tx, ty, tw, th, data, 0);
rQi += bytes - 1;
} else { } else {
if (subencoding & 0x02) { // Background if (subencoding & 0x02) { // Background
this._background = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]]; this._background = new Uint8Array(sock.rQshiftBytes(4));
rQi += 4;
} }
if (subencoding & 0x04) { // Foreground if (subencoding & 0x04) { // Foreground
this._foreground = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]]; this._foreground = new Uint8Array(sock.rQshiftBytes(4));
rQi += 4;
} }
this._startTile(tx, ty, tw, th, this._background); this._startTile(tx, ty, tw, th, this._background);
if (subencoding & 0x08) { // AnySubrects if (subencoding & 0x08) { // AnySubrects
let subrects = rQ[rQi]; let subrects = sock.rQshift8();
rQi++;
for (let s = 0; s < subrects; s++) { for (let s = 0; s < subrects; s++) {
let color; let color;
if (subencoding & 0x10) { // SubrectsColoured if (subencoding & 0x10) { // SubrectsColoured
color = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]]; color = sock.rQshiftBytes(4);
rQi += 4;
} else { } else {
color = this._foreground; color = this._foreground;
} }
const xy = rQ[rQi]; const xy = sock.rQshift8();
rQi++;
const sx = (xy >> 4); const sx = (xy >> 4);
const sy = (xy & 0x0f); const sy = (xy & 0x0f);
const wh = rQ[rQi]; const wh = sock.rQshift8();
rQi++;
const sw = (wh >> 4) + 1; const sw = (wh >> 4) + 1;
const sh = (wh & 0x0f) + 1; const sh = (wh & 0x0f) + 1;
@ -133,7 +124,6 @@ export default class HextileDecoder {
} }
this._finishTile(display); this._finishTile(display);
} }
sock.rQi = rQi;
this._lastsubencoding = subencoding; this._lastsubencoding = subencoding;
this._tiles--; this._tiles--;
} }

View File

@ -33,29 +33,26 @@ export default class RawDecoder {
Math.floor(sock.rQlen / bytesPerLine)); Math.floor(sock.rQlen / bytesPerLine));
const pixels = width * currHeight; const pixels = width * currHeight;
let data = sock.rQ; let data = sock.rQshiftBytes(currHeight * bytesPerLine);
let index = sock.rQi;
// Convert data if needed // Convert data if needed
if (depth == 8) { if (depth == 8) {
const newdata = new Uint8Array(pixels * 4); const newdata = new Uint8Array(pixels * 4);
for (let i = 0; i < pixels; i++) { for (let i = 0; i < pixels; i++) {
newdata[i * 4 + 0] = ((data[index + i] >> 0) & 0x3) * 255 / 3; newdata[i * 4 + 0] = ((data[i] >> 0) & 0x3) * 255 / 3;
newdata[i * 4 + 1] = ((data[index + i] >> 2) & 0x3) * 255 / 3; newdata[i * 4 + 1] = ((data[i] >> 2) & 0x3) * 255 / 3;
newdata[i * 4 + 2] = ((data[index + i] >> 4) & 0x3) * 255 / 3; newdata[i * 4 + 2] = ((data[i] >> 4) & 0x3) * 255 / 3;
newdata[i * 4 + 3] = 255; newdata[i * 4 + 3] = 255;
} }
data = newdata; data = newdata;
index = 0;
} }
// Max sure the image is fully opaque // Max sure the image is fully opaque
for (let i = 0; i < pixels; i++) { for (let i = 0; i < pixels; i++) {
data[index + i * 4 + 3] = 255; data[i * 4 + 3] = 255;
} }
display.blitImage(x, curY, width, currHeight, data, index); display.blitImage(x, curY, width, currHeight, data, 0);
sock.rQskipBytes(currHeight * bytesPerLine);
this._lines -= currHeight; this._lines -= currHeight;
if (this._lines > 0) { if (this._lines > 0) {
return false; return false;

View File

@ -76,12 +76,8 @@ export default class TightDecoder {
return false; return false;
} }
const rQi = sock.rQi; let pixel = sock.rQshiftBytes(3);
const rQ = sock.rQ; display.fillRect(x, y, width, height, pixel, false);
display.fillRect(x, y, width, height,
[rQ[rQi], rQ[rQi + 1], rQ[rQi + 2]], false);
sock.rQskipBytes(3);
return true; return true;
} }

View File

@ -94,22 +94,6 @@ export default class Websock {
return "unknown"; return "unknown";
} }
get sQ() {
return this._sQ;
}
get rQ() {
return this._rQ;
}
get rQi() {
return this._rQi;
}
set rQi(val) {
this._rQi = val;
}
// Receive Queue // Receive Queue
get rQlen() { get rQlen() {
return this._rQlen - this._rQi; return this._rQlen - this._rQi;

View File

@ -19,13 +19,13 @@ describe('Websock', function () {
}); });
describe('rQlen', function () { describe('rQlen', function () {
it('should return the length of the receive queue', function () { it('should return the length of the receive queue', function () {
sock.rQi = 0; sock._rQi = 0;
expect(sock.rQlen).to.equal(RQ_TEMPLATE.length); expect(sock.rQlen).to.equal(RQ_TEMPLATE.length);
}); });
it("should return the proper length if we read some from the receive queue", function () { it("should return the proper length if we read some from the receive queue", function () {
sock.rQi = 1; sock._rQi = 1;
expect(sock.rQlen).to.equal(RQ_TEMPLATE.length - 1); expect(sock.rQlen).to.equal(RQ_TEMPLATE.length - 1);
}); });
@ -72,8 +72,8 @@ describe('Websock', function () {
describe('rQshiftStr', function () { describe('rQshiftStr', function () {
it('should shift the given number of bytes off of the receive queue and return a string', function () { it('should shift the given number of bytes off of the receive queue and return a string', function () {
const befLen = sock.rQlen; const befLen = sock._rQlen;
const befRQi = sock.rQi; const befRQi = sock._rQi;
const shifted = sock.rQshiftStr(3); const shifted = sock.rQshiftStr(3);
expect(shifted).to.be.a('string'); expect(shifted).to.be.a('string');
expect(shifted).to.equal(String.fromCharCode.apply(null, Array.prototype.slice.call(new Uint8Array(RQ_TEMPLATE.buffer, befRQi, 3)))); expect(shifted).to.equal(String.fromCharCode.apply(null, Array.prototype.slice.call(new Uint8Array(RQ_TEMPLATE.buffer, befRQi, 3))));
@ -112,8 +112,8 @@ describe('Websock', function () {
describe('rQshiftBytes', function () { describe('rQshiftBytes', function () {
it('should shift the given number of bytes of the receive queue and return an array', function () { it('should shift the given number of bytes of the receive queue and return an array', function () {
const befLen = sock.rQlen; const befLen = sock._rQlen;
const befRQi = sock.rQi; const befRQi = sock._rQi;
const shifted = sock.rQshiftBytes(3); const shifted = sock.rQshiftBytes(3);
expect(shifted).to.be.an.instanceof(Uint8Array); expect(shifted).to.be.an.instanceof(Uint8Array);
expect(shifted).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, befRQi, 3)); expect(shifted).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, befRQi, 3));
@ -128,7 +128,7 @@ describe('Websock', function () {
describe('rQpeekBytes', function () { describe('rQpeekBytes', function () {
beforeEach(function () { beforeEach(function () {
sock.rQi = 0; sock._rQi = 0;
}); });
it('should not modify the receive queue', function () { it('should not modify the receive queue', function () {
@ -150,14 +150,14 @@ describe('Websock', function () {
}); });
it('should take the current rQi in to account', function () { it('should take the current rQi in to account', function () {
sock.rQi = 1; sock._rQi = 1;
expect(sock.rQpeekBytes(2)).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, 1, 2)); expect(sock.rQpeekBytes(2)).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, 1, 2));
}); });
}); });
describe('rQwait', function () { describe('rQwait', function () {
beforeEach(function () { beforeEach(function () {
sock.rQi = 0; sock._rQi = 0;
}); });
it('should return true if there are not enough bytes in the receive queue', function () { it('should return true if there are not enough bytes in the receive queue', function () {
@ -169,20 +169,20 @@ describe('Websock', function () {
}); });
it('should return true and reduce rQi by "goback" if there are not enough bytes', function () { it('should return true and reduce rQi by "goback" if there are not enough bytes', function () {
sock.rQi = 5; sock._rQi = 5;
expect(sock.rQwait('hi', RQ_TEMPLATE.length, 4)).to.be.true; expect(sock.rQwait('hi', RQ_TEMPLATE.length, 4)).to.be.true;
expect(sock.rQi).to.equal(1); expect(sock._rQi).to.equal(1);
}); });
it('should raise an error if we try to go back more than possible', function () { it('should raise an error if we try to go back more than possible', function () {
sock.rQi = 5; sock._rQi = 5;
expect(() => sock.rQwait('hi', RQ_TEMPLATE.length, 6)).to.throw(Error); expect(() => sock.rQwait('hi', RQ_TEMPLATE.length, 6)).to.throw(Error);
}); });
it('should not reduce rQi if there are enough bytes', function () { it('should not reduce rQi if there are enough bytes', function () {
sock.rQi = 5; sock._rQi = 5;
sock.rQwait('hi', 1, 6); sock.rQwait('hi', 1, 6);
expect(sock.rQi).to.equal(5); expect(sock._rQi).to.equal(5);
}); });
}); });
}); });
@ -461,52 +461,52 @@ describe('Websock', function () {
}); });
it('should compact the receive queue when a message handler empties it', function () { it('should compact the receive queue when a message handler empties it', function () {
sock._eventHandlers.message = () => { sock.rQi = sock._rQlen; }; sock._eventHandlers.message = () => { sock._rQi = sock._rQlen; };
sock._rQ = new Uint8Array([0, 1, 2, 3, 4, 5, 0, 0, 0, 0]); sock._rQ = new Uint8Array([0, 1, 2, 3, 4, 5, 0, 0, 0, 0]);
sock._rQlen = 6; sock._rQlen = 6;
sock.rQi = 6; sock._rQi = 6;
const msg = { data: new Uint8Array([1, 2, 3]).buffer }; const msg = { data: new Uint8Array([1, 2, 3]).buffer };
sock._mode = 'binary'; sock._mode = 'binary';
sock._recvMessage(msg); sock._recvMessage(msg);
expect(sock._rQlen).to.equal(0); expect(sock._rQlen).to.equal(0);
expect(sock.rQi).to.equal(0); expect(sock._rQi).to.equal(0);
}); });
it('should compact the receive queue when we reach the end of the buffer', function () { it('should compact the receive queue when we reach the end of the buffer', function () {
sock._rQ = new Uint8Array(20); sock._rQ = new Uint8Array(20);
sock._rQbufferSize = 20; sock._rQbufferSize = 20;
sock._rQlen = 20; sock._rQlen = 20;
sock.rQi = 10; sock._rQi = 10;
const msg = { data: new Uint8Array([1, 2]).buffer }; const msg = { data: new Uint8Array([1, 2]).buffer };
sock._mode = 'binary'; sock._mode = 'binary';
sock._recvMessage(msg); sock._recvMessage(msg);
expect(sock._rQlen).to.equal(12); expect(sock._rQlen).to.equal(12);
expect(sock.rQi).to.equal(0); expect(sock._rQi).to.equal(0);
}); });
it('should automatically resize the receive queue if the incoming message is larger than the buffer', function () { it('should automatically resize the receive queue if the incoming message is larger than the buffer', function () {
sock._rQ = new Uint8Array(20); sock._rQ = new Uint8Array(20);
sock._rQlen = 0; sock._rQlen = 0;
sock.rQi = 0; sock._rQi = 0;
sock._rQbufferSize = 20; sock._rQbufferSize = 20;
const msg = { data: new Uint8Array(30).buffer }; const msg = { data: new Uint8Array(30).buffer };
sock._mode = 'binary'; sock._mode = 'binary';
sock._recvMessage(msg); sock._recvMessage(msg);
expect(sock._rQlen).to.equal(30); expect(sock._rQlen).to.equal(30);
expect(sock.rQi).to.equal(0); expect(sock._rQi).to.equal(0);
expect(sock._rQ.length).to.equal(240); // keep the invariant that rQbufferSize / 8 >= rQlen expect(sock._rQ.length).to.equal(240); // keep the invariant that rQbufferSize / 8 >= rQlen
}); });
it('should automatically resize the receive queue if the incoming message is larger than 1/8th of the buffer and we reach the end of the buffer', function () { it('should automatically resize the receive queue if the incoming message is larger than 1/8th of the buffer and we reach the end of the buffer', function () {
sock._rQ = new Uint8Array(20); sock._rQ = new Uint8Array(20);
sock._rQlen = 16; sock._rQlen = 16;
sock.rQi = 16; sock._rQi = 16;
sock._rQbufferSize = 20; sock._rQbufferSize = 20;
const msg = { data: new Uint8Array(6).buffer }; const msg = { data: new Uint8Array(6).buffer };
sock._mode = 'binary'; sock._mode = 'binary';
sock._recvMessage(msg); sock._recvMessage(msg);
expect(sock._rQlen).to.equal(6); expect(sock._rQlen).to.equal(6);
expect(sock.rQi).to.equal(0); expect(sock._rQi).to.equal(0);
expect(sock._rQ.length).to.equal(48); expect(sock._rQ.length).to.equal(48);
}); });
}); });