Compare commits

...

6 Commits

Author SHA1 Message Date
Tristan B. Velloza Kildaire 6ed46d051c
Switch to niknaks (#4)
* Dub

- Added `niknaks` dependency

* Marshall

- `encodeBformat(byte[])` now uses niknaks for binary operations

* Dub

- Upgraded `niknaks` dependency to version `0.3.0`

* Client

- Removed bit-twiddling, use `niiknaks.bits`

* Marshall

- `encodeBformat(byte[])` now uses niknaks
2023-10-01 19:02:42 +02:00
Tristan B. Velloza Kildaire 6067155fca REAMDE
- Removed duplicate badge
2023-10-01 17:56:18 +02:00
Tristan B. Velloza Kildaire 6994518821 README
- Added more badges
2023-10-01 17:55:06 +02:00
Tristan B. Velloza Kildaire 6eb9445042 Pipelines
- Added code coverage support

README

- Added coveralls badge
2023-10-01 17:53:19 +02:00
Tristan B. Velloza Kildaire cef8adf2a9 README
- Fixed typos
2023-10-01 17:50:28 +02:00
Tristan B. Velloza Kildaire 280fb551f2 - Fixed typo in README 2023-06-18 14:18:50 +02:00
5 changed files with 27 additions and 68 deletions

View File

@ -6,9 +6,9 @@ name: D
on:
push:
branches: [ "master" ]
branches: [ "**" ]
pull_request:
branches: [ "master" ]
branches: [ "**" ]
permissions:
contents: read
@ -22,6 +22,11 @@ jobs:
- uses: actions/checkout@v3
- uses: dlang-community/setup-dlang@4c99aa991ce7d19dd3064de0a4f2f6b2f152e2d7
- name: Install Doveralls (code coverage tool)
run: |
dub fetch doveralls
sudo apt install libcurl4-openssl-dev
- name: 'Build & Test'
run: |
# Build the project, with its main file included, without unittests
@ -29,4 +34,8 @@ jobs:
# Build and run tests, as defined by `unittest` configuration
# In this mode, `mainSourceFile` is excluded and `version (unittest)` are included
# See https://dub.pm/package-format-json.html#configurations
dub test --compiler=$DC
dub test --compiler=$DC --coverage
- name: Coverage upload
run: |
dub run doveralls -- -t ${{secrets.COVERALLS_REPO_TOKEN}}

View File

@ -1,17 +1,18 @@
bformat
=======
[![D](https://github.com/besterprotocol/bformat/actions/workflows/d.yml/badge.svg)](https://github.com/besterprotocol/bformat/actions/workflows/d.yml)
[![D](https://github.com/besterprotocol/bformat/actions/workflows/d.yml/badge.svg)](https://github.com/besterprotocol/bformat/actions/workflows/d.yml) ![DUB](https://img.shields.io/dub/v/bformat?color=%23c10000ff%20&style=flat-square) ![DUB](https://img.shields.io/dub/dt/bformat?style=flat-square) ![DUB](https://img.shields.io/dub/l/bformat?style=flat-square) [![Coverage Status](https://coveralls.io/repos/github/besterprotocol/bformat/badge.svg?branch=master)](https://coveralls.io/github/besterprotocol/bformat?branch=master)
A simple message format for automatically length-prefixing messages over any [`Socket`](https://dlang.org/phobos/std_socket.html#.Socket) or [River-based](https://github.com/deavmi/river) [`Stream`](https://river.dpldocs.info/river.core.stream.Stream.html).
## What is bformat?
bformat makes it easy to build applications whereby you want to send data over a streaming interface (either a `Socket` opened in `SocketType.STREAM` mode or a River-based `Stream`) and want to be able to read the data as length-prefixed messages, without the hassle of implementing this yourself. This is whwre bformat shines by providing support for this in a cross-platform manner so you do not have to worry aboutimplementing it yourself countless times again everytime you require such functionality in a project.
bformat makes it easy to build applications whereby you want to send data over a streaming interface (either a `Socket` opened in `SocketType.STREAM` mode or a River-based `Stream`) and want to be able to read the data as length-prefixed messages, without the hassle of implementing this yourself. This is where bformat shines by providing support for this in a cross-platform manner so you do not have to worry about implementing it yourself countless times again every time you require such functionality in a project.
## Usage
You can see the [API](https://bformat.dpldocs.info/index.html) for information on how to use it but it boils down to spawning a new [`BClient`](https://bformat.dpldocs.info/bformat.client.BClient.html) which takes in either a `Socket` of `Stream` (see [River](https://river.dpldocs.info/river.html)) and then you can either send data using [`sendMessage(byte[])`](https://bformat.dpldocs.info/bformat.client.BClient.sendMessage.html) and receive using [`receiveMessage(ref byte[])`](https://bformat.dpldocs.info/bformat.client.BClient.receiveMessage.html).
You can see the [API](https://bformat.dpldocs.info/index.html) for information on how to use it but it boils down to spawning a new [`BClient`](https://bformat.dpldocs.info/bformat.client.BClient.html) which takes in either a `Socket` or `Stream` (see [River](https://river.dpldocs.info/river.html)) and then you can either send data using [`sendMessage(byte[])`](https://bformat.dpldocs.info/bformat.client.BClient.sendMessage.html) and receive using [`receiveMessage(ref byte[])`](https://bformat.dpldocs.info/bformat.client.BClient.receiveMessage.html).
Below we have an example application which does just this:

View File

@ -4,6 +4,7 @@
],
"copyright": "Copyright © 2023, Tristan B. Kildaire",
"dependencies": {
"niknaks": ">=0.3.0",
"river": ">=0.3.6"
},
"description": "A simple message format for automatically length-prefixing messages over any socket or stream",
@ -11,4 +12,4 @@
"license": "LGPL v3",
"name": "bformat",
"targetType": "library"
}
}

View File

@ -6,6 +6,7 @@ module bformat.client;
import std.socket : Socket;
import river.core;
import river.impls.sock : SockStream;
import niknaks.bits : bytesToIntegral, order, Order;
/**
* Bformat client to encode and decode via a
@ -76,27 +77,9 @@ public class BClient
/* Response message length */
uint messageLength;
/* Little endian version you simply read if off the bone (it's already in the correct order) */
version(LittleEndian)
{
messageLength = *cast(int*)messageLengthBytes.ptr;
}
/* Big endian requires we byte-sapped the little-endian encoded number */
version(BigEndian)
{
byte[] swappedLength;
swappedLength.length = 4;
swappedLength[0] = messageLengthBytes[3];
swappedLength[1] = messageLengthBytes[2];
swappedLength[2] = messageLengthBytes[1];
swappedLength[3] = messageLengthBytes[0];
messageLength = *cast(int*)swappedLength.ptr;
}
/* Order the bytes into Little endian (only flips if host order doesn't match LE) */
messageLength = order(bytesToIntegral!(uint)(cast(ubyte[])messageLengthBytes), Order.LE);
/* Read the full message */
receiveBuffer.length = messageLength;
try

View File

@ -3,6 +3,8 @@
*/
module bformat.marshall;
import niknaks.bits : toBytes, bytesToIntegral, order, Order;
/**
* Decodes the provided bformat message into the
* message itself
@ -22,25 +24,8 @@ public byte[] decodeMessage(byte[] bformatBytes)
/* Response message length */
uint messageLength;
/* Little endian version you simply read if off the bone (it's already in the correct order) */
version(LittleEndian)
{
messageLength = *cast(int*)messageLengthBytes.ptr;
}
/* Big endian requires we byte-sapped the little-endian encoded number */
version(BigEndian)
{
byte[] swappedLength;
swappedLength.length = 4;
swappedLength[0] = messageLengthBytes[3];
swappedLength[1] = messageLengthBytes[2];
swappedLength[2] = messageLengthBytes[1];
swappedLength[3] = messageLengthBytes[0];
messageLength = *cast(int*)swappedLength.ptr;
}
/* Order the bytes into Little endian (only flips if host order doesn't match LE) */
messageLength = order(bytesToIntegral!(uint)(cast(ubyte[])messageLengthBytes), Order.LE);
/* Read the full message */
@ -62,27 +47,7 @@ public byte[] encodeBformat(byte[] message)
byte[] messageBuffer;
/* Encode the 4 byte message length header (little endian) */
int payloadLength = cast(int)message.length;
byte* lengthBytes = cast(byte*)&payloadLength;
/* On little endian simply get the bytes as is (it would be encoded as little endian) */
version(LittleEndian)
{
messageBuffer ~= *(lengthBytes+0);
messageBuffer ~= *(lengthBytes+1);
messageBuffer ~= *(lengthBytes+2);
messageBuffer ~= *(lengthBytes+3);
}
/* On Big Endian you must swap the big-endian-encoded number to be in little endian ordering */
version(BigEndian)
{
messageBuffer ~= *(lengthBytes+3);
messageBuffer ~= *(lengthBytes+2);
messageBuffer ~= *(lengthBytes+1);
messageBuffer ~= *(lengthBytes+0);
}
messageBuffer ~= cast(byte[])toBytes(order(cast(int)message.length, Order.LE));
/* Add the message to the buffer */
messageBuffer ~= cast(byte[])message;