memx 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,103 @@
1
+ export const EMPTY_BUFFER = Buffer.alloc(0)
2
+
3
+ export enum FLAGS {
4
+ // by default everything is a "buffer"
5
+ BUFFER = 0x00000000,
6
+
7
+ // from "typeof" we get bigint, boolean, number and string
8
+ BIGINT = 0xCACA0000,
9
+ BOOLEAN = 0xCACA0001,
10
+ NUMBER = 0xCACA0002,
11
+ STRING = 0xCACA0003,
12
+
13
+ // typeof null === object
14
+ NULL = 0xCACA000E,
15
+
16
+ // objects become JSON
17
+ JSON = 0xCACA000F,
18
+
19
+ // typed arrays
20
+ UINT8ARRAY = 0xCACA0010,
21
+ UINT8CLAMPEDARRAY = 0xCACA0011,
22
+ UINT16ARRAY = 0xCACA0012,
23
+ UINT32ARRAY = 0xCACA0013,
24
+ INT8ARRAY = 0xCACA0014,
25
+ INT16ARRAY = 0xCACA0015,
26
+ INT32ARRAY = 0xCACA0016,
27
+ BIGUINT64ARRAY = 0xCACA0017,
28
+ BIGINT64ARRAY = 0xCACA0018,
29
+ FLOAT32ARRAY = 0xCACA0019,
30
+ FLOAT64ARRAY = 0xCACA001A,
31
+ }
32
+
33
+ export enum BUFFERS {
34
+ POOL_SIZE = 64,
35
+ BUFFER_SIZE = 8192,
36
+ HEADER_SIZE = 24,
37
+ KEY_SIZE = 250,
38
+ KEY_TOO_BIG = 251,
39
+ }
40
+
41
+ export enum OFFSETS {
42
+ MAGIC_$8 = 0,
43
+ OPCODE_$8 = 1,
44
+ KEY_LENGTH_$16 = 2,
45
+ EXTRAS_LENGTH_$8 = 4,
46
+ DATA_TYPE_$8 = 5,
47
+ STATUS_$16 = 6,
48
+ BODY_LENGTH_$32 = 8,
49
+ SEQUENCE_$32 = 12,
50
+ CAS_$64 = 16,
51
+ BODY = 24,
52
+ }
53
+
54
+ export enum MAGIC {
55
+ REQUEST = 0x80, // Request packet for this protocol version
56
+ RESPONSE = 0x81, // Response packet for this protocol version
57
+ }
58
+
59
+ export enum STATUS {
60
+ OK = 0x0000, // No error
61
+ KEY_NOT_FOUND = 0x0001, // Key not found
62
+ KEY_EXISTS = 0x0002, // Key exists
63
+ TOO_LARGE = 0x0003, // Value too large
64
+ INVALID_ARGS = 0x0004, // Invalid arguments
65
+ ITEM_NOT_STORED = 0x0005, // Item not stored
66
+ NON_NUMERIC_VALUE = 0x0006, // Incr/Decr on non-numeric value
67
+ WRONG_VBUCKET = 0x0007, // The vbucket belongs to another server
68
+ AUTH_ERROR = 0x0008, // Authentication error
69
+ AUTH_CONTINUE = 0x0009, // Authentication continue
70
+ UNKNOWN_COMMAND = 0x0081, // Unknown command
71
+ OUT_OF_MEMORY = 0x0082, // Out of memory
72
+ NOT_SUPPORTED = 0x0083, // Not supported
73
+ INTERNAL_ERROR = 0x0084, // Internal error
74
+ BUSY = 0x0085, // Busy
75
+ TEMPORARY_FAILURE = 0x0086, // Temporary failure
76
+ }
77
+
78
+ export enum OPCODE {
79
+ GET = 0x00,
80
+ SET = 0x01,
81
+ ADD = 0x02,
82
+ REPLACE = 0x03,
83
+ DELETE = 0x04,
84
+ INCREMENT = 0x05,
85
+ DECREMENT = 0x06,
86
+ QUIT = 0x07,
87
+ FLUSH = 0x08,
88
+ NOOP = 0x0a,
89
+ VERSION = 0x0b,
90
+ APPEND = 0x0e,
91
+ PREPEND = 0x0f,
92
+ STAT = 0x10,
93
+ TOUCH = 0x1c,
94
+ GAT = 0x1d,
95
+ }
96
+
97
+ export enum DATA_TYPE {
98
+ RAW = 0x00,
99
+ }
100
+
101
+ export enum VBUCKET {
102
+ NIL = 0x00,
103
+ }
package/src/decode.ts ADDED
@@ -0,0 +1,95 @@
1
+ import { DATA_TYPE, EMPTY_BUFFER, MAGIC, OFFSETS } from './constants'
2
+ import assert from 'assert'
3
+ import { allocateBuffer, RecyclableBuffer } from './buffers'
4
+
5
+ // Reading the full header would be like
6
+ // const magic = header.readUInt8 (0)
7
+ // const opcode = header.readUInt8 (1)
8
+ // const key_length = header.readUInt16BE (2)
9
+ // const extras_length = header.readUInt8 (4)
10
+ // const data_type = header.readUInt8 (5)
11
+ // const status = header.readUInt16BE (6)
12
+ // const body_length = header.readUInt32BE (8)
13
+ // const sequence = header.readUInt32BE (12)
14
+ // const cas = header.readBigUInt64BE (16)
15
+
16
+ export interface RawIncomingPacket {
17
+ readonly opcode: number
18
+ readonly status: number
19
+ readonly sequence: number
20
+ readonly cas: bigint
21
+ readonly extras: Buffer
22
+ readonly key: Buffer
23
+ readonly value: Buffer
24
+ recycle: () => void
25
+ }
26
+
27
+ export class Decoder {
28
+ #consumer: (packet: RawIncomingPacket) => void
29
+ #header = Buffer.allocUnsafeSlow(24)
30
+ #body?: RecyclableBuffer
31
+ #pos = 0
32
+
33
+ constructor(consumer: (packet: RawIncomingPacket) => void) {
34
+ this.#consumer = consumer
35
+ }
36
+
37
+ append(buffer: Buffer, start: number, end: number): void {
38
+ const header = this.#header
39
+
40
+ while (start < end) {
41
+ if (this.#pos < 24) {
42
+ const copied = buffer.copy(header, this.#pos, start, end)
43
+ this.#pos += copied
44
+ start += copied
45
+ }
46
+
47
+ if (this.#pos < 24) return
48
+
49
+ let extras = EMPTY_BUFFER
50
+ let key = EMPTY_BUFFER
51
+ let value = EMPTY_BUFFER
52
+ let recycle = () => void 0
53
+
54
+ const bodyLength = header.readUInt32BE(OFFSETS.BODY_LENGTH_$32)
55
+ if (bodyLength) {
56
+ const body = this.#body || (this.#body = allocateBuffer(bodyLength))
57
+
58
+ const copied = buffer.copy(body, this.#pos - 24, start, end)
59
+ this.#pos += copied
60
+ start += copied
61
+
62
+ if (this.#pos - 24 < body.length) return
63
+
64
+ const keyLength = header.readUInt16BE(OFFSETS.KEY_LENGTH_$16)
65
+ const extrasLength = header.readUInt8(OFFSETS.EXTRAS_LENGTH_$8)
66
+ const valueLength = bodyLength - keyLength - extrasLength
67
+
68
+ key = keyLength ? body.subarray(extrasLength, extrasLength + keyLength) : EMPTY_BUFFER
69
+ value = valueLength ? body.subarray(extrasLength + keyLength) : EMPTY_BUFFER
70
+ extras = extrasLength ? body.subarray(0, extrasLength) : EMPTY_BUFFER
71
+ recycle = () => void body.recycle()
72
+ }
73
+
74
+ const packet: RawIncomingPacket = {
75
+ opcode: header.readUInt8(OFFSETS.OPCODE_$8),
76
+ status: header.readUInt16BE(OFFSETS.STATUS_$16),
77
+ sequence: header.readUInt32BE(OFFSETS.SEQUENCE_$32),
78
+ cas: header.readBigUInt64BE(OFFSETS.CAS_$64),
79
+ extras,
80
+ key,
81
+ value,
82
+ recycle,
83
+ }
84
+
85
+ const magic = header.readUInt8(OFFSETS.MAGIC_$8)
86
+ const dataType = header.readUInt8(OFFSETS.DATA_TYPE_$8)
87
+ assert.equal(magic, MAGIC.RESPONSE, 'Invalid magic in header')
88
+ assert.equal(dataType, DATA_TYPE.RAW, 'Invalid data type in header')
89
+
90
+ this.#pos = 0
91
+ this.#body = undefined
92
+ this.#consumer(packet)
93
+ }
94
+ }
95
+ }
package/src/encode.ts ADDED
@@ -0,0 +1,81 @@
1
+ import { allocateBuffer, RecyclableBuffer } from './buffers'
2
+ import { DATA_TYPE, EMPTY_BUFFER, MAGIC, VBUCKET, OPCODE, OFFSETS } from './constants'
3
+
4
+ // Writing the full header would be like
5
+ // header.writeUInt8 (magic, 0)
6
+ // header.writeUInt8 (opcode, 1)
7
+ // header.writeUInt16BE (key_length, 2)
8
+ // header.writeUInt8 (extras_length, 4)
9
+ // header.writeUInt8 (data_type, 5)
10
+ // header.writeUInt16BE (status, 6)
11
+ // header.writeUInt32BE (body_length, 8)
12
+ // header.writeUInt32BE (sequence, 12)
13
+ // header.writeBigUInt64BE (cas, 16)
14
+
15
+ // keep as an "enum", the compiler will replace with values
16
+ enum CONSTANTS {
17
+ HEADER_SIZE = 24, // header size
18
+ }
19
+
20
+ export interface RawOutgoingPacket {
21
+ readonly opcode: OPCODE
22
+ readonly sequence?: number
23
+ readonly cas?: bigint
24
+
25
+ readonly extras?: Buffer
26
+ readonly extrasOffset?: number
27
+ readonly extrasLength?: number
28
+
29
+ readonly key?: Buffer
30
+ readonly keyOffset?: number
31
+ readonly keyLength?: number
32
+
33
+ readonly value?: Buffer
34
+ readonly valueOffset?: number
35
+ readonly valueLength?: number
36
+ }
37
+
38
+ export class Encoder {
39
+ encode(packet: RawOutgoingPacket, seq: number = 0): RecyclableBuffer {
40
+ const {
41
+ opcode,
42
+ sequence = seq,
43
+ cas = 0n,
44
+
45
+ extras = EMPTY_BUFFER,
46
+ extrasOffset = 0,
47
+ extrasLength = extras.length,
48
+
49
+ key = EMPTY_BUFFER,
50
+ keyOffset = 0,
51
+ keyLength = key.length,
52
+
53
+ value = EMPTY_BUFFER,
54
+ valueOffset = 0,
55
+ valueLength = value.length,
56
+ } = packet
57
+
58
+ const bodyLength = extrasLength + keyLength + valueLength
59
+ const length = bodyLength + CONSTANTS.HEADER_SIZE
60
+
61
+ const buffer = allocateBuffer(length) // /* length <= buffer.length ? buffer : */ Buffer.allocUnsafe(length)
62
+
63
+ buffer.writeUInt8(MAGIC.REQUEST, OFFSETS.MAGIC_$8)
64
+ buffer.writeUInt8(opcode, OFFSETS.OPCODE_$8)
65
+ buffer.writeUInt16BE(keyLength, OFFSETS.KEY_LENGTH_$16)
66
+ buffer.writeUInt8(extrasLength, OFFSETS.EXTRAS_LENGTH_$8)
67
+ buffer.writeUInt8(DATA_TYPE.RAW, OFFSETS.DATA_TYPE_$8)
68
+ buffer.writeUInt16BE(VBUCKET.NIL, OFFSETS.STATUS_$16)
69
+ buffer.writeUInt32BE(bodyLength, OFFSETS.BODY_LENGTH_$32)
70
+ buffer.writeUInt32BE(sequence, OFFSETS.SEQUENCE_$32)
71
+ buffer.writeBigUInt64BE(cas, OFFSETS.CAS_$64)
72
+
73
+ let pos = 24
74
+ if (extrasLength) pos += extras.copy(buffer, pos, extrasOffset, extrasOffset + extrasLength)
75
+ if (keyLength) pos += key.copy(buffer, pos, keyOffset, keyOffset + keyLength)
76
+ if (valueLength) pos += value.copy(buffer, pos, valueOffset, valueOffset + valueLength)
77
+ void pos
78
+
79
+ return buffer // .length === length ? buffer : buffer.subarray(0, length)
80
+ }
81
+ }
package/src/index.ts ADDED
@@ -0,0 +1,16 @@
1
+ import * as decode from './decode'
2
+ import * as encode from './encode'
3
+ import * as constants from './constants'
4
+ import * as connection from './connection'
5
+
6
+ export {
7
+ decode,
8
+ encode,
9
+ constants,
10
+ connection,
11
+ }
12
+
13
+ export * from './types'
14
+ export * from './cluster'
15
+ export * from './server'
16
+ export * from './client'
@@ -0,0 +1,41 @@
1
+ // Keep these here: they must be ignored by Istanbul, but ESBUILD swallows
2
+ // commments (for a reason: https://github.com/evanw/esbuild/issues/578)
3
+
4
+ import assert from 'assert'
5
+ import type { Socket } from 'net'
6
+ import {
7
+ isUint8Array,
8
+ isUint8ClampedArray,
9
+ isUint16Array,
10
+ isUint32Array,
11
+ isInt8Array,
12
+ isInt16Array,
13
+ isInt32Array,
14
+ isBigUint64Array,
15
+ isBigInt64Array,
16
+ isFloat32Array,
17
+ isFloat64Array,
18
+ } from 'util/types'
19
+
20
+ import { FLAGS } from './constants'
21
+
22
+ export const socketFinalizationRegistry = new FinalizationRegistry((socket: Socket): void => {
23
+ if (! socket.destroyed) socket.destroy()
24
+ })
25
+
26
+ export function typedArrayFlags(value: NodeJS.TypedArray): FLAGS {
27
+ const flags =
28
+ isUint8Array(value) ? FLAGS.UINT8ARRAY :
29
+ isUint8ClampedArray(value) ? FLAGS.UINT8CLAMPEDARRAY :
30
+ isUint16Array(value) ? FLAGS.UINT16ARRAY :
31
+ isUint32Array(value) ? FLAGS.UINT32ARRAY :
32
+ isInt8Array(value) ? FLAGS.INT8ARRAY :
33
+ isInt16Array(value) ? FLAGS.INT16ARRAY :
34
+ isInt32Array(value) ? FLAGS.INT32ARRAY :
35
+ isBigUint64Array(value) ? FLAGS.BIGUINT64ARRAY :
36
+ isBigInt64Array(value) ? FLAGS.BIGINT64ARRAY :
37
+ isFloat32Array(value) ? FLAGS.FLOAT32ARRAY :
38
+ isFloat64Array(value) ? FLAGS.FLOAT64ARRAY :
39
+ assert.fail('Unsupported kind of TypedArray')
40
+ return flags
41
+ }