ferrum-db-client
Advanced tools
Comparing version 0.0.5 to 0.1.0
/// <reference types="node" /> | ||
import { BinaryWriter } from "csharp-binary-stream"; | ||
import { Socket } from "net"; | ||
import { BinaryWriter } from "csharp-binary-stream"; | ||
export declare type SupportedEncodingTypes = "ndjson" | "json" | "bson" | "string" | "binary"; | ||
export declare type SupportedCompressionTypes = "gzip" | "none"; | ||
declare enum ApiMessageType { | ||
CREATE_DATABASE = 0, | ||
CREATE_DATABASE_IF_NOT_EXIST = 1, | ||
HAS_DATABASE = 2, | ||
DROP_DATABASE = 3, | ||
LIST_DATABASES = 4, | ||
CLEAR_DATABASE = 5, | ||
CREATE_INDEX = 6, | ||
CREATE_INDEX_IF_NOT_EXIST = 7, | ||
DELETE_INDEX = 8, | ||
HAS_INDEX = 9, | ||
GET_INDEXES = 10, | ||
COMPACT = 11, | ||
INDEX_HAS = 12, | ||
INDEX_GET = 13, | ||
INDEX_SET = 14, | ||
INDEX_DELETE = 15, | ||
INDEX_GET_KEYS = 16, | ||
INDEX_CLEAR = 17, | ||
INDEX_GET_RECORD_COUNT = 18, | ||
INDEX_GET_RECORD_SIZE = 19, | ||
INDEX_READ_CHUNK = 20, | ||
INDEX_READ_UNTIL = 21, | ||
INDEX_OPEN_WRITE_STREAM = 22, | ||
INDEX_CLOSE_WRITE_STREAM = 23, | ||
INDEX_WRITE_STREAM_WRITE_CHUNK = 24, | ||
HEARTBEAT = 25 | ||
} | ||
export declare function ferrumConnect(ip: string, port: number): Promise<FerrumServerConnection>; | ||
declare class FerrumServerClient { | ||
import { ApiMessageType } from "./protcol"; | ||
export declare class FerrumServerClient { | ||
protected id: number; | ||
@@ -58,44 +28,2 @@ socket: Socket; | ||
} | ||
export declare class FerrumServerConnection { | ||
private client; | ||
constructor(client: FerrumServerClient); | ||
disconnect(): void; | ||
createDatabaseIfNotExists(dbName: string): Promise<FerrumDBRemote>; | ||
createDatabase(dbName: string): Promise<FerrumDBRemote>; | ||
hasDatabase(dbName: string): Promise<boolean>; | ||
dropDatabase(dbName: string): Promise<void>; | ||
clearDatabase(dbName: string): Promise<void>; | ||
getDatabaseNames(): Promise<string[]>; | ||
getDatabase(dbName: string): FerrumDBRemote; | ||
} | ||
export declare class FerrumDBRemote { | ||
private client; | ||
private dbName; | ||
constructor(client: FerrumServerClient, dbName: string); | ||
createIndexIfNotExist<T>(index: string, pageFileSize?: number): Promise<IndexRemote<T>>; | ||
getIndex<T>(index: string, encoding?: SupportedEncodingTypes, compression?: SupportedCompressionTypes): IndexRemote<T>; | ||
deleteIndex(index: string): Promise<void>; | ||
createIndex<T>(index: string, pageFileSize?: number): Promise<IndexRemote<T>>; | ||
hasIndex(index: string): Promise<boolean>; | ||
getIndexes(): Promise<string[]>; | ||
compact(): Promise<void>; | ||
} | ||
export declare class IndexRemote<T> { | ||
private client; | ||
private indexKey; | ||
private encoding; | ||
private compression; | ||
private database; | ||
constructor(client: FerrumServerClient, database: string, indexKey: string, encoding: SupportedEncodingTypes, compression: SupportedCompressionTypes); | ||
has(key: string): Promise<boolean>; | ||
getRecordSize(key: string): Promise<number>; | ||
getRecordCount(): Promise<number>; | ||
get(key: string): Promise<T>; | ||
readChunk(key: string, offset: number, size: number): Promise<Buffer>; | ||
put(key: string, value: T): Promise<void>; | ||
set(key: string, value: T): Promise<void>; | ||
clear(): Promise<void>; | ||
getKeys(): Promise<string[]>; | ||
} | ||
export {}; | ||
//# sourceMappingURL=client.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.IndexRemote = exports.FerrumDBRemote = exports.FerrumServerConnection = exports.ferrumConnect = void 0; | ||
const net_1 = require("net"); | ||
exports.FerrumServerClient = void 0; | ||
const csharp_binary_stream_1 = require("csharp-binary-stream"); | ||
const zlib_1 = require("zlib"); | ||
const util_1 = require("util"); | ||
const bson_1 = require("bson"); | ||
const gunzipPromise = util_1.promisify(zlib_1.gunzip); | ||
const gzipPromise = util_1.promisify(zlib_1.gzip); | ||
let bsonBufferSize = 17825792; | ||
const protcol_1 = require("./protcol"); | ||
const utils_1 = require("./utils"); | ||
const MAX_BUFFER_SIZE = 128 * 1024 * 1024; | ||
var ApiMessageType; | ||
(function (ApiMessageType) { | ||
ApiMessageType[ApiMessageType["CREATE_DATABASE"] = 0] = "CREATE_DATABASE"; | ||
ApiMessageType[ApiMessageType["CREATE_DATABASE_IF_NOT_EXIST"] = 1] = "CREATE_DATABASE_IF_NOT_EXIST"; | ||
ApiMessageType[ApiMessageType["HAS_DATABASE"] = 2] = "HAS_DATABASE"; | ||
ApiMessageType[ApiMessageType["DROP_DATABASE"] = 3] = "DROP_DATABASE"; | ||
ApiMessageType[ApiMessageType["LIST_DATABASES"] = 4] = "LIST_DATABASES"; | ||
ApiMessageType[ApiMessageType["CLEAR_DATABASE"] = 5] = "CLEAR_DATABASE"; | ||
ApiMessageType[ApiMessageType["CREATE_INDEX"] = 6] = "CREATE_INDEX"; | ||
ApiMessageType[ApiMessageType["CREATE_INDEX_IF_NOT_EXIST"] = 7] = "CREATE_INDEX_IF_NOT_EXIST"; | ||
ApiMessageType[ApiMessageType["DELETE_INDEX"] = 8] = "DELETE_INDEX"; | ||
ApiMessageType[ApiMessageType["HAS_INDEX"] = 9] = "HAS_INDEX"; | ||
ApiMessageType[ApiMessageType["GET_INDEXES"] = 10] = "GET_INDEXES"; | ||
ApiMessageType[ApiMessageType["COMPACT"] = 11] = "COMPACT"; | ||
ApiMessageType[ApiMessageType["INDEX_HAS"] = 12] = "INDEX_HAS"; | ||
ApiMessageType[ApiMessageType["INDEX_GET"] = 13] = "INDEX_GET"; | ||
ApiMessageType[ApiMessageType["INDEX_SET"] = 14] = "INDEX_SET"; | ||
ApiMessageType[ApiMessageType["INDEX_DELETE"] = 15] = "INDEX_DELETE"; | ||
ApiMessageType[ApiMessageType["INDEX_GET_KEYS"] = 16] = "INDEX_GET_KEYS"; | ||
ApiMessageType[ApiMessageType["INDEX_CLEAR"] = 17] = "INDEX_CLEAR"; | ||
ApiMessageType[ApiMessageType["INDEX_GET_RECORD_COUNT"] = 18] = "INDEX_GET_RECORD_COUNT"; | ||
ApiMessageType[ApiMessageType["INDEX_GET_RECORD_SIZE"] = 19] = "INDEX_GET_RECORD_SIZE"; | ||
ApiMessageType[ApiMessageType["INDEX_READ_CHUNK"] = 20] = "INDEX_READ_CHUNK"; | ||
ApiMessageType[ApiMessageType["INDEX_READ_UNTIL"] = 21] = "INDEX_READ_UNTIL"; | ||
ApiMessageType[ApiMessageType["INDEX_OPEN_WRITE_STREAM"] = 22] = "INDEX_OPEN_WRITE_STREAM"; | ||
ApiMessageType[ApiMessageType["INDEX_CLOSE_WRITE_STREAM"] = 23] = "INDEX_CLOSE_WRITE_STREAM"; | ||
ApiMessageType[ApiMessageType["INDEX_WRITE_STREAM_WRITE_CHUNK"] = 24] = "INDEX_WRITE_STREAM_WRITE_CHUNK"; | ||
ApiMessageType[ApiMessageType["HEARTBEAT"] = 25] = "HEARTBEAT"; | ||
})(ApiMessageType || (ApiMessageType = {})); | ||
function ferrumConnect(ip, port) { | ||
return new Promise((resolve, reject) => { | ||
const socket = new net_1.Socket(); | ||
socket.setNoDelay(true); | ||
socket.connect(port, ip); | ||
let client; | ||
socket.once("connect", () => { | ||
client = new FerrumServerClient(socket); | ||
resolve(new FerrumServerConnection(client)); | ||
}); | ||
socket.once("error", (e) => { | ||
reject(e); | ||
}); | ||
socket.once("timeout", () => { | ||
reject(new Error("Connection timeout")); | ||
}); | ||
}); | ||
} | ||
exports.ferrumConnect = ferrumConnect; | ||
class FerrumServerClient { | ||
@@ -159,10 +106,10 @@ constructor(socket) { | ||
this.heartBeatPending = true; | ||
const { bw, myId } = this.getSendWriter(ApiMessageType.HEARTBEAT, 0); | ||
const { bw, myId } = this.getSendWriter(protcol_1.ApiMessageType.HEARTBEAT, 0); | ||
this.sendMsg(bw); | ||
const response = await this.getResponse(myId); | ||
this.heartBeatPending = false; | ||
const br = getBinaryReader(response); | ||
const br = utils_1.getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
return utils_1.handleErrorResponse(br); | ||
} | ||
@@ -212,475 +159,3 @@ } | ||
} | ||
class FerrumServerConnection { | ||
constructor(client) { | ||
this.client = client; | ||
} | ||
disconnect() { | ||
this.client.disconnect(); | ||
} | ||
async createDatabaseIfNotExists(dbName) { | ||
const { bw, myId } = this.client.getSendWriter(ApiMessageType.CREATE_DATABASE_IF_NOT_EXIST, dbName.length); | ||
bw.writeString(dbName, csharp_binary_stream_1.Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} | ||
else { | ||
return new FerrumDBRemote(this.client, dbName); | ||
} | ||
} | ||
async createDatabase(dbName) { | ||
const { bw, myId } = this.client.getSendWriter(ApiMessageType.CREATE_DATABASE, dbName.length); | ||
bw.writeString(dbName, csharp_binary_stream_1.Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} | ||
else { | ||
return new FerrumDBRemote(this.client, dbName); | ||
} | ||
} | ||
async hasDatabase(dbName) { | ||
const { bw, myId } = this.client.getSendWriter(ApiMessageType.HAS_DATABASE, dbName.length); | ||
bw.writeString(dbName, csharp_binary_stream_1.Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} | ||
else { | ||
return br.readBoolean(); | ||
} | ||
} | ||
async dropDatabase(dbName) { | ||
const { bw, myId } = this.client.getSendWriter(ApiMessageType.DROP_DATABASE, dbName.length); | ||
bw.writeString(dbName, csharp_binary_stream_1.Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} | ||
else { | ||
return undefined; | ||
} | ||
} | ||
async clearDatabase(dbName) { | ||
const { bw, myId } = this.client.getSendWriter(ApiMessageType.CLEAR_DATABASE, dbName.length); | ||
bw.writeString(dbName, csharp_binary_stream_1.Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} | ||
return undefined; | ||
} | ||
async getDatabaseNames() { | ||
const { bw, myId } = this.client.getSendWriter(ApiMessageType.LIST_DATABASES, 0); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} | ||
else { | ||
const len = br.readInt(); | ||
const result = new Array(len); | ||
for (let i = 0; i < len; i++) { | ||
result[i] = br.readString(csharp_binary_stream_1.Encoding.Utf8); | ||
} | ||
return result; | ||
} | ||
} | ||
getDatabase(dbName) { | ||
return new FerrumDBRemote(this.client, dbName); | ||
} | ||
} | ||
exports.FerrumServerConnection = FerrumServerConnection; | ||
class FerrumDBRemote { | ||
constructor(client, dbName) { | ||
this.client = client; | ||
this.dbName = dbName; | ||
} | ||
async createIndexIfNotExist(index, pageFileSize = 0) { | ||
const { bw, myId } = this.client.getSendWriter(ApiMessageType.CREATE_INDEX_IF_NOT_EXIST, this.dbName.length + index.length); | ||
bw.writeString(this.dbName, csharp_binary_stream_1.Encoding.Utf8); | ||
bw.writeString(index, csharp_binary_stream_1.Encoding.Utf8); | ||
bw.writeUnsignedInt(pageFileSize); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} | ||
else { | ||
return this.getIndex(index); | ||
} | ||
} | ||
getIndex(index, encoding = "bson", compression = "gzip") { | ||
return new IndexRemote(this.client, this.dbName, index, encoding, compression); | ||
} | ||
async deleteIndex(index) { | ||
const { bw, myId } = this.client.getSendWriter(ApiMessageType.DELETE_INDEX, index.length); | ||
bw.writeString(this.dbName, csharp_binary_stream_1.Encoding.Utf8); | ||
bw.writeString(index, csharp_binary_stream_1.Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} | ||
else { | ||
return undefined; | ||
} | ||
} | ||
async createIndex(index, pageFileSize = 0) { | ||
const { bw, myId } = this.client.getSendWriter(ApiMessageType.CREATE_INDEX, index.length); | ||
bw.writeString(this.dbName, csharp_binary_stream_1.Encoding.Utf8); | ||
bw.writeString(index, csharp_binary_stream_1.Encoding.Utf8); | ||
bw.writeUnsignedInt(pageFileSize); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} | ||
else { | ||
return this.getIndex(index); | ||
} | ||
} | ||
async hasIndex(index) { | ||
const { bw, myId } = this.client.getSendWriter(ApiMessageType.HAS_INDEX, index.length); | ||
bw.writeString(this.dbName, csharp_binary_stream_1.Encoding.Utf8); | ||
bw.writeString(index, csharp_binary_stream_1.Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} | ||
else { | ||
return br.readBoolean(); | ||
} | ||
} | ||
async getIndexes() { | ||
const { bw, myId } = this.client.getSendWriter(ApiMessageType.GET_INDEXES, 0); | ||
bw.writeString(this.dbName, csharp_binary_stream_1.Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} | ||
else { | ||
const len = br.readInt(); | ||
const result = new Array(len); | ||
for (let i = 0; i < len; i++) { | ||
result[i] = br.readString(csharp_binary_stream_1.Encoding.Utf8); | ||
} | ||
return result; | ||
} | ||
} | ||
async compact() { | ||
const { bw, myId } = this.client.getSendWriter(ApiMessageType.COMPACT, 0); | ||
bw.writeString(this.dbName, csharp_binary_stream_1.Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} | ||
else { | ||
return undefined; | ||
} | ||
} | ||
} | ||
exports.FerrumDBRemote = FerrumDBRemote; | ||
class IndexRemote { | ||
constructor(client, database, indexKey, encoding, compression) { | ||
this.client = client; | ||
this.database = database; | ||
this.encoding = encoding; | ||
this.compression = compression; | ||
this.indexKey = indexKey; | ||
} | ||
async has(key) { | ||
const { bw, myId } = this.client.getSendWriter(ApiMessageType.INDEX_HAS, this.database.length + this.indexKey.length + key.length); | ||
bw.writeString(this.database, csharp_binary_stream_1.Encoding.Utf8); | ||
bw.writeString(this.indexKey, csharp_binary_stream_1.Encoding.Utf8); | ||
bw.writeString(key, csharp_binary_stream_1.Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} | ||
else { | ||
return br.readBoolean(); | ||
} | ||
} | ||
async getRecordSize(key) { | ||
const { bw, myId } = this.client.getSendWriter(ApiMessageType.INDEX_GET_RECORD_SIZE, this.database.length + this.indexKey.length + key.length); | ||
bw.writeString(this.database, csharp_binary_stream_1.Encoding.Utf8); | ||
bw.writeString(this.indexKey, csharp_binary_stream_1.Encoding.Utf8); | ||
bw.writeString(key, csharp_binary_stream_1.Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} | ||
else { | ||
const len = br.readLong(); | ||
return len; | ||
} | ||
} | ||
async getRecordCount() { | ||
const { bw, myId } = this.client.getSendWriter(ApiMessageType.INDEX_GET_RECORD_COUNT, this.database.length + this.indexKey.length); | ||
bw.writeString(this.database, csharp_binary_stream_1.Encoding.Utf8); | ||
bw.writeString(this.indexKey, csharp_binary_stream_1.Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} | ||
else { | ||
const len = br.readInt(); | ||
return len; | ||
} | ||
} | ||
async get(key) { | ||
const { bw, myId } = this.client.getSendWriter(ApiMessageType.INDEX_GET, this.database.length + this.indexKey.length + key.length); | ||
bw.writeString(this.database, csharp_binary_stream_1.Encoding.Utf8); | ||
bw.writeString(this.indexKey, csharp_binary_stream_1.Encoding.Utf8); | ||
bw.writeString(key, csharp_binary_stream_1.Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} | ||
else { | ||
const len = br.readInt(); | ||
try { | ||
const result = Buffer.from(br.readBytes(len)); | ||
let decompressed; | ||
let decodedValue; | ||
switch (this.compression) { | ||
case "gzip": | ||
decompressed = await gunzipPromise(result); | ||
break; | ||
default: | ||
decompressed = result; | ||
break; | ||
} | ||
if (this.encoding === "bson") { | ||
if (decompressed.length > bsonBufferSize) { | ||
bson_1.setInternalBufferSize(decompressed.length); | ||
bsonBufferSize = decompressed.length; | ||
} | ||
decodedValue = bson_1.deserialize(decompressed); | ||
} | ||
else if (this.encoding === "json") { | ||
try { | ||
decodedValue = JSON.parse(decompressed.toString("utf8")); | ||
} | ||
catch (e) { | ||
throw new Error(`Failed to decode JSON for key ${key}. ${decompressed.toString("utf8")}`); | ||
} | ||
} | ||
else if (this.encoding === "ndjson") { | ||
decodedValue = decompressed | ||
.toString("utf8") | ||
.split("\n") | ||
.map((e) => JSON.parse(e)); | ||
} | ||
else if (this.encoding === "string") { | ||
decodedValue = decompressed.toString("utf8"); | ||
} | ||
else { | ||
decodedValue = decompressed; | ||
} | ||
return decodedValue; | ||
} | ||
catch (e) { | ||
throw new Error(`Failed to get ${key} from ${this.indexKey} \n\nCaused by: ${e}`); | ||
} | ||
} | ||
} | ||
async readChunk(key, offset, size) { | ||
const { bw, myId } = this.client.getSendWriter(ApiMessageType.INDEX_GET, this.database.length + this.indexKey.length + key.length); | ||
bw.writeString(this.database, csharp_binary_stream_1.Encoding.Utf8); | ||
bw.writeString(this.indexKey, csharp_binary_stream_1.Encoding.Utf8); | ||
bw.writeString(key, csharp_binary_stream_1.Encoding.Utf8); | ||
bw.writeLong(offset); | ||
bw.writeUnsignedInt(size); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} | ||
else { | ||
const len = br.readInt(); | ||
const result = Buffer.from(br.readBytes(len)); | ||
return result; | ||
} | ||
} | ||
// Alias for set | ||
put(key, value) { | ||
return this.set(key, value); | ||
} | ||
async set(key, value) { | ||
let encodedData; | ||
if (this.encoding === "bson") { | ||
encodedData = encodeBSON(value); | ||
} | ||
else if (this.encoding === "json") { | ||
encodedData = Buffer.from(JSON.stringify(value)); | ||
} | ||
else if (this.encoding === "ndjson") { | ||
if (Array.isArray(value)) { | ||
encodedData = Buffer.from(value.map((e) => JSON.stringify(e)).join("\n")); | ||
} | ||
else { | ||
throw new Error(`Non array data cannot be ndjson encoded`); | ||
} | ||
} | ||
else if (this.encoding === "string") { | ||
if (typeof value !== "string") { | ||
throw new Error(`Invalid input. Expected string got ${typeof value}`); | ||
} | ||
encodedData = Buffer.from(value); | ||
} | ||
else { | ||
if (value instanceof Buffer) { | ||
encodedData = value; | ||
} | ||
else { | ||
throw new Error(`Invalid input. Expected buffer`); | ||
} | ||
} | ||
switch (this.compression) { | ||
case "gzip": | ||
encodedData = await gzipPromise(encodedData); | ||
break; | ||
default: | ||
break; | ||
} | ||
const { bw, myId } = this.client.getSendWriter(ApiMessageType.INDEX_SET, this.database.length + | ||
this.indexKey.length + | ||
key.length + | ||
encodedData.length); | ||
bw.writeString(this.database, csharp_binary_stream_1.Encoding.Utf8); | ||
bw.writeString(this.indexKey, csharp_binary_stream_1.Encoding.Utf8); | ||
bw.writeString(key, csharp_binary_stream_1.Encoding.Utf8); | ||
bw.writeInt(encodedData.length); | ||
for (const byte of encodedData) { | ||
bw.writeByte(byte); | ||
} | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} | ||
else { | ||
return undefined; | ||
} | ||
} | ||
async clear() { | ||
const { bw, myId } = this.client.getSendWriter(ApiMessageType.INDEX_CLEAR, this.database.length + this.indexKey.length); | ||
bw.writeString(this.database, csharp_binary_stream_1.Encoding.Utf8); | ||
bw.writeString(this.indexKey, csharp_binary_stream_1.Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} | ||
else { | ||
return undefined; | ||
} | ||
} | ||
async getKeys() { | ||
const { bw, myId } = this.client.getSendWriter(ApiMessageType.INDEX_GET_KEYS, this.database.length + this.indexKey.length); | ||
bw.writeString(this.database, csharp_binary_stream_1.Encoding.Utf8); | ||
bw.writeString(this.indexKey, csharp_binary_stream_1.Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} | ||
else { | ||
const len = br.readInt(); | ||
const result = new Array(len); | ||
for (let i = 0; i < len; i++) { | ||
result[i] = br.readString(csharp_binary_stream_1.Encoding.Utf8); | ||
} | ||
return result; | ||
} | ||
} | ||
} | ||
exports.IndexRemote = IndexRemote; | ||
function handleErrorResponse(br) { | ||
throw new Error(`[Ferrum DB Server]${br.readString(csharp_binary_stream_1.Encoding.Utf8)}`); | ||
} | ||
function getBinaryReader(buffer) { | ||
//Hack to skip the constructor of BinaryReader because it does unecessairy and expensive copying | ||
const br = new csharp_binary_stream_1.BinaryReader(Buffer.from([])); | ||
//@ts-ignore | ||
br._stream = buffer.buffer; | ||
//@ts-ignore | ||
br._view = buffer; | ||
//@ts-ignore | ||
br._bufferStart = buffer.byteOffset; | ||
//@ts-ignore | ||
br._bufferLength = buffer.byteLength; | ||
return br; | ||
} | ||
// The BSON library decided that it would be smart to not support automatic buffer expansion and you can't know ahead of time how much space you need | ||
// So the best we can do is catch range errors and resize the buffer on error | ||
function encodeBSON(value) { | ||
try { | ||
return bson_1.serialize(value); | ||
} | ||
catch (e) { | ||
if (e instanceof RangeError) { | ||
bsonBufferSize *= 2; | ||
bson_1.setInternalBufferSize(bsonBufferSize); | ||
return encodeBSON(value); | ||
} | ||
else { | ||
throw e; | ||
} | ||
} | ||
} | ||
exports.FerrumServerClient = FerrumServerClient; | ||
//# sourceMappingURL=client.js.map |
{ | ||
"name": "ferrum-db-client", | ||
"version": "0.0.5", | ||
"main": "dist/client.js", | ||
"typings": "dist/client.d.ts", | ||
"version": "0.1.0", | ||
"main": "dist/index.js", | ||
"typings": "dist/index.d.ts", | ||
"dependencies": { | ||
@@ -7,0 +7,0 @@ "@types/node": "14.14.34", |
@@ -0,75 +1,9 @@ | ||
import { BinaryWriter, Encoding } from "csharp-binary-stream"; | ||
import { Socket } from "net"; | ||
import { BinaryReader, BinaryWriter, Encoding } from "csharp-binary-stream"; | ||
import { gzip, gunzip } from "zlib"; | ||
import { promisify } from "util"; | ||
import { serialize, deserialize, setInternalBufferSize } from "bson"; | ||
import { ApiMessageType } from "./protcol"; | ||
import { getBinaryReader, handleErrorResponse } from "./utils"; | ||
const gunzipPromise = promisify(gunzip); | ||
const gzipPromise = promisify(gzip); | ||
export type SupportedEncodingTypes = | ||
| "ndjson" | ||
| "json" | ||
| "bson" | ||
| "string" | ||
| "binary"; | ||
export type SupportedCompressionTypes = "gzip" | "none"; | ||
let bsonBufferSize = 17825792; | ||
const MAX_BUFFER_SIZE = 128 * 1024 * 1024; | ||
enum ApiMessageType { | ||
CREATE_DATABASE = 0, | ||
CREATE_DATABASE_IF_NOT_EXIST = 1, | ||
HAS_DATABASE = 2, | ||
DROP_DATABASE = 3, | ||
LIST_DATABASES = 4, | ||
CLEAR_DATABASE = 5, | ||
CREATE_INDEX = 6, | ||
CREATE_INDEX_IF_NOT_EXIST = 7, | ||
DELETE_INDEX = 8, | ||
HAS_INDEX = 9, | ||
GET_INDEXES = 10, | ||
COMPACT = 11, | ||
INDEX_HAS = 12, | ||
INDEX_GET = 13, | ||
INDEX_SET = 14, | ||
INDEX_DELETE = 15, | ||
INDEX_GET_KEYS = 16, | ||
INDEX_CLEAR = 17, | ||
INDEX_GET_RECORD_COUNT = 18, | ||
INDEX_GET_RECORD_SIZE = 19, | ||
INDEX_READ_CHUNK = 20, | ||
INDEX_READ_UNTIL = 21, | ||
INDEX_OPEN_WRITE_STREAM = 22, | ||
INDEX_CLOSE_WRITE_STREAM = 23, | ||
INDEX_WRITE_STREAM_WRITE_CHUNK = 24, | ||
HEARTBEAT = 25, | ||
} | ||
export function ferrumConnect( | ||
ip: string, | ||
port: number | ||
): Promise<FerrumServerConnection> { | ||
return new Promise((resolve, reject) => { | ||
const socket = new Socket(); | ||
socket.setNoDelay(true); | ||
socket.connect(port, ip); | ||
let client: FerrumServerClient; | ||
socket.once("connect", () => { | ||
client = new FerrumServerClient(socket); | ||
resolve(new FerrumServerConnection(client)); | ||
}); | ||
socket.once("error", (e) => { | ||
reject(e); | ||
}); | ||
socket.once("timeout", () => { | ||
reject(new Error("Connection timeout")); | ||
}); | ||
}); | ||
} | ||
class FerrumServerClient { | ||
export class FerrumServerClient { | ||
protected id: number = 0; | ||
@@ -268,629 +202,1 @@ public socket: Socket; | ||
} | ||
export class FerrumServerConnection { | ||
private client: FerrumServerClient; | ||
constructor(client: FerrumServerClient) { | ||
this.client = client; | ||
} | ||
public disconnect(): void { | ||
this.client.disconnect(); | ||
} | ||
public async createDatabaseIfNotExists( | ||
dbName: string | ||
): Promise<FerrumDBRemote> { | ||
const { bw, myId } = this.client.getSendWriter( | ||
ApiMessageType.CREATE_DATABASE_IF_NOT_EXIST, | ||
dbName.length | ||
); | ||
bw.writeString(dbName, Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} else { | ||
return new FerrumDBRemote(this.client, dbName); | ||
} | ||
} | ||
public async createDatabase(dbName: string): Promise<FerrumDBRemote> { | ||
const { bw, myId } = this.client.getSendWriter( | ||
ApiMessageType.CREATE_DATABASE, | ||
dbName.length | ||
); | ||
bw.writeString(dbName, Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} else { | ||
return new FerrumDBRemote(this.client, dbName); | ||
} | ||
} | ||
public async hasDatabase(dbName: string): Promise<boolean> { | ||
const { bw, myId } = this.client.getSendWriter( | ||
ApiMessageType.HAS_DATABASE, | ||
dbName.length | ||
); | ||
bw.writeString(dbName, Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} else { | ||
return br.readBoolean(); | ||
} | ||
} | ||
public async dropDatabase(dbName: string): Promise<void> { | ||
const { bw, myId } = this.client.getSendWriter( | ||
ApiMessageType.DROP_DATABASE, | ||
dbName.length | ||
); | ||
bw.writeString(dbName, Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} else { | ||
return undefined; | ||
} | ||
} | ||
public async clearDatabase(dbName: string): Promise<void> { | ||
const { bw, myId } = this.client.getSendWriter( | ||
ApiMessageType.CLEAR_DATABASE, | ||
dbName.length | ||
); | ||
bw.writeString(dbName, Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} | ||
return undefined; | ||
} | ||
public async getDatabaseNames(): Promise<string[]> { | ||
const { bw, myId } = this.client.getSendWriter( | ||
ApiMessageType.LIST_DATABASES, | ||
0 | ||
); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} else { | ||
const len = br.readInt(); | ||
const result = new Array(len); | ||
for (let i = 0; i < len; i++) { | ||
result[i] = br.readString(Encoding.Utf8); | ||
} | ||
return result; | ||
} | ||
} | ||
public getDatabase(dbName: string): FerrumDBRemote { | ||
return new FerrumDBRemote(this.client, dbName); | ||
} | ||
} | ||
export class FerrumDBRemote { | ||
private client: FerrumServerClient; | ||
private dbName: string; | ||
constructor(client: FerrumServerClient, dbName: string) { | ||
this.client = client; | ||
this.dbName = dbName; | ||
} | ||
public async createIndexIfNotExist<T>( | ||
index: string, | ||
pageFileSize: number = 0 | ||
): Promise<IndexRemote<T>> { | ||
const { bw, myId } = this.client.getSendWriter( | ||
ApiMessageType.CREATE_INDEX_IF_NOT_EXIST, | ||
this.dbName.length + index.length | ||
); | ||
bw.writeString(this.dbName, Encoding.Utf8); | ||
bw.writeString(index, Encoding.Utf8); | ||
bw.writeUnsignedInt(pageFileSize); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} else { | ||
return this.getIndex(index); | ||
} | ||
} | ||
public getIndex<T>( | ||
index: string, | ||
encoding: SupportedEncodingTypes = "bson", | ||
compression: SupportedCompressionTypes = "gzip" | ||
): IndexRemote<T> { | ||
return new IndexRemote<T>( | ||
this.client, | ||
this.dbName, | ||
index, | ||
encoding, | ||
compression | ||
); | ||
} | ||
public async deleteIndex(index: string): Promise<void> { | ||
const { bw, myId } = this.client.getSendWriter( | ||
ApiMessageType.DELETE_INDEX, | ||
index.length | ||
); | ||
bw.writeString(this.dbName, Encoding.Utf8); | ||
bw.writeString(index, Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} else { | ||
return undefined; | ||
} | ||
} | ||
public async createIndex<T>( | ||
index: string, | ||
pageFileSize: number = 0 | ||
): Promise<IndexRemote<T>> { | ||
const { bw, myId } = this.client.getSendWriter( | ||
ApiMessageType.CREATE_INDEX, | ||
index.length | ||
); | ||
bw.writeString(this.dbName, Encoding.Utf8); | ||
bw.writeString(index, Encoding.Utf8); | ||
bw.writeUnsignedInt(pageFileSize); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} else { | ||
return this.getIndex(index); | ||
} | ||
} | ||
public async hasIndex(index: string): Promise<boolean> { | ||
const { bw, myId } = this.client.getSendWriter( | ||
ApiMessageType.HAS_INDEX, | ||
index.length | ||
); | ||
bw.writeString(this.dbName, Encoding.Utf8); | ||
bw.writeString(index, Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} else { | ||
return br.readBoolean(); | ||
} | ||
} | ||
public async getIndexes(): Promise<string[]> { | ||
const { bw, myId } = this.client.getSendWriter( | ||
ApiMessageType.GET_INDEXES, | ||
0 | ||
); | ||
bw.writeString(this.dbName, Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} else { | ||
const len = br.readInt(); | ||
const result = new Array(len); | ||
for (let i = 0; i < len; i++) { | ||
result[i] = br.readString(Encoding.Utf8); | ||
} | ||
return result; | ||
} | ||
} | ||
public async compact(): Promise<void> { | ||
const { bw, myId } = this.client.getSendWriter( | ||
ApiMessageType.COMPACT, | ||
0 | ||
); | ||
bw.writeString(this.dbName, Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} else { | ||
return undefined; | ||
} | ||
} | ||
} | ||
export class IndexRemote<T> { | ||
private client: FerrumServerClient; | ||
private indexKey: string; | ||
private encoding: SupportedEncodingTypes; | ||
private compression: SupportedCompressionTypes; | ||
private database: string; | ||
constructor( | ||
client: FerrumServerClient, | ||
database: string, | ||
indexKey: string, | ||
encoding: SupportedEncodingTypes, | ||
compression: SupportedCompressionTypes | ||
) { | ||
this.client = client; | ||
this.database = database; | ||
this.encoding = encoding; | ||
this.compression = compression; | ||
this.indexKey = indexKey; | ||
} | ||
public async has(key: string): Promise<boolean> { | ||
const { bw, myId } = this.client.getSendWriter( | ||
ApiMessageType.INDEX_HAS, | ||
this.database.length + this.indexKey.length + key.length | ||
); | ||
bw.writeString(this.database, Encoding.Utf8); | ||
bw.writeString(this.indexKey, Encoding.Utf8); | ||
bw.writeString(key, Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} else { | ||
return br.readBoolean(); | ||
} | ||
} | ||
public async getRecordSize(key: string): Promise<number> { | ||
const { bw, myId } = this.client.getSendWriter( | ||
ApiMessageType.INDEX_GET_RECORD_SIZE, | ||
this.database.length + this.indexKey.length + key.length | ||
); | ||
bw.writeString(this.database, Encoding.Utf8); | ||
bw.writeString(this.indexKey, Encoding.Utf8); | ||
bw.writeString(key, Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} else { | ||
const len = br.readLong(); | ||
return len; | ||
} | ||
} | ||
public async getRecordCount(): Promise<number> { | ||
const { bw, myId } = this.client.getSendWriter( | ||
ApiMessageType.INDEX_GET_RECORD_COUNT, | ||
this.database.length + this.indexKey.length | ||
); | ||
bw.writeString(this.database, Encoding.Utf8); | ||
bw.writeString(this.indexKey, Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} else { | ||
const len = br.readInt(); | ||
return len; | ||
} | ||
} | ||
public async get(key: string): Promise<T> { | ||
const { bw, myId } = this.client.getSendWriter( | ||
ApiMessageType.INDEX_GET, | ||
this.database.length + this.indexKey.length + key.length | ||
); | ||
bw.writeString(this.database, Encoding.Utf8); | ||
bw.writeString(this.indexKey, Encoding.Utf8); | ||
bw.writeString(key, Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} else { | ||
const len = br.readInt(); | ||
try { | ||
const result = Buffer.from(br.readBytes(len)); | ||
let decompressed: Buffer; | ||
let decodedValue: any; | ||
switch (this.compression) { | ||
case "gzip": | ||
decompressed = await gunzipPromise(result); | ||
break; | ||
default: | ||
decompressed = result; | ||
break; | ||
} | ||
if (this.encoding === "bson") { | ||
if (decompressed.length > bsonBufferSize) { | ||
setInternalBufferSize(decompressed.length); | ||
bsonBufferSize = decompressed.length; | ||
} | ||
decodedValue = deserialize(decompressed); | ||
} else if (this.encoding === "json") { | ||
try { | ||
decodedValue = JSON.parse( | ||
decompressed.toString("utf8") | ||
); | ||
} catch (e) { | ||
throw new Error( | ||
`Failed to decode JSON for key ${key}. ${decompressed.toString( | ||
"utf8" | ||
)}` | ||
); | ||
} | ||
} else if (this.encoding === "ndjson") { | ||
decodedValue = decompressed | ||
.toString("utf8") | ||
.split("\n") | ||
.map((e) => JSON.parse(e)); | ||
} else if (this.encoding === "string") { | ||
decodedValue = decompressed.toString("utf8"); | ||
} else { | ||
decodedValue = decompressed; | ||
} | ||
return decodedValue; | ||
} catch (e) { | ||
throw new Error( | ||
`Failed to get ${key} from ${this.indexKey} \n\nCaused by: ${e}` | ||
); | ||
} | ||
} | ||
} | ||
public async readChunk( | ||
key: string, | ||
offset: number, | ||
size: number | ||
): Promise<Buffer> { | ||
const { bw, myId } = this.client.getSendWriter( | ||
ApiMessageType.INDEX_GET, | ||
this.database.length + this.indexKey.length + key.length | ||
); | ||
bw.writeString(this.database, Encoding.Utf8); | ||
bw.writeString(this.indexKey, Encoding.Utf8); | ||
bw.writeString(key, Encoding.Utf8); | ||
bw.writeLong(offset); | ||
bw.writeUnsignedInt(size); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} else { | ||
const len = br.readInt(); | ||
const result = Buffer.from(br.readBytes(len)); | ||
return result; | ||
} | ||
} | ||
// Alias for set | ||
public put(key: string, value: T): Promise<void> { | ||
return this.set(key, value); | ||
} | ||
public async set(key: string, value: T): Promise<void> { | ||
let encodedData: Buffer; | ||
if (this.encoding === "bson") { | ||
encodedData = encodeBSON(value); | ||
} else if (this.encoding === "json") { | ||
encodedData = Buffer.from(JSON.stringify(value)); | ||
} else if (this.encoding === "ndjson") { | ||
if (Array.isArray(value)) { | ||
encodedData = Buffer.from( | ||
value.map((e) => JSON.stringify(e)).join("\n") | ||
); | ||
} else { | ||
throw new Error(`Non array data cannot be ndjson encoded`); | ||
} | ||
} else if (this.encoding === "string") { | ||
if (typeof value !== "string") { | ||
throw new Error( | ||
`Invalid input. Expected string got ${typeof value}` | ||
); | ||
} | ||
encodedData = Buffer.from(value); | ||
} else { | ||
if (value instanceof Buffer) { | ||
encodedData = value; | ||
} else { | ||
throw new Error(`Invalid input. Expected buffer`); | ||
} | ||
} | ||
switch (this.compression) { | ||
case "gzip": | ||
encodedData = await gzipPromise(encodedData); | ||
break; | ||
default: | ||
break; | ||
} | ||
const { bw, myId } = this.client.getSendWriter( | ||
ApiMessageType.INDEX_SET, | ||
this.database.length + | ||
this.indexKey.length + | ||
key.length + | ||
encodedData.length | ||
); | ||
bw.writeString(this.database, Encoding.Utf8); | ||
bw.writeString(this.indexKey, Encoding.Utf8); | ||
bw.writeString(key, Encoding.Utf8); | ||
bw.writeInt(encodedData.length); | ||
for (const byte of encodedData) { | ||
bw.writeByte(byte); | ||
} | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} else { | ||
return undefined; | ||
} | ||
} | ||
public async clear(): Promise<void> { | ||
const { bw, myId } = this.client.getSendWriter( | ||
ApiMessageType.INDEX_CLEAR, | ||
this.database.length + this.indexKey.length | ||
); | ||
bw.writeString(this.database, Encoding.Utf8); | ||
bw.writeString(this.indexKey, Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} else { | ||
return undefined; | ||
} | ||
} | ||
public async getKeys(): Promise<string[]> { | ||
const { bw, myId } = this.client.getSendWriter( | ||
ApiMessageType.INDEX_GET_KEYS, | ||
this.database.length + this.indexKey.length | ||
); | ||
bw.writeString(this.database, Encoding.Utf8); | ||
bw.writeString(this.indexKey, Encoding.Utf8); | ||
this.client.sendMsg(bw); | ||
const response = await this.client.getResponse(myId); | ||
const br = getBinaryReader(response); | ||
const success = br.readBoolean(); | ||
if (!success) { | ||
return handleErrorResponse(br); | ||
} else { | ||
const len = br.readInt(); | ||
const result = new Array(len); | ||
for (let i = 0; i < len; i++) { | ||
result[i] = br.readString(Encoding.Utf8); | ||
} | ||
return result; | ||
} | ||
} | ||
} | ||
function handleErrorResponse(br: BinaryReader): never { | ||
throw new Error(`[Ferrum DB Server]${br.readString(Encoding.Utf8)}`); | ||
} | ||
function getBinaryReader(buffer: Buffer): BinaryReader { | ||
//Hack to skip the constructor of BinaryReader because it does unecessairy and expensive copying | ||
const br = new BinaryReader(Buffer.from([])); | ||
//@ts-ignore | ||
br._stream = buffer.buffer; | ||
//@ts-ignore | ||
br._view = buffer; | ||
//@ts-ignore | ||
br._bufferStart = buffer.byteOffset; | ||
//@ts-ignore | ||
br._bufferLength = buffer.byteLength; | ||
return br; | ||
} | ||
// The BSON library decided that it would be smart to not support automatic buffer expansion and you can't know ahead of time how much space you need | ||
// So the best we can do is catch range errors and resize the buffer on error | ||
function encodeBSON(value: any): Buffer { | ||
try { | ||
return serialize(value); | ||
} catch (e) { | ||
if (e instanceof RangeError) { | ||
bsonBufferSize *= 2; | ||
setInternalBufferSize(bsonBufferSize); | ||
return encodeBSON(value); | ||
} else { | ||
throw e; | ||
} | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
160774
37
2146
2