Skip to content

mysticatea/bre

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

43 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

bre

npm version Downloads/month Build Status codecov Dependency Status

A Object-Binary Mapper for JavaScript/TypeScript.

This is similar to C struct with a memory block.

import { defineObjectRecord } from "bre"

// Define a Record Type.
const TcpHeader = defineObjectRecord("TcpHeader", {
    srcPort: "uint16",
    dstPort: "uint16",
    seqNo: "uint32",
    ackNo: "uint32",
    dataOffset: "bit4",
    _reserved0: "bit6", // padding
    urgCode: "bit1",
    ackCode: "bit1",
    pshCode: "bit1",
    rstCode: "bit1",
    synCode: "bit1",
    finCode: "bit1",
    windowSize: "uint16",
    checksum: "uint16",
    urgentPonter: "uint16",
})

// Connect to a Buffer (E.g., a buffer came from stream...)
const header = TcpHeader.view(a_buffer, 0)

// Read and Write the Buffer
console.log(header.srcPort, "โ†’", header.dstPort, "๐Ÿš€")
header.seqNo += 1
header.ackNo += 1
header.ackCode = 1

bre's record object is just a wrapper of ArrayBuffer objects.

  • The read of properties reads the connected buffer directly.
  • The write of properties writes the connected buffer directly.

So the above TcpHeader example defines the class like the following automatically:

Open the defined class

class TcpHeader extends ObjectRecord {
    constructor(buffer, byteOffset) {
        super(buffer, byteOffset, 20)
    }

    static view(buffer, byteOffset = 0) {
        return Object.freeze(new TcpHeader(buffer, byteOffset))
    }

    static get bitLength() {
        return 160
    }

    static get byteLength() {
        return 20
    }

    static keys(record) {
        return [
            "srcPort",
            "dstPort",
            "seqNo",
            "ackNo",
            "dataOffset",
            "urgCode",
            "ackCode",
            "pshCode",
            "rstCode",
            "synCode",
            "finCode",
            "windowSize",
            "checksum",
            "urgentPonter",
        ]
    }

    static values(record) {
        return [
            record.srcPort,
            record.dstPort,
            record.seqNo,
            record.ackNo,
            record.dataOffset,
            record.urgCode,
            record.ackCode,
            record.pshCode,
            record.rstCode,
            record.synCode,
            record.finCode,
            record.windowSize,
            record.checksum,
            record.urgentPonter,
        ]
    }

    static entries(record) {
        return [
            ["srcPort", record.srcPort],
            ["dstPort", record.dstPort],
            ["seqNo", record.seqNo],
            ["ackNo", record.ackNo],
            ["dataOffset", record.dataOffset],
            ["urgCode", record.urgCode],
            ["ackCode", record.ackCode],
            ["pshCode", record.pshCode],
            ["rstCode", record.rstCode],
            ["synCode", record.synCode],
            ["finCode", record.finCode],
            ["windowSize", record.windowSize],
            ["checksum", record.checksum],
            ["urgentPonter", record.urgentPonter],
        ]
    }

    get srcPort() {
        return this[sBuffer].getUint16(0)
    }
    set srcPort(value) {
        assert.integer(value, "srcPort")
        assert.range(value, 0, 65535, "srcPort")
        this[sBuffer].setUint16(0, value)
    }
    get dstPort() {
        return this[sBuffer].getUint16(2)
    }
    set dstPort(value) {
        assert.integer(value, "dstPort")
        assert.range(value, 0, 65535, "dstPort")
        this[sBuffer].setUint16(2, value)
    }
    get seqNo() {
        return this[sBuffer].getUint32(4)
    }
    set seqNo(value) {
        assert.integer(value, "seqNo")
        assert.range(value, 0, 4294967295, "seqNo")
        this[sBuffer].setUint32(4, value)
    }
    get ackNo() {
        return this[sBuffer].getUint32(8)
    }
    set ackNo(value) {
        assert.integer(value, "ackNo")
        assert.range(value, 0, 4294967295, "ackNo")
        this[sBuffer].setUint32(8, value)
    }
    get dataOffset() {
        const data = this[sBuffer].getUint8(12)
        return (data & 240) >> 4
    }
    set dataOffset(value) {
        assert.integer(value, "dataOffset")
        assert.range(value, 0, 15, "dataOffset")

        const data = this[sBuffer].getUint8(12)
        this[sBuffer].setUint8(12, ((value << 4) & 240) | (data & ~240))
    }
    get urgCode() {
        const data = this[sBuffer].getUint8(13)
        return (data & 32) >> 5
    }
    set urgCode(value) {
        assert.integer(value, "urgCode")
        assert.range(value, 0, 1, "urgCode")

        const data = this[sBuffer].getUint8(13)
        this[sBuffer].setUint8(13, ((value << 5) & 32) | (data & ~32))
    }
    get ackCode() {
        const data = this[sBuffer].getUint8(13)
        return (data & 16) >> 4
    }
    set ackCode(value) {
        assert.integer(value, "ackCode")
        assert.range(value, 0, 1, "ackCode")

        const data = this[sBuffer].getUint8(13)
        this[sBuffer].setUint8(13, ((value << 4) & 16) | (data & ~16))
    }
    get pshCode() {
        const data = this[sBuffer].getUint8(13)
        return (data & 8) >> 3
    }
    set pshCode(value) {
        assert.integer(value, "pshCode")
        assert.range(value, 0, 1, "pshCode")

        const data = this[sBuffer].getUint8(13)
        this[sBuffer].setUint8(13, ((value << 3) & 8) | (data & ~8))
    }
    get rstCode() {
        const data = this[sBuffer].getUint8(13)
        return (data & 4) >> 2
    }
    set rstCode(value) {
        assert.integer(value, "rstCode")
        assert.range(value, 0, 1, "rstCode")

        const data = this[sBuffer].getUint8(13)
        this[sBuffer].setUint8(13, ((value << 2) & 4) | (data & ~4))
    }
    get synCode() {
        const data = this[sBuffer].getUint8(13)
        return (data & 2) >> 1
    }
    set synCode(value) {
        assert.integer(value, "synCode")
        assert.range(value, 0, 1, "synCode")

        const data = this[sBuffer].getUint8(13)
        this[sBuffer].setUint8(13, ((value << 1) & 2) | (data & ~2))
    }
    get finCode() {
        const data = this[sBuffer].getUint8(13)
        return (data & 1) >> 0
    }
    set finCode(value) {
        assert.integer(value, "finCode")
        assert.range(value, 0, 1, "finCode")

        const data = this[sBuffer].getUint8(13)
        this[sBuffer].setUint8(13, ((value << 0) & 1) | (data & ~1))
    }
    get windowSize() {
        return this[sBuffer].getUint16(14)
    }
    set windowSize(value) {
        assert.integer(value, "windowSize")
        assert.range(value, 0, 65535, "windowSize")
        this[sBuffer].setUint16(14, value)
    }
    get checksum() {
        return this[sBuffer].getUint16(16)
    }
    set checksum(value) {
        assert.integer(value, "checksum")
        assert.range(value, 0, 65535, "checksum")
        this[sBuffer].setUint16(16, value)
    }
    get urgentPonter() {
        return this[sBuffer].getUint16(18)
    }
    set urgentPonter(value) {
        assert.integer(value, "urgentPonter")
        assert.range(value, 0, 65535, "urgentPonter")
        this[sBuffer].setUint16(18, value)
    }
}

Plus, this package provides the perfect type definition for the classes that defineObjectRecord/defineArrayRecord defined.

๐Ÿ’ฟ Installation

$ npm install --save bre
  • Requires Node.js >=6.0.0

๐Ÿ“– Usage

TBD.

๐Ÿ“ฐ Changelog

โค๏ธ Contributing

Thank you for contributing!

Use Issues/PRs on GitHub.

Development tools

  • npm test ... Runs all tests.
  • npm run build ... Compiles JavaScript code from TypeScript source code.
  • npm run clean ... Removes compiled files, temporary files, and coverage data.
  • npm run coverage ... Opens the code coverage of the last npm test.
  • npm run lint ... Applies ESLint to the source code.
  • npm run watch ... Runs all tests on every file changes.