Compare commits
6 Commits
Author | SHA1 | Date |
---|---|---|
Tristan B. Velloza Kildaire | 6ed46d051c | |
Tristan B. Velloza Kildaire | 6067155fca | |
Tristan B. Velloza Kildaire | 6994518821 | |
Tristan B. Velloza Kildaire | 6eb9445042 | |
Tristan B. Velloza Kildaire | cef8adf2a9 | |
Tristan B. Velloza Kildaire | 280fb551f2 |
|
@ -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}}
|
|
@ -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:
|
||||
|
||||
|
|
3
dub.json
3
dub.json
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue