remove dependency on num

This commit is contained in:
Vinzenz Schroeter 2024-05-12 13:11:42 +02:00
parent 3b9586a69e
commit 62ca9037b6
11 changed files with 124 additions and 314 deletions

128
Cargo.lock generated
View file

@ -8,12 +8,6 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "autocfg"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]] [[package]]
name = "bzip2" name = "bzip2"
version = "0.4.4" version = "0.4.4"
@ -121,90 +115,6 @@ dependencies = [
"adler", "adler",
] ]
[[package]]
name = "num"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
dependencies = [
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
"num-rational",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "num-complex"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
dependencies = [
"num-traits",
]
[[package]]
name = "num-derive"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "num-integer"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
dependencies = [
"num-bigint",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.19.0" version = "1.19.0"
@ -217,24 +127,6 @@ version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
[[package]]
name = "proc-macro2"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
[[package]] [[package]]
name = "servicepoint2" name = "servicepoint2"
version = "0.1.3" version = "0.1.3"
@ -243,29 +135,9 @@ dependencies = [
"flate2", "flate2",
"log", "log",
"lz4", "lz4",
"num",
"num-derive",
"num-traits",
"zstd", "zstd",
] ]
[[package]]
name = "syn"
version = "2.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]] [[package]]
name = "zstd" name = "zstd"
version = "0.13.1" version = "0.13.1"

View file

@ -5,14 +5,11 @@ publish = true
edition = "2021" edition = "2021"
license = "GPL-3.0-or-later" license = "GPL-3.0-or-later"
description = "A rust library for the CCCB Service Point Display." description = "A rust library for the CCCB Service Point Display."
homepage = "https://github.com/kaesaecracker/servicepoint" homepage = "https://docs.rs/crate/servicepoint2"
repository = "https://github.com/kaesaecracker/servicepoint" repository = "https://github.com/kaesaecracker/servicepoint"
readme = "README.md" readme = "README.md"
[dependencies] [dependencies]
num = "0.4"
num-derive = "0.4"
num-traits = "0.2"
log = "0.4" log = "0.4"
flate2 = { version = "1.0", optional = true } flate2 = { version = "1.0", optional = true }
bzip2 = { version = "0.4", optional = true } bzip2 = { version = "0.4", optional = true }
@ -21,7 +18,8 @@ zstd = { version = "0.13", optional = true }
[features] [features]
default = ["compression-gz", "compression-bz", "compression-lz", "compression-zs"] default = ["compression-gz", "compression-bz", "compression-lz", "compression-zs"]
compression-gz = ["dep:flate2"] compression-gz = ["dep:flate2", "compression"]
compression-bz = ["dep:bzip2"] compression-bz = ["dep:bzip2", "compression"]
compression-lz = ["dep:lz4"] compression-lz = ["dep:lz4", "compression"]
compression-zs = ["dep:zstd"] compression-zs = ["dep:zstd", "compression"]
compression = []

View file

@ -1,7 +1,7 @@
# servicepoint # servicepoint
[![crates.io](https://img.shields.io/crates/v/servicepoint2.svg)](https://crates.io/crates/servicepoint2) [![crates.io](https://img.shields.io/crates/v/servicepoint2.svg)](https://crates.io/crates/servicepoint2)
![Crates.io Total Downloads](https://img.shields.io/crates/d/servicepoint2) [![Crates.io Total Downloads](https://img.shields.io/crates/d/servicepoint2)](https://crates.io/crates/servicepoint2)
[![docs.rs](https://img.shields.io/docsrs/servicepoint2)](https://docs.rs/servicepoint2/latest/servicepoint2/) [![docs.rs](https://img.shields.io/docsrs/servicepoint2)](https://docs.rs/servicepoint2/latest/servicepoint2/)
[![GPLv3 licensed](https://img.shields.io/crates/l/servicepoint2)](./LICENSE) [![GPLv3 licensed](https://img.shields.io/crates/l/servicepoint2)](./LICENSE)
@ -9,6 +9,10 @@ In [CCCB](https://berlin.ccc.de/), there is a big pixel matrix hanging on the wa
Display" or "Airport Display". Display" or "Airport Display".
This repository contains a library for parsing, encoding and sending packets to this display via UDP. This repository contains a library for parsing, encoding and sending packets to this display via UDP.
This library is still in early development.
You can absolutely use it and it works, but expect minor breaking changes with every version bump.
Please specify the full version including patch in your Cargo.toml until 1.0 is released.
### Installation ### Installation
```bash ```bash

93
examples/Cargo.lock generated
View file

@ -76,12 +76,6 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "autocfg"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]] [[package]]
name = "bzip2" name = "bzip2"
version = "0.4.4" version = "0.4.4"
@ -314,90 +308,6 @@ dependencies = [
"servicepoint2", "servicepoint2",
] ]
[[package]]
name = "num"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
dependencies = [
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
"num-rational",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "num-complex"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
dependencies = [
"num-traits",
]
[[package]]
name = "num-derive"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "num-integer"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
dependencies = [
"num-bigint",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.19.0" version = "1.19.0"
@ -512,9 +422,6 @@ dependencies = [
"flate2", "flate2",
"log", "log",
"lz4", "lz4",
"num",
"num-derive",
"num-traits",
"zstd", "zstd",
] ]

View file

@ -1,5 +1,4 @@
use crate::{BitVec, ByteGrid, Header, Packet, PixelGrid, TILE_SIZE}; use crate::{BitVec, ByteGrid, CommandCode, CompressionCode, Header, Packet, PixelGrid, TILE_SIZE};
use crate::command_codes::{CommandCode, CompressionCode};
use crate::compression::{into_compressed, into_decompressed}; use crate::compression::{into_compressed, into_decompressed};
/// An origin marks the top left position of a window sent to the display. /// An origin marks the top left position of a window sent to the display.
@ -20,6 +19,7 @@ type Offset = u16;
type Brightness = u8; type Brightness = u8;
/// A command to send to the display.
#[derive(Debug)] #[derive(Debug)]
pub enum Command { pub enum Command {
/// Set all pixels to the off state /// Set all pixels to the off state
@ -69,7 +69,7 @@ impl Into<Packet> for Command {
), ),
Command::Brightness(brightness) => Packet( Command::Brightness(brightness) => Packet(
Header( Header(
CommandCode::Brightness.to_primitive(), CommandCode::Brightness.into(),
0x00000, 0x00000,
0x0000, 0x0000,
0x0000, 0x0000,
@ -82,7 +82,7 @@ impl Into<Packet> for Command {
debug_assert_eq!(pixels.width % 8, 0); debug_assert_eq!(pixels.width % 8, 0);
Packet( Packet(
Header( Header(
CommandCode::BitmapLinearWin.to_primitive(), CommandCode::BitmapLinearWin.into(),
pixel_x / TILE_SIZE, pixel_x / TILE_SIZE,
pixel_y, pixel_y,
pixels.width as u16 / TILE_SIZE, pixels.width as u16 / TILE_SIZE,
@ -154,11 +154,11 @@ impl TryFrom<Packet> for Command {
fn try_from(value: Packet) -> Result<Self, Self::Error> { fn try_from(value: Packet) -> Result<Self, Self::Error> {
let Packet(Header(command_u16, a, b, c, d), _) = value; let Packet(Header(command_u16, a, b, c, d), _) = value;
let command_code = match CommandCode::from_primitive(command_u16) { let command_code = match CommandCode::try_from(command_u16) {
None => { Err(_) => {
return Err(TryFromPacketError::InvalidCommand(command_u16)); return Err(TryFromPacketError::InvalidCommand(command_u16));
} }
Some(value) => value, Ok(value) => value,
}; };
match command_code { match command_code {
@ -242,13 +242,12 @@ fn bitmap_linear_into_packet(
payload: Vec<u8>, payload: Vec<u8>,
) -> Packet { ) -> Packet {
let payload = into_compressed(compression, payload); let payload = into_compressed(compression, payload);
let compression = CompressionCode::to_primitive(&compression);
Packet( Packet(
Header( Header(
command.to_primitive(), command.into(),
offset, offset,
payload.len() as u16, payload.len() as u16,
compression, compression.into(),
0, 0,
), ),
payload, payload,
@ -263,12 +262,12 @@ fn origin_size_payload(
) -> Packet { ) -> Packet {
let Origin(x, y) = origin; let Origin(x, y) = origin;
let Size(w, h) = size; let Size(w, h) = size;
Packet(Header(command.to_primitive(), x, y, w, h), payload.into()) Packet(Header(command.into(), x, y, w, h), payload.into())
} }
fn command_code_only(code: CommandCode) -> Packet { fn command_code_only(code: CommandCode) -> Packet {
Packet( Packet(
Header(code.to_primitive(), 0x0000, 0x0000, 0x0000, 0x0000), Header(code.into(), 0x0000, 0x0000, 0x0000, 0x0000),
vec![], vec![],
) )
} }
@ -306,9 +305,9 @@ fn packet_into_linear_bitmap(
payload.len(), payload.len(),
)); ));
} }
let sub = match CompressionCode::from_primitive(sub) { let sub = match CompressionCode::try_from(sub) {
None => return Err(TryFromPacketError::InvalidCompressionCode(sub)), Err(_) => return Err(TryFromPacketError::InvalidCompressionCode(sub)),
Some(value) => value, Ok(value) => value,
}; };
let payload = match into_decompressed(sub, payload) { let payload = match into_decompressed(sub, payload) {
None => return Err(TryFromPacketError::DecompressionFailed), None => return Err(TryFromPacketError::DecompressionFailed),

49
src/command_code.rs Normal file
View file

@ -0,0 +1,49 @@
use CommandCode::*;
/// The codes used for the commands. See the documentation on the corresponding commands.
#[repr(u16)]
#[derive(Debug, Copy, Clone)]
pub enum CommandCode {
Clear = 0x0002,
Cp437Data = 0x0003,
CharBrightness = 0x0005,
Brightness = 0x0007,
HardReset = 0x000b,
FadeOut = 0x000d,
#[deprecated]
BitmapLegacy = 0x0010,
BitmapLinear = 0x0012,
BitmapLinearWin = 0x0013,
BitmapLinearAnd = 0x0014,
BitmapLinearOr = 0x0015,
BitmapLinearXor = 0x0016,
}
impl Into<u16> for CommandCode {
fn into(self) -> u16 {
self as u16
}
}
impl TryFrom<u16> for CommandCode {
type Error = ();
fn try_from(value: u16) -> Result<Self, Self::Error> {
match value {
value if value == Clear as u16 => Ok(Clear),
value if value == Cp437Data as u16 => Ok(Cp437Data),
value if value == CharBrightness as u16 => Ok(CharBrightness),
value if value == Brightness as u16 => Ok(Brightness),
value if value == HardReset as u16 => Ok(HardReset),
value if value == FadeOut as u16 => Ok(FadeOut),
#[allow(deprecated)]
value if value == BitmapLegacy as u16 => Ok(BitmapLegacy),
value if value == BitmapLinear as u16 => Ok(BitmapLinear),
value if value == BitmapLinearWin as u16 => Ok(BitmapLinearWin),
value if value == BitmapLinearAnd as u16 => Ok(BitmapLinearAnd),
value if value == BitmapLinearOr as u16 => Ok(BitmapLinearOr),
value if value == BitmapLinearXor as u16 => Ok(BitmapLinearXor),
_ => Err(())
}
}
}

View file

@ -1,62 +0,0 @@
use num::{FromPrimitive, ToPrimitive};
use num_derive::{FromPrimitive, ToPrimitive};
/// The codes used for the commands. See the documentation on the corresponding commands.
#[repr(u16)]
#[derive(Debug, FromPrimitive, ToPrimitive, Copy, Clone)]
pub enum CommandCode {
Clear = 0x0002,
Cp437Data = 0x0003,
CharBrightness = 0x0005,
Brightness = 0x0007,
HardReset = 0x000b,
FadeOut = 0x000d,
#[deprecated]
BitmapLegacy = 0x0010,
BitmapLinear = 0x0012,
BitmapLinearWin = 0x0013,
BitmapLinearAnd = 0x0014,
BitmapLinearOr = 0x0015,
BitmapLinearXor = 0x0016,
}
impl CommandCode {
/// convert u16 to CommandCode enum
pub fn from_primitive(value: u16) -> Option<Self> {
FromPrimitive::from_u16(value)
}
/// convert CommandCode enum to u16
pub fn to_primitive(&self) -> u16 {
ToPrimitive::to_u16(self).unwrap()
}
}
/// Specifies the kind of compression to use. Availability depends on features.
#[repr(u16)]
#[derive(Debug, FromPrimitive, ToPrimitive, Clone, Copy)]
pub enum CompressionCode {
None = 0x0,
#[cfg(feature = "compression-gz")]
Gz = 0x677a,
#[cfg(feature = "compression-bz")]
Bz = 0x627a,
#[cfg(feature = "compression-lz")]
Lz = 0x6c7a,
#[cfg(feature = "compression-zs")]
Zs = 0x7a73,
}
impl CompressionCode {
/// convert CompressionCode enum to u16
pub fn to_primitive(&self) -> u16 {
ToPrimitive::to_u16(self).unwrap()
}
/// Convert u16 to CommandCode enum.
///
/// returns: None if the provided value is not one of the valid enum values.
pub fn from_primitive(value: u16) -> Option<Self> {
FromPrimitive::from_u16(value)
}
}

View file

@ -1,5 +1,5 @@
#[cfg(feature = "compression")]
use std::io::{Read, Write}; use std::io::{Read, Write};
#[cfg(feature = "compression-bz")] #[cfg(feature = "compression-bz")]
use bzip2::read::{BzDecoder, BzEncoder}; use bzip2::read::{BzDecoder, BzEncoder};
#[cfg(feature = "compression-gz")] #[cfg(feature = "compression-gz")]
@ -16,7 +16,7 @@ pub(crate) fn into_decompressed(
payload: Payload, payload: Payload,
) -> Option<Payload> { ) -> Option<Payload> {
match kind { match kind {
CompressionCode::None => Some(payload), CompressionCode::Uncompressed => Some(payload),
#[cfg(feature = "compression-gz")] #[cfg(feature = "compression-gz")]
CompressionCode::Gz => { CompressionCode::Gz => {
let mut decoder = GzDecoder::new(&*payload); let mut decoder = GzDecoder::new(&*payload);
@ -67,7 +67,7 @@ pub(crate) fn into_compressed(
payload: Payload, payload: Payload,
) -> Payload { ) -> Payload {
match kind { match kind {
CompressionCode::None => payload, CompressionCode::Uncompressed => payload,
#[cfg(feature = "compression-gz")] #[cfg(feature = "compression-gz")]
CompressionCode::Gz => { CompressionCode::Gz => {
let mut encoder = let mut encoder =

41
src/compression_code.rs Normal file
View file

@ -0,0 +1,41 @@
use CompressionCode::*;
/// Specifies the kind of compression to use. Availability depends on features.
#[repr(u16)]
#[derive(Debug, Clone, Copy)]
pub enum CompressionCode {
Uncompressed = 0x0,
#[cfg(feature = "compression-gz")]
Gz = 0x677a,
#[cfg(feature = "compression-bz")]
Bz = 0x627a,
#[cfg(feature = "compression-lz")]
Lz = 0x6c7a,
#[cfg(feature = "compression-zs")]
Zs = 0x7a73,
}
impl Into<u16> for CompressionCode {
fn into(self) -> u16 {
self as u16
}
}
impl TryFrom<u16> for CompressionCode {
type Error = ();
fn try_from(value: u16) -> Result<Self, Self::Error> {
match value {
value if value == Uncompressed as u16 => Ok(Uncompressed),
#[cfg(feature = "compression-gz")]
value if value == Gz as u16 => Ok(Gz),
#[cfg(feature = "compression-bz")]
value if value == Bz as u16 => Ok(Bz),
#[cfg(feature = "compression-lz")]
value if value == Lz as u16 => Ok(Lz),
#[cfg(feature = "compression-zs")]
value if value == Zs as u16 => Ok(Zs),
_ => Err(())
}
}
}

View file

@ -1,19 +1,21 @@
pub use crate::bit_vec::BitVec; pub use crate::bit_vec::BitVec;
pub use crate::byte_grid::ByteGrid; pub use crate::byte_grid::ByteGrid;
pub use crate::command::{Command, Origin, Size}; pub use crate::command::{Command, Origin, Size};
pub use crate::command_codes::{CommandCode, CompressionCode};
pub use crate::connection::Connection; pub use crate::connection::Connection;
pub use crate::packet::{Header, Packet, Payload}; pub use crate::packet::{Header, Packet, Payload};
pub use crate::pixel_grid::PixelGrid; pub use crate::pixel_grid::PixelGrid;
pub use crate::command_code::CommandCode;
pub use crate::compression_code::CompressionCode;
mod bit_vec; mod bit_vec;
mod byte_grid; mod byte_grid;
mod command; mod command;
mod command_codes; mod command_code;
mod compression; mod compression;
mod connection; mod connection;
mod packet; mod packet;
mod pixel_grid; mod pixel_grid;
mod compression_code;
/// size of a single tile in one dimension /// size of a single tile in one dimension
pub const TILE_SIZE: u16 = 8; pub const TILE_SIZE: u16 = 8;

View file

@ -9,7 +9,7 @@ pub type Payload = Vec<u8>;
#[derive(Debug)] #[derive(Debug)]
pub struct Packet(pub Header, pub Payload); pub struct Packet(pub Header, pub Payload);
impl Into<Vec<u8>> for Packet { impl Into<Payload> for Packet {
fn into(self) -> Vec<u8> { fn into(self) -> Vec<u8> {
let Packet(Header(mode, a, b, c, d), payload) = self; let Packet(Header(mode, a, b, c, d), payload) = self;
@ -33,7 +33,7 @@ fn u16_from_be_slice(slice: &[u8]) -> u16 {
u16::from_be_bytes(bytes) u16::from_be_bytes(bytes)
} }
impl From<Vec<u8>> for Packet { impl From<Payload> for Packet {
fn from(value: Vec<u8>) -> Self { fn from(value: Vec<u8>) -> Self {
let mode = u16_from_be_slice(&value[0..=1]); let mode = u16_from_be_slice(&value[0..=1]);
let a = u16_from_be_slice(&value[2..=3]); let a = u16_from_be_slice(&value[2..=3]);