Skip to content

Commit

Permalink
Remove osc-min dependency
Browse files Browse the repository at this point in the history
Related to #115

Remove the `osc-min` dependency and implement custom OSC encoding/decoding functions.

* **Remove `osc-min` dependency**
  - Remove `osc-min` from `package.json`.
  - Remove `#decode` alias from `package.json`.

* **Update `Client.mjs`**
  - Import `toBuffer` from `internal/osc.mjs`.
  - Update `send` method to use the new `toBuffer` function.

* **Update `Server.mjs`**
  - Import `fromBuffer` from `internal/osc.mjs`.
  - Update `message` event handler to use the new `fromBuffer` function.

* **Add `internal/osc.mjs`**
  - Implement `toBuffer` function to encode OSC messages.
  - Implement `fromBuffer` function to decode OSC messages.

* **Update `rollup.config.mjs`**
  - Remove `osc-min` and `#decode` from the `external` array in the `walkLib` function.
  - Remove `osc-min` and `#decode` from the `external` array in the `walkTest` function.

* **Add tests for new implementation**
  - Add `test/test-osc.mjs` with tests for `toBuffer` and `fromBuffer` functions.

* **Remove old files**
  - Delete `lib/internal/decode.mjs`.
  - Delete `test/test-decode.mjs`.

---

For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/MylesBorins/node-osc/issues/115?shareId=XXXX-XXXX-XXXX-XXXX).
  • Loading branch information
MylesBorins committed Aug 9, 2024
1 parent f2a0602 commit da56567
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 83 deletions.
4 changes: 1 addition & 3 deletions lib/Client.mjs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { createSocket } from 'node:dgram';
import oscMin from 'osc-min';
import { toBuffer } from '../internal/osc.mjs';
import Message from './Message.mjs';

const { toBuffer } = oscMin;

class Client {
constructor(host, port) {
this.host = host;
Expand Down
4 changes: 2 additions & 2 deletions lib/Server.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createSocket } from 'node:dgram';
import { EventEmitter } from 'node:events';

import decode from '#decode';
import { fromBuffer } from '../internal/osc.mjs';

class Server extends EventEmitter {
constructor(port, host='127.0.0.1', cb) {
Expand All @@ -25,7 +25,7 @@ class Server extends EventEmitter {
});
this._sock.on('message', (msg, rinfo) => {
try {
decoded = decode(msg);
decoded = fromBuffer(msg);
}
catch (e) {
const error = new Error(`can't decode incoming message: ${e.message}`);
Expand Down
33 changes: 0 additions & 33 deletions lib/internal/decode.mjs

This file was deleted.

130 changes: 130 additions & 0 deletions lib/internal/osc.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { Buffer } from 'node:buffer';

function toBuffer(message) {
if (typeof message !== 'object') {
throw new TypeError('Message must be an object');
}

const address = message.address;
const args = message.args || [];

const addressBuffer = encodeString(address);
const typeTagBuffer = encodeString(',' + args.map(arg => getTypeTag(arg)).join(''));
const argsBuffer = Buffer.concat(args.map(arg => encodeArg(arg)));

return Buffer.concat([addressBuffer, typeTagBuffer, argsBuffer]);
}

function fromBuffer(buffer) {
if (!Buffer.isBuffer(buffer)) {
throw new TypeError('Buffer must be a Buffer');
}

let offset = 0;

const address = decodeString(buffer, offset);
offset += address.length + 4 - (address.length % 4);

const typeTag = decodeString(buffer, offset);
offset += typeTag.length + 4 - (typeTag.length % 4);

const args = [];
for (let i = 1; i < typeTag.length; i++) {
const type = typeTag[i];
const arg = decodeArg(buffer, offset, type);
args.push(arg.value);
offset += arg.size;
}

return {
address,
args
};
}

function encodeString(str) {
const length = str.length + 1;
const paddedLength = length + (4 - (length % 4));
const buffer = Buffer.alloc(paddedLength);
buffer.write(str, 0, 'ascii');
return buffer;
}

function decodeString(buffer, offset) {
let end = offset;
while (buffer[end] !== 0) {
end++;
}
return buffer.toString('ascii', offset, end);
}

function getTypeTag(arg) {
switch (typeof arg) {
case 'string':
return 's';
case 'number':
return Number.isInteger(arg) ? 'i' : 'f';
case 'object':
if (Buffer.isBuffer(arg)) {
return 'b';
}
throw new TypeError('Unsupported argument type');
default:
throw new TypeError('Unsupported argument type');
}
}

function encodeArg(arg) {
switch (typeof arg) {
case 'string':
return encodeString(arg);
case 'number':
return Number.isInteger(arg) ? encodeInt32(arg) : encodeFloat32(arg);
case 'object':
if (Buffer.isBuffer(arg)) {
return encodeBlob(arg);
}
throw new TypeError('Unsupported argument type');
default:
throw new TypeError('Unsupported argument type');
}
}

function decodeArg(buffer, offset, type) {
switch (type) {
case 's':
return { value: decodeString(buffer, offset), size: 4 * Math.ceil((buffer.indexOf(0, offset) - offset + 1) / 4) };
case 'i':
return { value: buffer.readInt32BE(offset), size: 4 };
case 'f':
return { value: buffer.readFloatBE(offset), size: 4 };
case 'b':
const length = buffer.readInt32BE(offset);

Check failure on line 102 in lib/internal/osc.mjs

View workflow job for this annotation

GitHub Actions / run-tests (22, ubuntu-latest)

Unexpected lexical declaration in case block

Check failure on line 102 in lib/internal/osc.mjs

View workflow job for this annotation

GitHub Actions / run-tests (20, ubuntu-latest)

Unexpected lexical declaration in case block

Check failure on line 102 in lib/internal/osc.mjs

View workflow job for this annotation

GitHub Actions / run-tests (20, macos-latest)

Unexpected lexical declaration in case block

Check failure on line 102 in lib/internal/osc.mjs

View workflow job for this annotation

GitHub Actions / run-tests (18, ubuntu-latest)

Unexpected lexical declaration in case block

Check failure on line 102 in lib/internal/osc.mjs

View workflow job for this annotation

GitHub Actions / run-tests (18, macos-latest)

Unexpected lexical declaration in case block
return { value: buffer.slice(offset + 4, offset + 4 + length), size: 4 + length + (4 - (length % 4)) };
default:
throw new TypeError('Unsupported argument type');
}
}

function encodeInt32(value) {
const buffer = Buffer.alloc(4);
buffer.writeInt32BE(value, 0);
return buffer;
}

function encodeFloat32(value) {
const buffer = Buffer.alloc(4);
buffer.writeFloatBE(value, 0);
return buffer;
}

function encodeBlob(blob) {
const length = blob.length;
const paddedLength = length + (4 - (length % 4));
const buffer = Buffer.alloc(4 + paddedLength);
buffer.writeInt32BE(length, 0);
blob.copy(buffer, 4);
return buffer;
}

export { toBuffer, fromBuffer };
7 changes: 0 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@
"default": "./lib/index.mjs"
},
"imports": {
"#decode": {
"require": "./dist/lib/internal/decode.js",
"default": "./lib/internal/decode.mjs"
}
},
"author": {
"name": "Myles Borins",
Expand Down Expand Up @@ -42,9 +38,6 @@
"type": "git",
"url": "git+https://github.com/MylesBorins/node-osc.git"
},
"dependencies": {
"osc-min": "^1.1.1"
},
"devDependencies": {
"@eslint/js": "^9.4.0",
"eslint": "^9.4.0",
Expand Down
9 changes: 2 additions & 7 deletions rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,7 @@ function walkLib(config) {
},
external: [
'node:dgram',
'node:events',
'osc-min',
'jspack',
'#decode'
'node:events'
]
});
});
Expand All @@ -59,9 +56,7 @@ function walkTest(config) {
'node:dgram',
'node:net',
'node-osc',
'osc-min',
'tap',
'#decode'
'tap'
]
});
});
Expand Down
31 changes: 0 additions & 31 deletions test/test-decode.mjs

This file was deleted.

22 changes: 22 additions & 0 deletions test/test-osc.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { test } from 'tap';
import { toBuffer, fromBuffer } from '../lib/internal/osc.mjs';

test('toBuffer: valid message', (t) => {
const message = {
address: '/test',
args: ['testing', 123, 3.14, Buffer.from('blob')]
};
const buffer = toBuffer(message);
t.type(buffer, Buffer, 'should return a Buffer');
t.end();
});

test('toBuffer: invalid message', (t) => {
t.throws(() => {
toBuffer('invalid');
}, /Message must be an object/);
t.end();
});

test('fromBuffer: valid buffer', (t) => {
const buffer = Buffer.from('/test\0\0\0,sifb\0\0\0\0testing\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0

0 comments on commit da56567

Please sign in to comment.