Begin the hairy process of making this thing deal safely with string encodings. This will be broken for some time.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3356 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
parent
885fbb0ac1
commit
ba298b2e7c
@ -1,5 +1,7 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
require("stringview");
|
||||||
|
|
||||||
var gHandlers = [];
|
var gHandlers = [];
|
||||||
var gSocketHandlers = [];
|
var gSocketHandlers = [];
|
||||||
|
|
||||||
@ -185,7 +187,7 @@ function handleRequest(request, response) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleWebSocketRequest(request, response, client) {
|
function handleWebSocketRequest(request, response, client) {
|
||||||
var buffer = "";
|
var buffer = new Uint8Array(0);
|
||||||
var frame = "";
|
var frame = "";
|
||||||
var frameOpCode = 0x0;
|
var frameOpCode = 0x0;
|
||||||
|
|
||||||
@ -199,24 +201,30 @@ function handleWebSocketRequest(request, response, client) {
|
|||||||
if (opCode === undefined) {
|
if (opCode === undefined) {
|
||||||
opCode = 0x2;
|
opCode = 0x2;
|
||||||
}
|
}
|
||||||
|
if (opCode == 0x1 && (typeof message == "string" || message instanceof String)) {
|
||||||
|
message = new StringView(message, "UTF-8").rawData;
|
||||||
|
}
|
||||||
var fin = true;
|
var fin = true;
|
||||||
var packet = String.fromCharCode((fin ? (1 << 7) : 0) | (opCode & 0xf));
|
var packet = [(fin ? (1 << 7) : 0) | (opCode & 0xf)];
|
||||||
var mask = false;
|
var mask = false;
|
||||||
if (message.length < 126) {
|
if (message.length < 126) {
|
||||||
packet += String.fromCharCode((mask ? (1 << 7) : 0) | message.length);
|
packet.push((mask ? (1 << 7) : 0) | message.length);
|
||||||
} else if (message.length < (1 << 16)) {
|
} else if (message.length < (1 << 16)) {
|
||||||
packet += String.fromCharCode((mask ? (1 << 7) : 0) | 126);
|
packet.push((mask ? (1 << 7) : 0) | 126);
|
||||||
packet += String.fromCharCode((message.length >> 8) & 0xff);
|
packet.push((message.length >> 8) & 0xff);
|
||||||
packet += String.fromCharCode(message.length & 0xff);
|
packet.push(message.length & 0xff);
|
||||||
} else {
|
} else {
|
||||||
packet += String.fromCharCode((mask ? (1 << 7) : 0) | 127);
|
packet.push((mask ? (1 << 7) : 0) | 127);
|
||||||
packet += String.fromCharCode((message.length >> 24) & 0xff);
|
packet.push((message.length >> 24) & 0xff);
|
||||||
packet += String.fromCharCode((message.length >> 16) & 0xff);
|
packet.push((message.length >> 16) & 0xff);
|
||||||
packet += String.fromCharCode((message.length >> 8) & 0xff);
|
packet.push((message.length >> 8) & 0xff);
|
||||||
packet += String.fromCharCode(message.length & 0xff);
|
packet.push(message.length & 0xff);
|
||||||
}
|
}
|
||||||
packet += message;
|
|
||||||
return client.write(packet);
|
var array = new Uint8Array(packet.length + message.length);
|
||||||
|
array.set(packet, 0);
|
||||||
|
array.set(message, packet.length);
|
||||||
|
return client.write(array);
|
||||||
}
|
}
|
||||||
response.onMessage = null;
|
response.onMessage = null;
|
||||||
|
|
||||||
@ -224,10 +232,14 @@ function handleWebSocketRequest(request, response, client) {
|
|||||||
|
|
||||||
client.read(function(data) {
|
client.read(function(data) {
|
||||||
if (data) {
|
if (data) {
|
||||||
buffer += data;
|
var newBuffer = new Uint8Array(buffer.length + data.length);
|
||||||
|
newBuffer.set(buffer, 0);
|
||||||
|
newBuffer.set(data, buffer.length);
|
||||||
|
buffer = newBuffer;
|
||||||
|
|
||||||
if (buffer.length >= 2) {
|
if (buffer.length >= 2) {
|
||||||
var bits0 = buffer.charCodeAt(0);
|
var bits0 = buffer[0];
|
||||||
var bits1 = buffer.charCodeAt(1);
|
var bits1 = buffer[1];
|
||||||
if (bits1 & (1 << 7) == 0) {
|
if (bits1 & (1 << 7) == 0) {
|
||||||
// Unmasked message.
|
// Unmasked message.
|
||||||
client.close();
|
client.close();
|
||||||
@ -241,26 +253,26 @@ function handleWebSocketRequest(request, response, client) {
|
|||||||
payloadLength = 0;
|
payloadLength = 0;
|
||||||
for (var i = 0; i < 2; i++) {
|
for (var i = 0; i < 2; i++) {
|
||||||
payloadLength <<= 8;
|
payloadLength <<= 8;
|
||||||
payloadLength |= buffer.charCodeAt(2 + i);
|
payloadLength |= buffer[2 + i];
|
||||||
}
|
}
|
||||||
maskStart = 4;
|
maskStart = 4;
|
||||||
} else if (payloadLength == 127) {
|
} else if (payloadLength == 127) {
|
||||||
payloadLength = 0;
|
payloadLength = 0;
|
||||||
for (var i = 0; i < 8; i++) {
|
for (var i = 0; i < 8; i++) {
|
||||||
payloadLength <<= 8;
|
payloadLength <<= 8;
|
||||||
payloadLength |= buffer.charCodeAt(2 + i);
|
payloadLength |= buffer[2 + i];
|
||||||
}
|
}
|
||||||
maskStart = 10;
|
maskStart = 10;
|
||||||
}
|
}
|
||||||
var havePayload = buffer.length >= payloadLength + 2 + 4;
|
var havePayload = buffer.length >= payloadLength + 2 + 4;
|
||||||
if (havePayload) {
|
if (havePayload) {
|
||||||
var mask = buffer.substring(maskStart, maskStart + 4);
|
var mask = buffer.slice(maskStart, maskStart + 4);
|
||||||
var dataStart = maskStart + 4;
|
var dataStart = maskStart + 4;
|
||||||
var decoded = "";
|
var decoded = "";
|
||||||
var payload = buffer.substring(dataStart, dataStart + payloadLength);
|
var payload = buffer.slice(dataStart, dataStart + payloadLength);
|
||||||
buffer = buffer.substring(dataStart + payloadLength);
|
buffer = buffer.slice(dataStart + payloadLength);
|
||||||
for (var i = 0; i < payloadLength; i++) {
|
for (var i = 0; i < payloadLength; i++) {
|
||||||
decoded += String.fromCharCode(payload.charCodeAt(i) ^ mask.charCodeAt(i % 4));
|
decoded += String.fromCharCode(payload[i] ^ mask[i % 4]);
|
||||||
}
|
}
|
||||||
|
|
||||||
frame += decoded;
|
frame += decoded;
|
||||||
@ -319,7 +331,7 @@ function webSocketAcceptResponse(key) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleConnection(client) {
|
function handleConnection(client) {
|
||||||
var inputBuffer = "";
|
var inputBuffer = new Uint8Array(0);
|
||||||
var request;
|
var request;
|
||||||
var headers = {};
|
var headers = {};
|
||||||
var lineByLine = true;
|
var lineByLine = true;
|
||||||
@ -327,7 +339,7 @@ function handleConnection(client) {
|
|||||||
var body;
|
var body;
|
||||||
|
|
||||||
function reset() {
|
function reset() {
|
||||||
inputBuffer = "";
|
inputBuffer = new Uint8Array(0);
|
||||||
request = undefined;
|
request = undefined;
|
||||||
headers = {};
|
headers = {};
|
||||||
lineByLine = true;
|
lineByLine = true;
|
||||||
@ -350,6 +362,7 @@ function handleConnection(client) {
|
|||||||
|
|
||||||
function handleLine(line, length) {
|
function handleLine(line, length) {
|
||||||
if (bodyToRead == -1) {
|
if (bodyToRead == -1) {
|
||||||
|
line = new StringView(line, "ASCII").toString();
|
||||||
if (!request) {
|
if (!request) {
|
||||||
request = line.split(' ');
|
request = line.split(' ');
|
||||||
return true;
|
return true;
|
||||||
@ -379,6 +392,7 @@ function handleConnection(client) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
line = new StringView(line, "UTF-8").toString();
|
||||||
body += line;
|
body += line;
|
||||||
bodyToRead -= length;
|
bodyToRead -= length;
|
||||||
if (bodyToRead <= 0) {
|
if (bodyToRead <= 0) {
|
||||||
@ -393,14 +407,21 @@ function handleConnection(client) {
|
|||||||
|
|
||||||
client.read(function(data) {
|
client.read(function(data) {
|
||||||
if (data) {
|
if (data) {
|
||||||
inputBuffer += data;
|
var newBuffer = new Uint8Array(inputBuffer.length + data.length);
|
||||||
|
newBuffer.set(inputBuffer, 0);
|
||||||
|
newBuffer.set(data, inputBuffer.length);
|
||||||
|
inputBuffer = newBuffer;
|
||||||
|
|
||||||
|
var newLine = '\n'.charCodeAt(0);
|
||||||
|
var carriageReturn = '\r'.charCodeAt(0);
|
||||||
|
|
||||||
var more = true;
|
var more = true;
|
||||||
while (more) {
|
while (more) {
|
||||||
if (lineByLine) {
|
if (lineByLine) {
|
||||||
more = false;
|
more = false;
|
||||||
var end = inputBuffer.indexOf('\n');
|
var end = inputBuffer.indexOf(newLine);
|
||||||
var realEnd = end;
|
var realEnd = end;
|
||||||
if (end > 0 && inputBuffer[end - 1] == '\r') {
|
if (end > 0 && inputBuffer[end - 1] == carriageReturn) {
|
||||||
--end;
|
--end;
|
||||||
}
|
}
|
||||||
if (end != -1) {
|
if (end != -1) {
|
||||||
@ -410,7 +431,7 @@ function handleConnection(client) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
more = handleLine(inputBuffer, inputBuffer.length);
|
more = handleLine(inputBuffer, inputBuffer.length);
|
||||||
inputBuffer = "";
|
inputBuffer = new Uint8Array(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
679
core/stringview.js
Normal file
679
core/stringview.js
Normal file
@ -0,0 +1,679 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
/*\
|
||||||
|
|*|
|
||||||
|
|*| :: Number.isInteger() polyfill ::
|
||||||
|
|*|
|
||||||
|
|*| https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger
|
||||||
|
|*|
|
||||||
|
\*/
|
||||||
|
|
||||||
|
if (!Number.isInteger) {
|
||||||
|
Number.isInteger = function isInteger (nVal) {
|
||||||
|
return typeof nVal === "number" && isFinite(nVal) && nVal > -9007199254740992 && nVal < 9007199254740992 && Math.floor(nVal) === nVal;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*\
|
||||||
|
|*|
|
||||||
|
|*| StringView - Mozilla Developer Network
|
||||||
|
|*|
|
||||||
|
|*| Revision #9, October 30, 2016
|
||||||
|
|*|
|
||||||
|
|*| https://developer.mozilla.org/en-US/Add-ons/Code_snippets/StringView
|
||||||
|
|*| https://developer.mozilla.org/en-US/docs/User:fusionchess
|
||||||
|
|*| https://github.com/madmurphy/stringview.js
|
||||||
|
|*|
|
||||||
|
|*| This framework is released under the GNU Lesser General Public License, version 3 or later.
|
||||||
|
|*| http://www.gnu.org/licenses/lgpl-3.0.html
|
||||||
|
|*|
|
||||||
|
\*/
|
||||||
|
|
||||||
|
function StringView (vInput, sEncoding /* optional (default: UTF-8) */, nOffset /* optional */, nLength /* optional */) {
|
||||||
|
|
||||||
|
var fTAView, aWhole, aRaw, fPutOutptCode, fGetOutptChrSize, nInptLen, nStartIdx = isFinite(nOffset) ? nOffset : 0, nTranscrType = 15;
|
||||||
|
|
||||||
|
if (sEncoding) { this.encoding = sEncoding.toString(); }
|
||||||
|
|
||||||
|
encSwitch: switch (this.encoding) {
|
||||||
|
case "UTF-8":
|
||||||
|
fPutOutptCode = StringView.putUTF8CharCode;
|
||||||
|
fGetOutptChrSize = StringView.getUTF8CharLength;
|
||||||
|
fTAView = Uint8Array;
|
||||||
|
break encSwitch;
|
||||||
|
case "UTF-16":
|
||||||
|
fPutOutptCode = StringView.putUTF16CharCode;
|
||||||
|
fGetOutptChrSize = StringView.getUTF16CharLength;
|
||||||
|
fTAView = Uint16Array;
|
||||||
|
break encSwitch;
|
||||||
|
case "UTF-32":
|
||||||
|
fTAView = Uint32Array;
|
||||||
|
nTranscrType &= 14;
|
||||||
|
break encSwitch;
|
||||||
|
default:
|
||||||
|
/* case "ASCII", or case "BinaryString" or unknown cases */
|
||||||
|
fTAView = Uint8Array;
|
||||||
|
nTranscrType &= 14;
|
||||||
|
}
|
||||||
|
|
||||||
|
typeSwitch: switch (typeof vInput) {
|
||||||
|
case "string":
|
||||||
|
/* the input argument is a primitive string: a new buffer will be created. */
|
||||||
|
nTranscrType &= 7;
|
||||||
|
break typeSwitch;
|
||||||
|
case "object":
|
||||||
|
classSwitch: switch (vInput.constructor) {
|
||||||
|
case StringView:
|
||||||
|
/* the input argument is a stringView: a new buffer will be created. */
|
||||||
|
nTranscrType &= 3;
|
||||||
|
break typeSwitch;
|
||||||
|
case String:
|
||||||
|
/* the input argument is an objectified string: a new buffer will be created. */
|
||||||
|
nTranscrType &= 7;
|
||||||
|
break typeSwitch;
|
||||||
|
case ArrayBuffer:
|
||||||
|
/* the input argument is an arrayBuffer: the buffer will be shared. */
|
||||||
|
aWhole = new fTAView(vInput);
|
||||||
|
nInptLen = this.encoding === "UTF-32" ?
|
||||||
|
vInput.byteLength >>> 2
|
||||||
|
: this.encoding === "UTF-16" ?
|
||||||
|
vInput.byteLength >>> 1
|
||||||
|
:
|
||||||
|
vInput.byteLength;
|
||||||
|
aRaw = nStartIdx === 0 && (!isFinite(nLength) || nLength === nInptLen) ?
|
||||||
|
aWhole
|
||||||
|
: new fTAView(vInput, nStartIdx, !isFinite(nLength) ? nInptLen - nStartIdx : nLength);
|
||||||
|
|
||||||
|
break typeSwitch;
|
||||||
|
case Uint32Array:
|
||||||
|
case Uint16Array:
|
||||||
|
case Uint8Array:
|
||||||
|
/* the input argument is a typedArray: the buffer, and possibly the array itself, will be shared. */
|
||||||
|
fTAView = vInput.constructor;
|
||||||
|
nInptLen = vInput.length;
|
||||||
|
aWhole = vInput.byteOffset === 0 && vInput.length === (
|
||||||
|
fTAView === Uint32Array ?
|
||||||
|
vInput.buffer.byteLength >>> 2
|
||||||
|
: fTAView === Uint16Array ?
|
||||||
|
vInput.buffer.byteLength >>> 1
|
||||||
|
:
|
||||||
|
vInput.buffer.byteLength
|
||||||
|
) ? vInput : new fTAView(vInput.buffer);
|
||||||
|
aRaw = nStartIdx === 0 && (!isFinite(nLength) || nLength === nInptLen) ?
|
||||||
|
vInput
|
||||||
|
: vInput.subarray(nStartIdx, isFinite(nLength) ? nStartIdx + nLength : nInptLen);
|
||||||
|
|
||||||
|
break typeSwitch;
|
||||||
|
default:
|
||||||
|
/* the input argument is an array or another serializable object: a new typedArray will be created. */
|
||||||
|
aWhole = new fTAView(vInput);
|
||||||
|
nInptLen = aWhole.length;
|
||||||
|
aRaw = nStartIdx === 0 && (!isFinite(nLength) || nLength === nInptLen) ?
|
||||||
|
aWhole
|
||||||
|
: aWhole.subarray(nStartIdx, isFinite(nLength) ? nStartIdx + nLength : nInptLen);
|
||||||
|
}
|
||||||
|
break typeSwitch;
|
||||||
|
default:
|
||||||
|
/* the input argument is a number, a boolean or a function: a new typedArray will be created. */
|
||||||
|
aWhole = aRaw = new fTAView(Number(vInput) || 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nTranscrType < 8) {
|
||||||
|
|
||||||
|
var vSource, nOutptLen, nCharStart, nCharEnd, nEndIdx, fGetInptChrSize, fGetInptChrCode;
|
||||||
|
|
||||||
|
if (nTranscrType & 4) { /* input is string */
|
||||||
|
|
||||||
|
vSource = vInput;
|
||||||
|
nOutptLen = nInptLen = vSource.length;
|
||||||
|
nTranscrType ^= this.encoding === "UTF-32" ? 0 : 2;
|
||||||
|
/* ...or...: nTranscrType ^= Number(this.encoding !== "UTF-32") << 1; */
|
||||||
|
nStartIdx = nCharStart = nOffset ? Math.max((nOutptLen + nOffset) % nOutptLen, 0) : 0;
|
||||||
|
nEndIdx = nCharEnd = (Number.isInteger(nLength) ? Math.min(Math.max(nLength, 0) + nStartIdx, nOutptLen) : nOutptLen) - 1;
|
||||||
|
|
||||||
|
} else { /* input is stringView */
|
||||||
|
|
||||||
|
vSource = vInput.rawData;
|
||||||
|
nInptLen = vInput.makeIndex();
|
||||||
|
nStartIdx = nCharStart = nOffset ? Math.max((nInptLen + nOffset) % nInptLen, 0) : 0;
|
||||||
|
nOutptLen = Number.isInteger(nLength) ? Math.min(Math.max(nLength, 0), nInptLen - nCharStart) : nInptLen;
|
||||||
|
nEndIdx = nCharEnd = nOutptLen + nCharStart;
|
||||||
|
|
||||||
|
if (vInput.encoding === "UTF-8") {
|
||||||
|
fGetInptChrSize = StringView.getUTF8CharLength;
|
||||||
|
fGetInptChrCode = StringView.loadUTF8CharCode;
|
||||||
|
} else if (vInput.encoding === "UTF-16") {
|
||||||
|
fGetInptChrSize = StringView.getUTF16CharLength;
|
||||||
|
fGetInptChrCode = StringView.loadUTF16CharCode;
|
||||||
|
} else {
|
||||||
|
nTranscrType &= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nOutptLen === 0 || nTranscrType < 4 && vSource.encoding === this.encoding && nCharStart === 0 && nOutptLen === nInptLen) {
|
||||||
|
|
||||||
|
/* the encoding is the same, the length too and the offset is 0... or the input is empty! */
|
||||||
|
|
||||||
|
nTranscrType = 7;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
conversionSwitch: switch (nTranscrType) {
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
|
||||||
|
/* both the source and the new StringView have a fixed-length encoding... */
|
||||||
|
|
||||||
|
aWhole = new fTAView(nOutptLen);
|
||||||
|
for (var nOutptIdx = 0; nOutptIdx < nOutptLen; aWhole[nOutptIdx] = vSource[nStartIdx + nOutptIdx++]);
|
||||||
|
break conversionSwitch;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
|
||||||
|
/* the source has a fixed-length encoding but the new StringView has a variable-length encoding... */
|
||||||
|
|
||||||
|
/* mapping... */
|
||||||
|
|
||||||
|
nOutptLen = 0;
|
||||||
|
|
||||||
|
for (var nInptIdx = nStartIdx; nInptIdx < nEndIdx; nInptIdx++) {
|
||||||
|
nOutptLen += fGetOutptChrSize(vSource[nInptIdx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
aWhole = new fTAView(nOutptLen);
|
||||||
|
|
||||||
|
/* transcription of the source... */
|
||||||
|
|
||||||
|
for (var nInptIdx = nStartIdx, nOutptIdx = 0; nOutptIdx < nOutptLen; nInptIdx++) {
|
||||||
|
nOutptIdx = fPutOutptCode(aWhole, vSource[nInptIdx], nOutptIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
break conversionSwitch;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
|
||||||
|
/* the source has a variable-length encoding but the new StringView has a fixed-length encoding... */
|
||||||
|
|
||||||
|
/* mapping... */
|
||||||
|
|
||||||
|
nStartIdx = 0;
|
||||||
|
|
||||||
|
var nChrCode;
|
||||||
|
|
||||||
|
for (nChrIdx = 0; nChrIdx < nCharStart; nChrIdx++) {
|
||||||
|
nChrCode = fGetInptChrCode(vSource, nStartIdx);
|
||||||
|
nStartIdx += fGetInptChrSize(nChrCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
aWhole = new fTAView(nOutptLen);
|
||||||
|
|
||||||
|
/* transcription of the source... */
|
||||||
|
|
||||||
|
for (var nInptIdx = nStartIdx, nOutptIdx = 0; nOutptIdx < nOutptLen; nInptIdx += fGetInptChrSize(nChrCode), nOutptIdx++) {
|
||||||
|
nChrCode = fGetInptChrCode(vSource, nInptIdx);
|
||||||
|
aWhole[nOutptIdx] = nChrCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
break conversionSwitch;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
|
||||||
|
/* both the source and the new StringView have a variable-length encoding... */
|
||||||
|
|
||||||
|
/* mapping... */
|
||||||
|
|
||||||
|
nOutptLen = 0;
|
||||||
|
|
||||||
|
var nChrCode;
|
||||||
|
|
||||||
|
for (var nChrIdx = 0, nInptIdx = 0; nChrIdx < nCharEnd; nInptIdx += fGetInptChrSize(nChrCode)) {
|
||||||
|
nChrCode = fGetInptChrCode(vSource, nInptIdx);
|
||||||
|
if (nChrIdx === nCharStart) { nStartIdx = nInptIdx; }
|
||||||
|
if (++nChrIdx > nCharStart) { nOutptLen += fGetOutptChrSize(nChrCode); }
|
||||||
|
}
|
||||||
|
|
||||||
|
aWhole = new fTAView(nOutptLen);
|
||||||
|
|
||||||
|
/* transcription... */
|
||||||
|
|
||||||
|
for (var nInptIdx = nStartIdx, nOutptIdx = 0; nOutptIdx < nOutptLen; nInptIdx += fGetInptChrSize(nChrCode)) {
|
||||||
|
nChrCode = fGetInptChrCode(vSource, nInptIdx);
|
||||||
|
nOutptIdx = fPutOutptCode(aWhole, nChrCode, nOutptIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
break conversionSwitch;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
|
||||||
|
/* DOMString to ASCII or BinaryString or other unknown encodings */
|
||||||
|
|
||||||
|
aWhole = new fTAView(nOutptLen);
|
||||||
|
|
||||||
|
/* transcription... */
|
||||||
|
|
||||||
|
for (var nIdx = 0; nIdx < nOutptLen; nIdx++) {
|
||||||
|
aWhole[nIdx] = vSource.charCodeAt(nIdx) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
break conversionSwitch;
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
|
||||||
|
/* DOMString to UTF-8 or to UTF-16 */
|
||||||
|
|
||||||
|
/* mapping... */
|
||||||
|
|
||||||
|
nOutptLen = 0;
|
||||||
|
|
||||||
|
for (var nMapIdx = 0; nMapIdx < nInptLen; nMapIdx++) {
|
||||||
|
if (nMapIdx === nCharStart) { nStartIdx = nOutptLen; }
|
||||||
|
nOutptLen += fGetOutptChrSize(vSource.charCodeAt(nMapIdx));
|
||||||
|
if (nMapIdx === nCharEnd) { nEndIdx = nOutptLen; }
|
||||||
|
}
|
||||||
|
|
||||||
|
aWhole = new fTAView(nOutptLen);
|
||||||
|
|
||||||
|
/* transcription... */
|
||||||
|
|
||||||
|
for (var nOutptIdx = 0, nChrIdx = 0; nOutptIdx < nOutptLen; nChrIdx++) {
|
||||||
|
nOutptIdx = fPutOutptCode(aWhole, vSource.charCodeAt(nChrIdx), nOutptIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
break conversionSwitch;
|
||||||
|
|
||||||
|
case 6:
|
||||||
|
|
||||||
|
/* DOMString to UTF-32 */
|
||||||
|
|
||||||
|
aWhole = new fTAView(nOutptLen);
|
||||||
|
|
||||||
|
/* transcription... */
|
||||||
|
|
||||||
|
for (var nIdx = 0; nIdx < nOutptLen; nIdx++) {
|
||||||
|
aWhole[nIdx] = vSource.charCodeAt(nIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
break conversionSwitch;
|
||||||
|
|
||||||
|
case 7:
|
||||||
|
|
||||||
|
aWhole = new fTAView(nOutptLen ? vSource : 0);
|
||||||
|
break conversionSwitch;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
aRaw = nTranscrType > 3 && (nStartIdx > 0 || nEndIdx < aWhole.length - 1) ? aWhole.subarray(nStartIdx, nEndIdx) : aWhole;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
this.buffer = aWhole.buffer;
|
||||||
|
this.bufferView = aWhole;
|
||||||
|
this.rawData = aRaw;
|
||||||
|
|
||||||
|
Object.freeze(this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CONSTRUCTOR'S METHODS */
|
||||||
|
|
||||||
|
StringView.loadUTF8CharCode = function (aChars, nIdx) {
|
||||||
|
|
||||||
|
var nLen = aChars.length, nPart = aChars[nIdx];
|
||||||
|
|
||||||
|
return nPart > 251 && nPart < 254 && nIdx + 5 < nLen ?
|
||||||
|
/* (nPart - 252 << 30) may be not safe in ECMAScript! So...: */
|
||||||
|
/* six bytes */ (nPart - 252) * 1073741824 + (aChars[nIdx + 1] - 128 << 24) + (aChars[nIdx + 2] - 128 << 18) + (aChars[nIdx + 3] - 128 << 12) + (aChars[nIdx + 4] - 128 << 6) + aChars[nIdx + 5] - 128
|
||||||
|
: nPart > 247 && nPart < 252 && nIdx + 4 < nLen ?
|
||||||
|
/* five bytes */ (nPart - 248 << 24) + (aChars[nIdx + 1] - 128 << 18) + (aChars[nIdx + 2] - 128 << 12) + (aChars[nIdx + 3] - 128 << 6) + aChars[nIdx + 4] - 128
|
||||||
|
: nPart > 239 && nPart < 248 && nIdx + 3 < nLen ?
|
||||||
|
/* four bytes */(nPart - 240 << 18) + (aChars[nIdx + 1] - 128 << 12) + (aChars[nIdx + 2] - 128 << 6) + aChars[nIdx + 3] - 128
|
||||||
|
: nPart > 223 && nPart < 240 && nIdx + 2 < nLen ?
|
||||||
|
/* three bytes */ (nPart - 224 << 12) + (aChars[nIdx + 1] - 128 << 6) + aChars[nIdx + 2] - 128
|
||||||
|
: nPart > 191 && nPart < 224 && nIdx + 1 < nLen ?
|
||||||
|
/* two bytes */ (nPart - 192 << 6) + aChars[nIdx + 1] - 128
|
||||||
|
:
|
||||||
|
/* one byte */ nPart;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
StringView.putUTF8CharCode = function (aTarget, nChar, nPutAt) {
|
||||||
|
|
||||||
|
var nIdx = nPutAt;
|
||||||
|
|
||||||
|
if (nChar < 0x80 /* 128 */) {
|
||||||
|
/* one byte */
|
||||||
|
aTarget[nIdx++] = nChar;
|
||||||
|
} else if (nChar < 0x800 /* 2048 */) {
|
||||||
|
/* two bytes */
|
||||||
|
aTarget[nIdx++] = 0xc0 /* 192 */ + (nChar >>> 6);
|
||||||
|
aTarget[nIdx++] = 0x80 /* 128 */ + (nChar & 0x3f /* 63 */);
|
||||||
|
} else if (nChar < 0x10000 /* 65536 */) {
|
||||||
|
/* three bytes */
|
||||||
|
aTarget[nIdx++] = 0xe0 /* 224 */ + (nChar >>> 12);
|
||||||
|
aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 6) & 0x3f /* 63 */);
|
||||||
|
aTarget[nIdx++] = 0x80 /* 128 */ + (nChar & 0x3f /* 63 */);
|
||||||
|
} else if (nChar < 0x200000 /* 2097152 */) {
|
||||||
|
/* four bytes */
|
||||||
|
aTarget[nIdx++] = 0xf0 /* 240 */ + (nChar >>> 18);
|
||||||
|
aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 12) & 0x3f /* 63 */);
|
||||||
|
aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 6) & 0x3f /* 63 */);
|
||||||
|
aTarget[nIdx++] = 0x80 /* 128 */ + (nChar & 0x3f /* 63 */);
|
||||||
|
} else if (nChar < 0x4000000 /* 67108864 */) {
|
||||||
|
/* five bytes */
|
||||||
|
aTarget[nIdx++] = 0xf8 /* 248 */ + (nChar >>> 24);
|
||||||
|
aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 18) & 0x3f /* 63 */);
|
||||||
|
aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 12) & 0x3f /* 63 */);
|
||||||
|
aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 6) & 0x3f /* 63 */);
|
||||||
|
aTarget[nIdx++] = 0x80 /* 128 */ + (nChar & 0x3f /* 63 */);
|
||||||
|
} else /* if (nChar <= 0x7fffffff) */ { /* 2147483647 */
|
||||||
|
/* six bytes */
|
||||||
|
aTarget[nIdx++] = 0xfc /* 252 */ + /* (nChar >>> 30) may be not safe in ECMAScript! So...: */ (nChar / 1073741824);
|
||||||
|
aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 24) & 0x3f /* 63 */);
|
||||||
|
aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 18) & 0x3f /* 63 */);
|
||||||
|
aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 12) & 0x3f /* 63 */);
|
||||||
|
aTarget[nIdx++] = 0x80 /* 128 */ + ((nChar >>> 6) & 0x3f /* 63 */);
|
||||||
|
aTarget[nIdx++] = 0x80 /* 128 */ + (nChar & 0x3f /* 63 */);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nIdx;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
StringView.getUTF8CharLength = function (nChar) {
|
||||||
|
return nChar < 0x80 ? 1 : nChar < 0x800 ? 2 : nChar < 0x10000 ? 3 : nChar < 0x200000 ? 4 : nChar < 0x4000000 ? 5 : 6;
|
||||||
|
};
|
||||||
|
|
||||||
|
StringView.loadUTF16CharCode = function (aChars, nIdx) {
|
||||||
|
|
||||||
|
/* UTF-16 to DOMString decoding algorithm */
|
||||||
|
var nFrstChr = aChars[nIdx];
|
||||||
|
|
||||||
|
return nFrstChr > 0xD7BF /* 55231 */ && nIdx + 1 < aChars.length ?
|
||||||
|
(nFrstChr - 0xD800 /* 55296 */ << 10) + aChars[nIdx + 1] + 0x2400 /* 9216 */
|
||||||
|
: nFrstChr;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
StringView.putUTF16CharCode = function (aTarget, nChar, nPutAt) {
|
||||||
|
|
||||||
|
var nIdx = nPutAt;
|
||||||
|
|
||||||
|
if (nChar < 0x10000 /* 65536 */) {
|
||||||
|
/* one element */
|
||||||
|
aTarget[nIdx++] = nChar;
|
||||||
|
} else {
|
||||||
|
/* two elements */
|
||||||
|
aTarget[nIdx++] = 0xD7C0 /* 55232 */ + (nChar >>> 10);
|
||||||
|
aTarget[nIdx++] = 0xDC00 /* 56320 */ + (nChar & 0x3FF /* 1023 */);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nIdx;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
StringView.getUTF16CharLength = function (nChar) {
|
||||||
|
return nChar < 0x10000 ? 1 : 2;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Array of bytes to base64 string decoding */
|
||||||
|
|
||||||
|
StringView.b64ToUint6 = function (nChr) {
|
||||||
|
|
||||||
|
return nChr > 64 && nChr < 91 ?
|
||||||
|
nChr - 65
|
||||||
|
: nChr > 96 && nChr < 123 ?
|
||||||
|
nChr - 71
|
||||||
|
: nChr > 47 && nChr < 58 ?
|
||||||
|
nChr + 4
|
||||||
|
: nChr === 43 ?
|
||||||
|
62
|
||||||
|
: nChr === 47 ?
|
||||||
|
63
|
||||||
|
:
|
||||||
|
0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
StringView.uint6ToB64 = function (nUint6) {
|
||||||
|
|
||||||
|
return nUint6 < 26 ?
|
||||||
|
nUint6 + 65
|
||||||
|
: nUint6 < 52 ?
|
||||||
|
nUint6 + 71
|
||||||
|
: nUint6 < 62 ?
|
||||||
|
nUint6 - 4
|
||||||
|
: nUint6 === 62 ?
|
||||||
|
43
|
||||||
|
: nUint6 === 63 ?
|
||||||
|
47
|
||||||
|
:
|
||||||
|
65;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Base64 string to array encoding */
|
||||||
|
|
||||||
|
StringView.bytesToBase64 = function (aBytes) {
|
||||||
|
|
||||||
|
var eqLen = (3 - (aBytes.length % 3)) % 3, sB64Enc = "";
|
||||||
|
|
||||||
|
for (var nMod3, nLen = aBytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx++) {
|
||||||
|
nMod3 = nIdx % 3;
|
||||||
|
/* Uncomment the following line in order to split the output in lines 76-character long: */
|
||||||
|
/*
|
||||||
|
if (nIdx > 0 && (nIdx * 4 / 3) % 76 === 0) { sB64Enc += "\r\n"; }
|
||||||
|
*/
|
||||||
|
nUint24 |= aBytes[nIdx] << (16 >>> nMod3 & 24);
|
||||||
|
if (nMod3 === 2 || aBytes.length - nIdx === 1) {
|
||||||
|
sB64Enc += String.fromCharCode(StringView.uint6ToB64(nUint24 >>> 18 & 63), StringView.uint6ToB64(nUint24 >>> 12 & 63), StringView.uint6ToB64(nUint24 >>> 6 & 63), StringView.uint6ToB64(nUint24 & 63));
|
||||||
|
nUint24 = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return eqLen === 0 ?
|
||||||
|
sB64Enc
|
||||||
|
:
|
||||||
|
sB64Enc.substring(0, sB64Enc.length - eqLen) + (eqLen === 1 ? "=" : "==");
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
StringView.base64ToBytes = function (sBase64, nBlockBytes) {
|
||||||
|
|
||||||
|
var
|
||||||
|
sB64Enc = sBase64.replace(/[^A-Za-z0-9\+\/]/g, ""), nInLen = sB64Enc.length,
|
||||||
|
nOutLen = nBlockBytes ? Math.ceil((nInLen * 3 + 1 >>> 2) / nBlockBytes) * nBlockBytes : nInLen * 3 + 1 >>> 2, aBytes = new Uint8Array(nOutLen);
|
||||||
|
|
||||||
|
for (var nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx++) {
|
||||||
|
nMod4 = nInIdx & 3;
|
||||||
|
nUint24 |= StringView.b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << 18 - 6 * nMod4;
|
||||||
|
if (nMod4 === 3 || nInLen - nInIdx === 1) {
|
||||||
|
for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) {
|
||||||
|
aBytes[nOutIdx] = nUint24 >>> (16 >>> nMod3 & 24) & 255;
|
||||||
|
}
|
||||||
|
nUint24 = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return aBytes;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
StringView.makeFromBase64 = function (sB64Inpt, sEncoding, nByteOffset, nLength) {
|
||||||
|
|
||||||
|
return new StringView(sEncoding === "UTF-16" || sEncoding === "UTF-32" ? StringView.base64ToBytes(sB64Inpt, sEncoding === "UTF-16" ? 2 : 4).buffer : StringView.base64ToBytes(sB64Inpt), sEncoding, nByteOffset, nLength);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/* DEFAULT VALUES */
|
||||||
|
|
||||||
|
StringView.prototype.encoding = "UTF-8"; /* Default encoding... */
|
||||||
|
|
||||||
|
/* INSTANCES' METHODS */
|
||||||
|
|
||||||
|
StringView.prototype.makeIndex = function (nChrLength, nStartFrom) {
|
||||||
|
|
||||||
|
var
|
||||||
|
|
||||||
|
aTarget = this.rawData, nChrEnd, nRawLength = aTarget.length,
|
||||||
|
nStartIdx = nStartFrom || 0, nIdxEnd = nStartIdx, nStopAtChr = isNaN(nChrLength) ? Infinity : nChrLength;
|
||||||
|
|
||||||
|
if (nChrLength + 1 > aTarget.length) { throw new RangeError("StringView.prototype.makeIndex - The offset can\'t be major than the length of the array - 1."); }
|
||||||
|
|
||||||
|
switch (this.encoding) {
|
||||||
|
|
||||||
|
case "UTF-8":
|
||||||
|
|
||||||
|
var nPart;
|
||||||
|
|
||||||
|
for (nChrEnd = 0; nIdxEnd < nRawLength && nChrEnd < nStopAtChr; nChrEnd++) {
|
||||||
|
nPart = aTarget[nIdxEnd];
|
||||||
|
nIdxEnd += nPart > 251 && nPart < 254 && nIdxEnd + 5 < nRawLength ? 6
|
||||||
|
: nPart > 247 && nPart < 252 && nIdxEnd + 4 < nRawLength ? 5
|
||||||
|
: nPart > 239 && nPart < 248 && nIdxEnd + 3 < nRawLength ? 4
|
||||||
|
: nPart > 223 && nPart < 240 && nIdxEnd + 2 < nRawLength ? 3
|
||||||
|
: nPart > 191 && nPart < 224 && nIdxEnd + 1 < nRawLength ? 2
|
||||||
|
: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "UTF-16":
|
||||||
|
|
||||||
|
for (nChrEnd = nStartIdx; nIdxEnd < nRawLength && nChrEnd < nStopAtChr; nChrEnd++) {
|
||||||
|
nIdxEnd += aTarget[nIdxEnd] > 0xD7BF /* 55231 */ && nIdxEnd + 1 < aTarget.length ? 2 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
nIdxEnd = nChrEnd = isFinite(nChrLength) ? nChrLength : nRawLength - 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nChrLength) { return nIdxEnd; }
|
||||||
|
|
||||||
|
return nChrEnd;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
StringView.prototype.toBase64 = function (bWholeBuffer) {
|
||||||
|
|
||||||
|
return StringView.bytesToBase64(
|
||||||
|
bWholeBuffer ?
|
||||||
|
(
|
||||||
|
this.bufferView.constructor === Uint8Array ?
|
||||||
|
this.bufferView
|
||||||
|
:
|
||||||
|
new Uint8Array(this.buffer)
|
||||||
|
)
|
||||||
|
: this.rawData.constructor === Uint8Array ?
|
||||||
|
this.rawData
|
||||||
|
:
|
||||||
|
new Uint8Array(this.buffer, this.rawData.byteOffset, this.rawData.length << (this.rawData.constructor === Uint16Array ? 1 : 2))
|
||||||
|
);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
StringView.prototype.subview = function (nCharOffset /* optional */, nCharLength /* optional */) {
|
||||||
|
|
||||||
|
var
|
||||||
|
|
||||||
|
nChrLen, nCharStart, nStrLen, bVariableLen = this.encoding === "UTF-8" || this.encoding === "UTF-16",
|
||||||
|
nStartOffset = nCharOffset, nStringLength, nRawLen = this.rawData.length;
|
||||||
|
|
||||||
|
if (nRawLen === 0) {
|
||||||
|
return new StringView(this.buffer, this.encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
nStringLength = bVariableLen ? this.makeIndex() : nRawLen;
|
||||||
|
nCharStart = nCharOffset ? Math.max((nStringLength + nCharOffset) % nStringLength, 0) : 0;
|
||||||
|
nStrLen = Number.isInteger(nCharLength) ? Math.max(nCharLength, 0) + nCharStart > nStringLength ? nStringLength - nCharStart : nCharLength : nStringLength;
|
||||||
|
|
||||||
|
if (nCharStart === 0 && nStrLen === nStringLength) { return this; }
|
||||||
|
|
||||||
|
if (bVariableLen) {
|
||||||
|
nStartOffset = this.makeIndex(nCharStart);
|
||||||
|
nChrLen = this.makeIndex(nStrLen, nStartOffset) - nStartOffset;
|
||||||
|
} else {
|
||||||
|
nStartOffset = nCharStart;
|
||||||
|
nChrLen = nStrLen - nCharStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.encoding === "UTF-16") {
|
||||||
|
nStartOffset <<= 1;
|
||||||
|
} else if (this.encoding === "UTF-32") {
|
||||||
|
nStartOffset <<= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StringView(this.buffer, this.encoding, nStartOffset, nChrLen);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
StringView.prototype.forEachChar = function (fCallback, oThat, nChrOffset, nChrLen) {
|
||||||
|
|
||||||
|
var aSource = this.rawData, nRawEnd, nRawIdx;
|
||||||
|
|
||||||
|
if (this.encoding === "UTF-8" || this.encoding === "UTF-16") {
|
||||||
|
|
||||||
|
var fGetInptChrSize, fGetInptChrCode;
|
||||||
|
|
||||||
|
if (this.encoding === "UTF-8") {
|
||||||
|
fGetInptChrSize = StringView.getUTF8CharLength;
|
||||||
|
fGetInptChrCode = StringView.loadUTF8CharCode;
|
||||||
|
} else if (this.encoding === "UTF-16") {
|
||||||
|
fGetInptChrSize = StringView.getUTF16CharLength;
|
||||||
|
fGetInptChrCode = StringView.loadUTF16CharCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
nRawIdx = isFinite(nChrOffset) ? this.makeIndex(nChrOffset) : 0;
|
||||||
|
nRawEnd = isFinite(nChrLen) ? this.makeIndex(nChrLen, nRawIdx) : aSource.length;
|
||||||
|
|
||||||
|
for (var nChrCode, nChrIdx = 0; nRawIdx < nRawEnd; nChrIdx++) {
|
||||||
|
nChrCode = fGetInptChrCode(aSource, nRawIdx);
|
||||||
|
fCallback.call(oThat || null, nChrCode, nChrIdx, nRawIdx, aSource);
|
||||||
|
nRawIdx += fGetInptChrSize(nChrCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
nRawIdx = isFinite(nChrOffset) ? nChrOffset : 0;
|
||||||
|
nRawEnd = isFinite(nChrLen) ? nChrLen + nRawIdx : aSource.length;
|
||||||
|
|
||||||
|
for (nRawIdx; nRawIdx < nRawEnd; nRawIdx++) {
|
||||||
|
fCallback.call(oThat || null, aSource[nRawIdx], nRawIdx, nRawIdx, aSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
StringView.prototype.valueOf = StringView.prototype.toString = function () {
|
||||||
|
|
||||||
|
if (this.encoding !== "UTF-8" && this.encoding !== "UTF-16") {
|
||||||
|
/* ASCII, UTF-32 or BinaryString to DOMString */
|
||||||
|
return String.fromCharCode.apply(null, this.rawData);
|
||||||
|
}
|
||||||
|
|
||||||
|
var fGetCode, fGetIncr, sView = "";
|
||||||
|
|
||||||
|
if (this.encoding === "UTF-8") {
|
||||||
|
fGetIncr = StringView.getUTF8CharLength;
|
||||||
|
fGetCode = StringView.loadUTF8CharCode;
|
||||||
|
} else if (this.encoding === "UTF-16") {
|
||||||
|
fGetIncr = StringView.getUTF16CharLength;
|
||||||
|
fGetCode = StringView.loadUTF16CharCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var nChr, nLen = this.rawData.length, nIdx = 0; nIdx < nLen; nIdx += fGetIncr(nChr)) {
|
||||||
|
nChr = fGetCode(this.rawData, nIdx);
|
||||||
|
sView += String.fromCharCode(nChr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sView;
|
||||||
|
|
||||||
|
};
|
@ -80,6 +80,13 @@ bool Serialize::storeInternal(Task* task, std::vector<char>& buffer, v8::Handle<
|
|||||||
v8::String::Utf8Value utf8(value->ToString());
|
v8::String::Utf8Value utf8(value->ToString());
|
||||||
writeInt32(buffer, utf8.length());
|
writeInt32(buffer, utf8.length());
|
||||||
buffer.insert(buffer.end(), *utf8, *utf8 + utf8.length());
|
buffer.insert(buffer.end(), *utf8, *utf8 + utf8.length());
|
||||||
|
} else if (value->IsUint8Array()) {
|
||||||
|
writeInt32(buffer, kUint8Array);
|
||||||
|
v8::Handle<v8::ArrayBuffer> array = v8::Handle<v8::ArrayBuffer>::Cast(value);
|
||||||
|
char* data = reinterpret_cast<char*>(array->GetContents().Data());
|
||||||
|
size_t length = array->GetContents().ByteLength();
|
||||||
|
writeInt32(buffer, length);
|
||||||
|
buffer.insert(buffer.end(), data, data + length);
|
||||||
} else if (value->IsArray()) {
|
} else if (value->IsArray()) {
|
||||||
writeInt32(buffer, kArray);
|
writeInt32(buffer, kArray);
|
||||||
v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
|
v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
|
||||||
@ -175,6 +182,15 @@ v8::Handle<v8::Value> Serialize::loadInternal(Task* task, TaskStub* from, const
|
|||||||
offset += length;
|
offset += length;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case kUint8Array:
|
||||||
|
{
|
||||||
|
int32_t length = readInt32(buffer, offset);
|
||||||
|
v8::Handle<v8::ArrayBuffer> array = v8::ArrayBuffer::New(task->getIsolate(), length);
|
||||||
|
std::memcpy(array->GetContents().Data(), &*buffer.begin() + offset, length);
|
||||||
|
offset += length;
|
||||||
|
result = v8::Uint8Array::New(array, 0, length);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case kArray:
|
case kArray:
|
||||||
{
|
{
|
||||||
int32_t length = readInt32(buffer, offset);
|
int32_t length = readInt32(buffer, offset);
|
||||||
|
@ -38,6 +38,7 @@ private:
|
|||||||
kNumber,
|
kNumber,
|
||||||
kString,
|
kString,
|
||||||
kArray,
|
kArray,
|
||||||
|
kUint8Array,
|
||||||
kObject,
|
kObject,
|
||||||
kFunction,
|
kFunction,
|
||||||
kError,
|
kError,
|
||||||
|
@ -442,11 +442,7 @@ void Socket::onRead(uv_stream_t* stream, ssize_t readSize, const uv_buf_t* buffe
|
|||||||
char plain[8192];
|
char plain[8192];
|
||||||
int result = socket->_tls->readPlain(plain, sizeof(plain));
|
int result = socket->_tls->readPlain(plain, sizeof(plain));
|
||||||
if (result > 0) {
|
if (result > 0) {
|
||||||
v8::Local<v8::Function> callback = v8::Local<v8::Function>::New(socket->_task->getIsolate(), socket->_onRead);
|
socket->notifyDataRead(plain, result);
|
||||||
if (!callback.IsEmpty()) {
|
|
||||||
data = v8::String::NewFromOneByte(socket->_task->getIsolate(), reinterpret_cast<const uint8_t*>(plain), v8::String::kNormalString, result);
|
|
||||||
callback->Call(callback, 1, &data);
|
|
||||||
}
|
|
||||||
} else if (result == TlsSession::kReadFailed) {
|
} else if (result == TlsSession::kReadFailed) {
|
||||||
socket->reportTlsErrors();
|
socket->reportTlsErrors();
|
||||||
socket->close();
|
socket->close();
|
||||||
@ -466,36 +462,52 @@ void Socket::onRead(uv_stream_t* stream, ssize_t readSize, const uv_buf_t* buffe
|
|||||||
socket->processOutgoingTls();
|
socket->processOutgoingTls();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
v8::Local<v8::Function> callback = v8::Local<v8::Function>::New(socket->_task->getIsolate(), socket->_onRead);
|
socket->notifyDataRead(buffer->base, readSize);
|
||||||
if (!callback.IsEmpty()) {
|
|
||||||
data = v8::String::NewFromOneByte(socket->_task->getIsolate(), reinterpret_cast<const uint8_t*>(buffer->base), v8::String::kNormalString, readSize);
|
|
||||||
callback->Call(callback, 1, &data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete[] buffer->base;
|
delete[] buffer->base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Socket::notifyDataRead(const char* data, size_t length) {
|
||||||
|
v8::Local<v8::Function> callback = v8::Local<v8::Function>::New(_task->getIsolate(), _onRead);
|
||||||
|
if (!callback.IsEmpty()) {
|
||||||
|
if (data && length > 0) {
|
||||||
|
v8::Handle<v8::ArrayBuffer> buffer = v8::ArrayBuffer::New(_task->getIsolate(), length);
|
||||||
|
std::memcpy(buffer->GetContents().Data(), data, length);
|
||||||
|
v8::Handle<v8::Uint8Array> array = v8::Uint8Array::New(buffer, 0, length);
|
||||||
|
|
||||||
|
v8::Handle<v8::Value> arguments = array;
|
||||||
|
callback->Call(callback, 1, &arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Socket::write(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
void Socket::write(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||||
if (Socket* socket = Socket::get(args.Data())) {
|
if (Socket* socket = Socket::get(args.Data())) {
|
||||||
promiseid_t promise = socket->_task->allocatePromise();
|
promiseid_t promise = socket->_task->allocatePromise();
|
||||||
args.GetReturnValue().Set(socket->_task->getPromise(promise));
|
args.GetReturnValue().Set(socket->_task->getPromise(promise));
|
||||||
v8::Handle<v8::String> value = args[0].As<v8::String>();
|
v8::Handle<v8::Value> value = args[0];
|
||||||
if (!value.IsEmpty() && value->IsString()) {
|
if (!value.IsEmpty() && (value->IsString() || value->IsUint8Array())) {
|
||||||
if (socket->_tls) {
|
if (socket->_tls) {
|
||||||
socket->reportTlsErrors();
|
socket->reportTlsErrors();
|
||||||
int result;
|
int result;
|
||||||
int length;
|
int length;
|
||||||
if (value->ContainsOnlyOneByte()) {
|
if (value->IsArrayBufferView()) {
|
||||||
length = value->Length();
|
v8::Handle<v8::ArrayBufferView> array = v8::Handle<v8::ArrayBufferView>::Cast(value);
|
||||||
std::vector<uint8_t> bytes(length);
|
result = socket->_tls->writePlain(reinterpret_cast<const char*>(array->Buffer()->GetContents().Data()), array->Buffer()->GetContents().ByteLength());
|
||||||
value->WriteOneByte(bytes.data(), 0, bytes.size(), v8::String::NO_NULL_TERMINATION);
|
} else if (value->IsString()) {
|
||||||
result = socket->_tls->writePlain(reinterpret_cast<const char*>(bytes.data()), bytes.size());
|
v8::Handle<v8::String> stringValue = v8::Handle<v8::String>::Cast(value);
|
||||||
} else {
|
if (stringValue->ContainsOnlyOneByte()) {
|
||||||
v8::String::Utf8Value utf8(value);
|
length = stringValue->Length();
|
||||||
length = utf8.length();
|
std::vector<uint8_t> bytes(length);
|
||||||
result = socket->_tls->writePlain(*utf8, utf8.length());
|
stringValue->WriteOneByte(bytes.data(), 0, bytes.size(), v8::String::NO_NULL_TERMINATION);
|
||||||
|
result = socket->_tls->writePlain(reinterpret_cast<const char*>(bytes.data()), bytes.size());
|
||||||
|
} else {
|
||||||
|
v8::String::Utf8Value utf8(stringValue);
|
||||||
|
length = utf8.length();
|
||||||
|
result = socket->_tls->writePlain(*utf8, utf8.length());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
char buffer[8192];
|
char buffer[8192];
|
||||||
if (result <= 0 && socket->_tls->getError(buffer, sizeof(buffer))) {
|
if (result <= 0 && socket->_tls->getError(buffer, sizeof(buffer))) {
|
||||||
@ -507,18 +519,26 @@ void Socket::write(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|||||||
}
|
}
|
||||||
socket->processOutgoingTls();
|
socket->processOutgoingTls();
|
||||||
} else {
|
} else {
|
||||||
v8::String::Utf8Value utf8(value);
|
|
||||||
int length;
|
int length;
|
||||||
char* rawBuffer = 0;
|
char* rawBuffer = 0;
|
||||||
if (value->ContainsOnlyOneByte()) {
|
v8::Handle<v8::String> stringValue = v8::Handle<v8::String>::Cast(value);
|
||||||
length = value->Length();
|
if (stringValue->IsArrayBufferView()) {
|
||||||
|
v8::Handle<v8::ArrayBufferView> array = v8::Handle<v8::ArrayBufferView>::Cast(value);
|
||||||
|
length = array->Buffer()->GetContents().ByteLength();
|
||||||
rawBuffer = new char[sizeof(uv_write_t) + length];
|
rawBuffer = new char[sizeof(uv_write_t) + length];
|
||||||
value->WriteOneByte(reinterpret_cast<uint8_t*>(rawBuffer) + sizeof(uv_write_t), 0, length, v8::String::NO_NULL_TERMINATION);
|
std::memcpy(rawBuffer + sizeof(uv_write_t), array->Buffer()->GetContents().Data(), length);
|
||||||
} else {
|
} else if (value->IsString()) {
|
||||||
v8::String::Utf8Value utf8(value);
|
v8::String::Utf8Value utf8(stringValue);
|
||||||
length = utf8.length();
|
if (stringValue->ContainsOnlyOneByte()) {
|
||||||
rawBuffer = new char[sizeof(uv_write_t) + length];
|
length = stringValue->Length();
|
||||||
std::memcpy(rawBuffer + sizeof(uv_write_t), *utf8, length);
|
rawBuffer = new char[sizeof(uv_write_t) + length];
|
||||||
|
stringValue->WriteOneByte(reinterpret_cast<uint8_t*>(rawBuffer) + sizeof(uv_write_t), 0, length, v8::String::NO_NULL_TERMINATION);
|
||||||
|
} else {
|
||||||
|
v8::String::Utf8Value utf8(stringValue);
|
||||||
|
length = utf8.length();
|
||||||
|
rawBuffer = new char[sizeof(uv_write_t) + length];
|
||||||
|
std::memcpy(rawBuffer + sizeof(uv_write_t), *utf8, length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
uv_write_t* request = reinterpret_cast<uv_write_t*>(rawBuffer);
|
uv_write_t* request = reinterpret_cast<uv_write_t*>(rawBuffer);
|
||||||
|
|
||||||
|
@ -74,6 +74,7 @@ private:
|
|||||||
static void onWrite(uv_write_t* request, int status);
|
static void onWrite(uv_write_t* request, int status);
|
||||||
static void onRelease(const v8::WeakCallbackInfo<Socket>& data);
|
static void onRelease(const v8::WeakCallbackInfo<Socket>& data);
|
||||||
|
|
||||||
|
void notifyDataRead(const char* data, size_t length);
|
||||||
void processTlsShutdown(promiseid_t promise);
|
void processTlsShutdown(promiseid_t promise);
|
||||||
static void onTlsShutdown(uv_write_t* request, int status);
|
static void onTlsShutdown(uv_write_t* request, int status);
|
||||||
void shutdownInternal(promiseid_t promise);
|
void shutdownInternal(promiseid_t promise);
|
||||||
|
38
tests/14-uint8array
Normal file
38
tests/14-uint8array
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
cat > test.js << EOF
|
||||||
|
var task = new Task();
|
||||||
|
task.onExit = function() {
|
||||||
|
print("child exited");
|
||||||
|
};
|
||||||
|
task.activate();
|
||||||
|
task.execute({name: "child.js", source: File.readFile("child.js")}).then(async function() {
|
||||||
|
print("child started");
|
||||||
|
var input = new ArrayBuffer(10);
|
||||||
|
for (var i = 0; i < 10; i++) {
|
||||||
|
input[i] = i;
|
||||||
|
}
|
||||||
|
var test = (await task.getExports()).test;
|
||||||
|
var output = await test(input);
|
||||||
|
print("input", input);
|
||||||
|
print("output", output);
|
||||||
|
for (var i = 0; i < 10; i++) {
|
||||||
|
print(output[i]);
|
||||||
|
if (output[i] != i) {
|
||||||
|
print("output[" + i + "] == " + output[i]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
});
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat > child.js << EOF
|
||||||
|
exports = {
|
||||||
|
test: function(data) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
$TILDEFRIENDS test.js
|
@ -42,15 +42,16 @@ def indent(text):
|
|||||||
passCount = 0
|
passCount = 0
|
||||||
failCount = 0
|
failCount = 0
|
||||||
|
|
||||||
for test in selectedTests:
|
for test in sorted(selectedTests):
|
||||||
if os.path.isdir(tmp):
|
if os.path.isdir(tmp):
|
||||||
shutil.rmtree(tmp)
|
shutil.rmtree(tmp)
|
||||||
if not os.path.isdir(tmp):
|
if not os.path.isdir(tmp):
|
||||||
os.makedirs(tmp)
|
os.makedirs(tmp)
|
||||||
|
|
||||||
process = subprocess.Popen(['bash', test], stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=tmp, env=env)
|
process = subprocess.Popen(['bash', test], stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=tmp, env=env)
|
||||||
|
interrupted = False
|
||||||
stdout, stderr = process.communicate()
|
stdout, stderr = process.communicate()
|
||||||
if process.returncode == 0:
|
if interrupted or process.returncode == 0:
|
||||||
if stdout.strip() == "SKIP":
|
if stdout.strip() == "SKIP":
|
||||||
print 'SKIPPED', test
|
print 'SKIPPED', test
|
||||||
else:
|
else:
|
||||||
|
Loading…
Reference in New Issue
Block a user