285 lines
9.9 KiB
JavaScript
Executable File
285 lines
9.9 KiB
JavaScript
Executable File
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
// Copyright (c) 2015-2024 MariaDB Corporation Ab
|
|
|
|
'use strict';
|
|
|
|
class BinaryEncoder {
|
|
/**
|
|
* Write (and escape) current parameter value to output writer
|
|
*
|
|
* @param out output writer
|
|
* @param value current parameter
|
|
* @param opts connection options
|
|
* @param info connection information
|
|
*/
|
|
static writeParam(out, value, opts, info) {
|
|
// GEOJSON are not checked, because change to null/Buffer on parameter validation
|
|
switch (typeof value) {
|
|
case 'boolean':
|
|
out.writeInt8(value ? 0x01 : 0x00);
|
|
break;
|
|
case 'bigint':
|
|
if (value >= 2n ** 63n) {
|
|
out.writeLengthEncodedString(value.toString());
|
|
} else {
|
|
out.writeBigInt(value);
|
|
}
|
|
break;
|
|
|
|
case 'number':
|
|
// additional verification, to permit query without type,
|
|
// like 'SELECT ?' returning same type of value
|
|
if (Number.isInteger(value) && value >= -2147483648 && value < 2147483647) {
|
|
out.writeInt32(value);
|
|
break;
|
|
}
|
|
out.writeDouble(value);
|
|
break;
|
|
case 'string':
|
|
out.writeLengthEncodedString(value);
|
|
break;
|
|
case 'object':
|
|
if (Object.prototype.toString.call(value) === '[object Date]') {
|
|
out.writeBinaryDate(value);
|
|
} else if (Buffer.isBuffer(value)) {
|
|
out.writeLengthEncodedBuffer(value);
|
|
} else if (typeof value.toSqlString === 'function') {
|
|
out.writeLengthEncodedString(String(value.toSqlString()));
|
|
} else {
|
|
out.writeLengthEncodedString(JSON.stringify(value));
|
|
}
|
|
break;
|
|
default:
|
|
out.writeLengthEncodedBuffer(value);
|
|
}
|
|
}
|
|
|
|
static getBufferFromGeometryValue(value, headerType) {
|
|
let geoBuff;
|
|
let pos;
|
|
let type;
|
|
if (!headerType) {
|
|
switch (value.type) {
|
|
case 'Point':
|
|
geoBuff = Buffer.allocUnsafe(21);
|
|
geoBuff.writeInt8(0x01, 0); //LITTLE ENDIAN
|
|
geoBuff.writeInt32LE(1, 1); //wkbPoint
|
|
if (
|
|
value.coordinates &&
|
|
Array.isArray(value.coordinates) &&
|
|
value.coordinates.length >= 2 &&
|
|
!isNaN(value.coordinates[0]) &&
|
|
!isNaN(value.coordinates[1])
|
|
) {
|
|
geoBuff.writeDoubleLE(value.coordinates[0], 5); //X
|
|
geoBuff.writeDoubleLE(value.coordinates[1], 13); //Y
|
|
return geoBuff;
|
|
} else {
|
|
return null;
|
|
}
|
|
|
|
case 'LineString':
|
|
if (value.coordinates && Array.isArray(value.coordinates)) {
|
|
const pointNumber = value.coordinates.length;
|
|
geoBuff = Buffer.allocUnsafe(9 + 16 * pointNumber);
|
|
geoBuff.writeInt8(0x01, 0); //LITTLE ENDIAN
|
|
geoBuff.writeInt32LE(2, 1); //wkbLineString
|
|
geoBuff.writeInt32LE(pointNumber, 5);
|
|
for (let i = 0; i < pointNumber; i++) {
|
|
if (
|
|
value.coordinates[i] &&
|
|
Array.isArray(value.coordinates[i]) &&
|
|
value.coordinates[i].length >= 2 &&
|
|
!isNaN(value.coordinates[i][0]) &&
|
|
!isNaN(value.coordinates[i][1])
|
|
) {
|
|
geoBuff.writeDoubleLE(value.coordinates[i][0], 9 + 16 * i); //X
|
|
geoBuff.writeDoubleLE(value.coordinates[i][1], 17 + 16 * i); //Y
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
return geoBuff;
|
|
} else {
|
|
return null;
|
|
}
|
|
|
|
case 'Polygon':
|
|
if (value.coordinates && Array.isArray(value.coordinates)) {
|
|
const numRings = value.coordinates.length;
|
|
let size = 0;
|
|
for (let i = 0; i < numRings; i++) {
|
|
size += 4 + 16 * value.coordinates[i].length;
|
|
}
|
|
geoBuff = Buffer.allocUnsafe(9 + size);
|
|
geoBuff.writeInt8(0x01, 0); //LITTLE ENDIAN
|
|
geoBuff.writeInt32LE(3, 1); //wkbPolygon
|
|
geoBuff.writeInt32LE(numRings, 5);
|
|
pos = 9;
|
|
for (let i = 0; i < numRings; i++) {
|
|
const lineString = value.coordinates[i];
|
|
if (lineString && Array.isArray(lineString)) {
|
|
geoBuff.writeInt32LE(lineString.length, pos);
|
|
pos += 4;
|
|
for (let j = 0; j < lineString.length; j++) {
|
|
if (
|
|
lineString[j] &&
|
|
Array.isArray(lineString[j]) &&
|
|
lineString[j].length >= 2 &&
|
|
!isNaN(lineString[j][0]) &&
|
|
!isNaN(lineString[j][1])
|
|
) {
|
|
geoBuff.writeDoubleLE(lineString[j][0], pos); //X
|
|
geoBuff.writeDoubleLE(lineString[j][1], pos + 8); //Y
|
|
pos += 16;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return geoBuff;
|
|
} else {
|
|
return null;
|
|
}
|
|
|
|
case 'MultiPoint':
|
|
type = 'MultiPoint';
|
|
geoBuff = Buffer.allocUnsafe(9);
|
|
geoBuff.writeInt8(0x01, 0); //LITTLE ENDIAN
|
|
geoBuff.writeInt32LE(4, 1); //wkbMultiPoint
|
|
break;
|
|
|
|
case 'MultiLineString':
|
|
type = 'MultiLineString';
|
|
geoBuff = Buffer.allocUnsafe(9);
|
|
geoBuff.writeInt8(0x01, 0); //LITTLE ENDIAN
|
|
geoBuff.writeInt32LE(5, 1); //wkbMultiLineString
|
|
break;
|
|
|
|
case 'MultiPolygon':
|
|
type = 'MultiPolygon';
|
|
geoBuff = Buffer.allocUnsafe(9);
|
|
geoBuff.writeInt8(0x01, 0); //LITTLE ENDIAN
|
|
geoBuff.writeInt32LE(6, 1); //wkbMultiPolygon
|
|
break;
|
|
|
|
case 'GeometryCollection':
|
|
geoBuff = Buffer.allocUnsafe(9);
|
|
geoBuff.writeInt8(0x01, 0); //LITTLE ENDIAN
|
|
geoBuff.writeInt32LE(7, 1); //wkbGeometryCollection
|
|
|
|
if (value.geometries && Array.isArray(value.geometries)) {
|
|
const coordinateLength = value.geometries.length;
|
|
const subArrays = [geoBuff];
|
|
for (let i = 0; i < coordinateLength; i++) {
|
|
const tmpBuf = this.getBufferFromGeometryValue(value.geometries[i]);
|
|
if (tmpBuf == null) break;
|
|
subArrays.push(tmpBuf);
|
|
}
|
|
geoBuff.writeInt32LE(subArrays.length - 1, 5);
|
|
return Buffer.concat(subArrays);
|
|
} else {
|
|
geoBuff.writeInt32LE(0, 5);
|
|
return geoBuff;
|
|
}
|
|
default:
|
|
return null;
|
|
}
|
|
if (value.coordinates && Array.isArray(value.coordinates)) {
|
|
const coordinateLength = value.coordinates.length;
|
|
const subArrays = [geoBuff];
|
|
for (let i = 0; i < coordinateLength; i++) {
|
|
const tmpBuf = this.getBufferFromGeometryValue(value.coordinates[i], type);
|
|
if (tmpBuf == null) break;
|
|
subArrays.push(tmpBuf);
|
|
}
|
|
geoBuff.writeInt32LE(subArrays.length - 1, 5);
|
|
return Buffer.concat(subArrays);
|
|
} else {
|
|
geoBuff.writeInt32LE(0, 5);
|
|
return geoBuff;
|
|
}
|
|
} else {
|
|
switch (headerType) {
|
|
case 'MultiPoint':
|
|
if (value && Array.isArray(value) && value.length >= 2 && !isNaN(value[0]) && !isNaN(value[1])) {
|
|
geoBuff = Buffer.allocUnsafe(21);
|
|
geoBuff.writeInt8(0x01, 0); //LITTLE ENDIAN
|
|
geoBuff.writeInt32LE(1, 1); //wkbPoint
|
|
geoBuff.writeDoubleLE(value[0], 5); //X
|
|
geoBuff.writeDoubleLE(value[1], 13); //Y
|
|
return geoBuff;
|
|
}
|
|
return null;
|
|
|
|
case 'MultiLineString':
|
|
if (value && Array.isArray(value)) {
|
|
const pointNumber = value.length;
|
|
geoBuff = Buffer.allocUnsafe(9 + 16 * pointNumber);
|
|
geoBuff.writeInt8(0x01, 0); //LITTLE ENDIAN
|
|
geoBuff.writeInt32LE(2, 1); //wkbLineString
|
|
geoBuff.writeInt32LE(pointNumber, 5);
|
|
for (let i = 0; i < pointNumber; i++) {
|
|
if (
|
|
value[i] &&
|
|
Array.isArray(value[i]) &&
|
|
value[i].length >= 2 &&
|
|
!isNaN(value[i][0]) &&
|
|
!isNaN(value[i][1])
|
|
) {
|
|
geoBuff.writeDoubleLE(value[i][0], 9 + 16 * i); //X
|
|
geoBuff.writeDoubleLE(value[i][1], 17 + 16 * i); //Y
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
return geoBuff;
|
|
}
|
|
return null;
|
|
|
|
case 'MultiPolygon':
|
|
if (value && Array.isArray(value)) {
|
|
const numRings = value.length;
|
|
let size = 0;
|
|
for (let i = 0; i < numRings; i++) {
|
|
size += 4 + 16 * value[i].length;
|
|
}
|
|
geoBuff = Buffer.allocUnsafe(9 + size);
|
|
geoBuff.writeInt8(0x01, 0); //LITTLE ENDIAN
|
|
geoBuff.writeInt32LE(3, 1); //wkbPolygon
|
|
geoBuff.writeInt32LE(numRings, 5);
|
|
pos = 9;
|
|
for (let i = 0; i < numRings; i++) {
|
|
const lineString = value[i];
|
|
if (lineString && Array.isArray(lineString)) {
|
|
geoBuff.writeInt32LE(lineString.length, pos);
|
|
pos += 4;
|
|
for (let j = 0; j < lineString.length; j++) {
|
|
if (
|
|
lineString[j] &&
|
|
Array.isArray(lineString[j]) &&
|
|
lineString[j].length >= 2 &&
|
|
!isNaN(lineString[j][0]) &&
|
|
!isNaN(lineString[j][1])
|
|
) {
|
|
geoBuff.writeDoubleLE(lineString[j][0], pos); //X
|
|
geoBuff.writeDoubleLE(lineString[j][1], pos + 8); //Y
|
|
pos += 16;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return geoBuff;
|
|
}
|
|
return null;
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = BinaryEncoder;
|