Implement smart buffer

This commit is contained in:
Jack Hadrill
2022-02-02 01:58:55 +00:00
parent a93bfc182f
commit 7d1a0991e4
12 changed files with 1717 additions and 152 deletions

View File

@@ -1,17 +1,23 @@
import { IMessageEvent } from 'websocket'
import { IncomingPacket } from './structures/server'
// import { SubscribeMessage } from './structures/subscribe'
export function connect (url: string): void {
const webSocket = new WebSocket(url)
webSocket.binaryType = 'arraybuffer'
webSocket.onopen = onOpen
webSocket.onmessage = onMessage
}
function onOpen (): void {
console.log('Connected!')
webSocket.onopen = () => {
console.log('Connected!')
// const subscribeMessage = new SubscribeMessage(0x0001)
const tmp = Uint8Array.from([0x00, 0x00, 0x00, 0x02, 0x00, 0x01])
console.log(tmp)
webSocket.send(tmp)
}
}
function onMessage (event: IMessageEvent): void {
console.log(new IncomingPacket(event.data as Buffer))
console.log('New message.')
console.log(event.data)
console.log(new IncomingPacket(event.data as ArrayBuffer))
}

View File

@@ -1,37 +0,0 @@
import { ByteLength } from './common'
export class BufferReader {
private readonly _data: Buffer
private _cursor: number
constructor (data: Buffer = Buffer.from([])) {
this._data = data
this._cursor = 0
}
get cursor (): number {
return this._cursor
}
set cursor (position: number) {
if (position < 0 || position > this._data.length) {
throw RangeError(`Cannot seek to ${this.cursor} of ${this._data.length} bytes.`)
}
this._cursor = position
}
readUInt16 (): number {
this.cursor += ByteLength.UInt16
return this._data.slice(this.cursor - ByteLength.UInt16, this.cursor).readInt16BE()
}
readUInt32 (): number {
this.cursor += ByteLength.UInt32
return this._data.slice(this.cursor - ByteLength.UInt32, this.cursor).readInt32BE()
}
readBuffer (len: number): Buffer {
this.cursor += len
return this._data.slice(this.cursor - len, this.cursor)
}
}

View File

@@ -1,3 +1,5 @@
export const MAX_DATA_LENGTH = 1000
export enum ByteLength {
UInt16 = 2,
UInt32 = 4

View File

@@ -1 +1,3 @@
export { connect } from './bennc'
import { connect } from './bennc'
connect('wss://chat.3t.network/BENNC')

158
src/smart-buffer.ts Normal file
View File

@@ -0,0 +1,158 @@
import { ByteLength } from './common'
export class SmartBuffer {
private _data: number[]
private _cursor: number
/**
* Wrap a buffer to track position and provide useful read / write functionality.
* @param data Buffer to wrap (optional).
*/
constructor (data?: Uint8Array) {
this._data = data === undefined ? [] : [...data]
this._cursor = 0
}
/**
* Return a regular buffer.
*/
get data (): Uint8Array {
return Uint8Array.from(this._data)
}
/**
* Update the smart buffer to wrap new data.
*/
set data (data: Uint8Array) {
this._data = [...data]
this.cursor = 0
}
/**
* Get the length of the smart buffer data.
*/
get length (): number {
return this._data.length
}
/**
* Set the length of the smart buffer data.
*/
set length (length: number) {
this._data.length = length
}
/**
* Get the current cursor position of the smart buffer.
*/
get cursor (): number {
return this._cursor
}
/**
* Set the cursor position of the smart buffer.
*/
set cursor (position: number) {
if (position < 0) {
throw RangeError(`Cannot seek to ${this.cursor} of ${this.length} bytes.`)
}
this._cursor = position
}
/**
* Pads bytes to the end of the smart buffer.
* @param length The number of bytes to pad.
*/
pad (length: number): void {
this._data.push(...new Uint8Array(length))
}
/**
* Return the data from the specified range.
* @param start The start position.
* @param end The end position.
* @returns A new buffer containing data from the specified range.
*/
slice (start: number, end: number): Uint8Array {
return Uint8Array.from(this._data.slice(start, end))
}
/**
* Splice bytes into the smart buffer's data.
* @param start The position at which to insert the new data.
* @param deleteCount The number of items to remove before inserting new data.
* @param items The items to insert at the specified position.
*/
splice (start: number, deleteCount: number, ...items: number[]): void {
if (this.length < start) {
this.pad(start)
}
this._data.splice(this.cursor, ByteLength.UInt16, ...items)
}
/**
* Read a UInt16 number from the smart buffer at the current cursor position, and increment the cursor.
* @returns A number represented by the bytes at the current cursor position.
*/
readUInt16 (): number {
this.cursor += ByteLength.UInt16
const bytes = this.slice(this.cursor - ByteLength.UInt16, this.cursor)
return (bytes[0] << 8) | bytes[1]
}
/**
* Read a UInt32 number from the smart buffer at the current cursor position, and increment the cursor.
* @returns A number represented by the bytes at the current cursor position.
*/
readUInt32 (): number {
this.cursor += ByteLength.UInt32
const bytes = this.slice(this.cursor - ByteLength.UInt32, this.cursor)
return (((bytes[0] << 24) | (bytes[1] << 16)) | (bytes[2] << 8) | bytes[3])
}
/**
* Read the specified number of bytes from the smart buffer at the current cursor position, and increment the cursor.
* @returns A buffer containing the bytes read from the current cursor position.
*/
readBytes (length: number): Uint8Array {
this.cursor += length
return this.slice(this.cursor - length, this.cursor)
}
/**
* Write a UInt16 number to the smart buffer at the current cursor position, and increment the cursor.
* @param number The number to write in UInt16 format at the current cursor position.
*/
writeUInt16 (number: number): void {
const bytes = [
(number & 0xff00) >> 8,
(number & 0x00ff)
]
this.splice(this.cursor, ByteLength.UInt16, ...bytes)
this.cursor += ByteLength.UInt16
}
/**
* Write a UInt32 number to the smart buffer at the current cursor position, and increment the cursor.
* @param number The number to write in UInt32 format at the current cursor position.
*/
writeUInt32 (number: number): void {
const bytes = [
(number & 0xff000000) >> 24,
(number & 0x00ff0000) >> 16,
(number & 0x0000ff00) >> 8,
(number & 0x000000ff)
]
this.splice(this.cursor, ByteLength.UInt32, ...bytes)
this.cursor += ByteLength.UInt32
}
/**
* Write the specified bytes to the smart buffer at the current cursor position, and increment the cursor.
* @param buffer The bytes to write at the current cursor position.
*/
writeBytes (buffer: Uint8Array): void {
this.splice(this.cursor, buffer.length, ...buffer)
this.cursor += buffer.length
}
}

View File

@@ -1,16 +1,35 @@
import { BufferReader } from '../buffer-reader'
import { MAX_DATA_LENGTH } from '../common'
import { SmartBuffer } from '../smart-buffer'
export class IncomingPacket extends BufferReader {
messageType: number
senderId: number
length: number
data: Buffer
export class IncomingPacket extends SmartBuffer {
fields: {
messageType: number
senderId: number
length: number
data: Uint8Array
}
constructor (data: Buffer) {
constructor (data: ArrayBuffer) {
super()
this.messageType = this.readUInt16()
this.senderId = this.readUInt32()
this.length = this.readUInt16()
this.data = this.readBuffer(this.length)
this.data = new Uint8Array(data)
this.fields = {
messageType: this.readUInt16(),
senderId: this.readUInt32(),
length: this.readUInt16(),
data: this.readBytes(this.length)
}
}
}
export class OutgoingPacket extends SmartBuffer {
constructor (messageType: number, data: Uint8Array = new Uint8Array(0)) {
super()
if (data.length > MAX_DATA_LENGTH) {
throw RangeError(`Specified data of length ${data.length} exceeds max data length ${MAX_DATA_LENGTH}.`)
}
this.writeUInt16(messageType)
this.writeUInt16(data.length)
this.writeBytes(data)
}
}

View File

@@ -0,0 +1,22 @@
import { SmartBuffer } from '../smart-buffer'
import { OutgoingPacket } from './server'
export class SubscribeMessage extends SmartBuffer {
static readonly messageType = 0x0000
constructor (id: number) {
super()
const outgoingPacket = new OutgoingPacket(SubscribeMessage.messageType)
this.data = outgoingPacket.data
this.writeUInt16(id)
}
}
export class Unsubscribe extends OutgoingPacket {
static readonly messageType = 0xffff
constructor (id: number) {
super(Unsubscribe.messageType)
this.writeUInt16(id)
}
}