@litert/redis
Version:
A redis protocol implement for Node.js.
253 lines • 9.66 kB
JavaScript
"use strict";
/**
* Copyright 2025 Angus.Fenying <fenying@litert.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Decoder = void 0;
const C = require("./Common");
const E = require("./Errors");
const Constants = require("./Constants");
const node_events_1 = require("node:events");
var PROTO_DELIMITER = Constants.PROTO_DELIMITER;
var EDecodeStatus;
(function (EDecodeStatus) {
/**
* Reading nothing.
*/
EDecodeStatus[EDecodeStatus["IDLE"] = 0] = "IDLE";
/**
* Reading the length of a byte-string.
*/
EDecodeStatus[EDecodeStatus["READING_STRING_LENGTH"] = 1] = "READING_STRING_LENGTH";
/**
* Reading the content of a byte-string.
*/
EDecodeStatus[EDecodeStatus["READING_STRING"] = 2] = "READING_STRING";
/**
* Reading the content of a message.
*/
EDecodeStatus[EDecodeStatus["READING_MESSAGE"] = 3] = "READING_MESSAGE";
/**
* Reading the content of a failure.
*/
EDecodeStatus[EDecodeStatus["READING_FAILURE"] = 4] = "READING_FAILURE";
/**
* Reading the value of an integer.
*/
EDecodeStatus[EDecodeStatus["READING_INTEGER"] = 5] = "READING_INTEGER";
/**
* Reading the length of a list.
*/
EDecodeStatus[EDecodeStatus["READING_LIST_LENGTH"] = 6] = "READING_LIST_LENGTH";
/**
* Reading the elements of a list.
*/
EDecodeStatus[EDecodeStatus["READING_LIST"] = 7] = "READING_LIST";
})(EDecodeStatus || (EDecodeStatus = {}));
class DecodeContext {
/**
* The status of decoder context.
*/
status;
data;
value;
type;
pos;
constructor(status = EDecodeStatus.IDLE, pos = 0) {
this.data = {};
this.status = status;
this.pos = pos;
}
}
class Decoder extends node_events_1.EventEmitter {
_buf;
_ctx;
_contextStack;
_cursor;
constructor() {
super();
this.reset();
}
reset() {
this._ctx = new DecodeContext();
this._contextStack = [];
this._buf = Buffer.allocUnsafe(0);
this._cursor = 0;
return this;
}
update(data) {
this._buf = Buffer.concat([this._buf, data]);
while (this._buf.length) {
let end;
switch (this._ctx.status) {
case EDecodeStatus.READING_LIST:
case EDecodeStatus.IDLE:
switch (this._buf[this._cursor]) {
case 36: // $
this._push(EDecodeStatus.READING_STRING_LENGTH);
break;
case 42: // *
this._push(EDecodeStatus.READING_LIST_LENGTH);
break;
case 43: // +
this._push(EDecodeStatus.READING_MESSAGE);
break;
case 45: // -
this._push(EDecodeStatus.READING_FAILURE);
break;
case 58: // :
this._push(EDecodeStatus.READING_INTEGER);
break;
default:
this.emit('error', new E.E_PROTOCOL_ERROR({
'message': 'Unrecognizable format of stream.'
}));
return this;
}
break;
case EDecodeStatus.READING_MESSAGE:
this._ctx.type = C.EDataType.MESSAGE;
end = this._buf.indexOf(PROTO_DELIMITER, this._cursor);
if (end > -1) {
this._ctx.value = this._buf.subarray(this._ctx.pos, end);
this._pop(end + 2);
}
else {
// this._cursor = this._buf.length - 2;
return this;
}
break;
case EDecodeStatus.READING_FAILURE:
this._ctx.type = C.EDataType.FAILURE;
end = this._buf.indexOf(PROTO_DELIMITER, this._cursor);
if (end > -1) {
this._ctx.value = this._buf.subarray(this._ctx.pos, end);
this._pop(end + 2);
}
else {
// this._cursor = this._buf.length - 2;
return this;
}
break;
case EDecodeStatus.READING_INTEGER:
this._ctx.type = C.EDataType.INTEGER;
end = this._buf.indexOf(PROTO_DELIMITER, this._cursor);
if (end > -1) {
this._ctx.value = parseInt(this._buf.subarray(this._ctx.pos, end).toString());
this._pop(end + 2);
}
else {
// this._cursor = this._buf.length - 2;
return this;
}
break;
case EDecodeStatus.READING_LIST_LENGTH:
end = this._buf.indexOf(PROTO_DELIMITER, this._cursor);
if (end > -1) {
this._ctx.data.length = parseInt(this._buf.subarray(this._ctx.pos, end).toString());
if (this._ctx.data.length === -1) {
this._ctx.type = C.EDataType.NULL;
this._ctx.value = null;
this._pop(end + 2);
}
else {
this._ctx.type = C.EDataType.LIST;
this._ctx.value = [];
if (this._ctx.data.length === 0) {
this._pop(end + 2);
}
else {
this._cut(end + 2);
this._ctx.pos = this._cursor;
this._ctx.status = EDecodeStatus.READING_LIST;
}
}
}
else {
// this._cursor = this._buf.length - 2;
return this;
}
break;
case EDecodeStatus.READING_STRING_LENGTH:
this._ctx.type = C.EDataType.STRING;
end = this._buf.indexOf(PROTO_DELIMITER, this._cursor);
if (end > -1) {
this._ctx.data.length = parseInt(this._buf.subarray(this._ctx.pos, end).toString());
if (this._ctx.data.length === -1) {
this._ctx.type = C.EDataType.NULL;
this._ctx.value = null;
this._pop(end + 2);
}
else {
this._cut(end + 2);
this._ctx.pos = this._cursor;
this._ctx.status = EDecodeStatus.READING_STRING;
}
}
else {
// this._cursor = this._buf.length - 2;
return this;
}
break;
case EDecodeStatus.READING_STRING:
if (this._buf.length >= this._ctx.data.length + 2) {
this._ctx.value = this._buf.subarray(this._ctx.pos, this._ctx.pos + this._ctx.data.length);
this._pop(this._ctx.data.length + 2);
}
else {
// this._cursor = this._buf.length - 2;
return this;
}
break;
}
}
return this;
}
_push(newStatus) {
this._contextStack.push(this._ctx);
this._ctx = new DecodeContext(newStatus);
this._cursor++;
this._ctx.pos = this._cursor;
}
_pop(end) {
const context = this._ctx;
this._ctx = this._contextStack.pop();
if (this._ctx.status === EDecodeStatus.READING_LIST) {
this._ctx.value.push([
context.type,
context.value
]);
if (this._ctx.data.length === this._ctx.value.length) {
this._pop(0);
}
}
else {
this.emit('data', context.type, context.value);
}
if (end) {
this._cut(end);
}
}
/**
* Trim the buffer before the specific position.
*/
_cut(end) {
this._buf = this._buf.subarray(end);
this._cursor = 0;
}
}
exports.Decoder = Decoder;
exports.default = Decoder;
//# sourceMappingURL=Decoder.js.map