{"id":18586151,"url":"https://github.com/netrexmc/binary-utils","last_synced_at":"2025-04-10T13:31:52.140Z","repository":{"id":45941827,"uuid":"377351102","full_name":"NetrexMC/binary-utils","owner":"NetrexMC","description":"A panic free binary network utility library.","archived":false,"fork":false,"pushed_at":"2024-06-02T16:03:57.000Z","size":214,"stargazers_count":7,"open_issues_count":2,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-09T20:17:07.200Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NetrexMC.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-06-16T02:44:17.000Z","updated_at":"2024-03-21T23:39:18.000Z","dependencies_parsed_at":"2024-11-07T00:39:53.533Z","dependency_job_id":"ff7df15b-10a5-425d-be1a-6e857dad20e4","html_url":"https://github.com/NetrexMC/binary-utils","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NetrexMC%2Fbinary-utils","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NetrexMC%2Fbinary-utils/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NetrexMC%2Fbinary-utils/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NetrexMC%2Fbinary-utils/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NetrexMC","download_url":"https://codeload.github.com/NetrexMC/binary-utils/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248225708,"owners_count":21068078,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-07T00:37:14.599Z","updated_at":"2025-04-10T13:31:47.131Z","avatar_url":"https://github.com/NetrexMC.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# binary_util\n\nA panic-free binary utility crate to read/write binary streams over the wire.\n\n[Documentation](https://docs.rs/binary-util/latest/) |\n[Discord](https://discord.gg/y4aWA5MQxK)\n\n---\n\n\u003e [!WARNING]\n\u003e Version `0.4.0` is the next major version of Binary Utils, and will fully remove the `Streamable`\n\u003e trait, and replace it with `Reader` and `Writer`. This will be a breaking change, and will require\n\u003e you to update your code to use the new `Reader` and `Writer` traits.\n\n# Packages\nThis repository is split into two crates:\n* [`binary-util`](./binary-util) - The main crate.\n* [`binary-util-derive`](./binary-util-derive) - A crate to derive [`binary_util::interfaces::Reader`](https://docs.rs/binary-util/latest/binary_util/interfaces/trait.Reader.html) and [`binary_util::interfaces::Writer`](https://docs.rs/binary-util/latest/binary_util/interfaces/trait.Writer.html) for structs and enums.\n\nBinaryUtils provides the following features:\n\n* [`binary_util::io`](https://docs.rs/binary-util/latest/binary_util/io), to read and write to streams manually.\n* [`binary_util::interfaces`](https://docs.rs/binary-util/latest/binary_util/interfaces), to allow automation of reading data structures.\n* [`binary_util::BinaryIo`](https://docs.rs/binary-util-derive/latest), to automatically implement [`binary_util::interfaces::Reader`](https://docs.rs/binary-util/latest/binary_util/interfaces)\n  and [`binary_util::interfaces::Writer`](https://docs.rs/binary-util/latest/binary_util/interfaces) .\n* [`binary_util::types`](https://docs.rs/binary-util/latest/binary_util/types) for reading and writing non-primitive types like `u24` and `varint`.\n\n# Getting Started\n\nBinary Utils is available on [crates.io](https://crates.io/crates/binary_util), add the following to your `Cargo.toml`:\n\n```toml\n[dependencies]\nbinary_util = \"0.3.3\"\n```\n\nOptionally, if you wish to remove the `derive` feature, you can add the following to your `Cargo.toml`:\n\n```toml\n[dependencies]\nbinary_util = { version = \"0.3.3\", default-features = false }\n```\n\nTo explicitly enable derive, you can use:\n\n```toml\n[dependencies]\nbinary_util = { version = \"0.3.3\", default-features = false, features = [\"derive\"] }\n```\n\n# Binary IO\n\nThe [`io`](https://docs.rs/binary-util/latest/binary_util/io) module provides a way to contingiously write and read binary data with the garauntees of being panic-free.\nThis module provides two structs, [`ByteReader`](https://docs.rs/binary-util/latest/binary_util/interfaces) and [`ByteWriter`](https://docs.rs/binary-util/latest/binary_util/interfaces), which are both wrappers\naround [`bytes::Buf`](https://docs.rs/bytes/1.4.0/bytes/buf/trait.Buf.html) and [`bytes::BufMut`](https://docs.rs/bytes/1.4.0/bytes/buf/trait.BufMut.html) respectively.\nGenerally, you will want to use [`ByteReader`](https://docs.rs/binary-util/latest/binary_util/io/struct.ByteReader.html) and [`ByteWriter`](https://docs.rs/binary-util/latest/binary_util/io/struct.ByteWriter.html) when you are reading and writing binary data manually.\n\n**Read Example:**\nThe following example shows how to read a varint from a stream:\n\n```rust\nuse binary_util::io::ByteReader;\nconst BUFFER: \u0026[u8] = \u0026[255, 255, 255, 255, 7]; // 2147483647\nfn main() {\n    let mut buf = ByteReader::from(\u0026BUFFER[..]);\n    buf.read_var_u32().unwrap();\n}\n```\n\n**Write Example:**\nThe following is an example of how to write a string to a stream:\n\n```rust\nuse binary_util::io::ByteWriter;\nfn main() {\n    let mut buf = ByteWriter::new();\n    buf.write_string(\"Hello world!\");\n}\n```\n\n**Real-world example:**\nA more real-world use-case of this module could be a simple pong server,\nwhere you have two packets, `Ping` and `Pong`, that respectively get relayed\nover udp.\nThis is an example using both [`ByteReader`](https://docs.rs/binary-util/latest/binary_util/io/struct.ByteReader.html) and [`ByteWriter`](https://docs.rs/binary-util/latest/binary_util/io/struct.ByteWriter.html) utilizing [`std::net::UdpSocket`](https://docs.rs/binary-util/latest)\nto send and receive packets.\n\n```rust\nuse binary_util::io::{ByteReader, ByteWriter};\nuse std::net::UdpSocket;\npub struct PingPacket {\n    pub time: u64\n}\npub struct PongPacket {\n    pub time: u64,\n    pub ping_time: u64\n}\nfn main() -\u003e std::io::Result\u003c()\u003e {\n    let socket = UdpSocket::bind(\"127.0.0.1:5000\")?;\n    let mut buf = [0; 1024];\n    loop {\n        let (amt, src) = socket.recv_from(\u0026mut buf)?;\n        let mut buf = ByteReader::from(\u0026buf[..amt]);\n        match buf.read_u8()? {\n            0 =\u003e {\n                let ping = PingPacket {\n                    time: buf.read_var_u64()?\n                };\n                println!(\"Received ping from {}\", src);\n                let mut writer = ByteWriter::new();\n                let pong = PongPacket {\n                    time: std::time::SystemTime::now()\n                            .duration_since(\n                                std::time::UNIX_EPOCH\n                            )\n                            .unwrap()\n                            .as_millis() as u64,\n                    ping_time: ping.time\n                };\n                // Write pong packet\n                writer.write_u8(1);\n                writer.write_var_u64(pong.time);\n                writer.write_var_u64(pong.ping_time);\n                socket.send_to(writer.as_slice(), src)?;\n            },\n            1 =\u003e {\n                let pong = PongPacket {\n                    time: buf.read_var_u64()?,\n                    ping_time: buf.read_var_u64()?\n                };\n                println!(\n                    \"Received pong from {} with ping time of {}ms\",\n                    src,\n                    pong.time - pong.ping_time\n                );\n            }\n            _ =\u003e {\n                println!(\"Received unknown packet from {}\", src);\n            }\n        }\n    }\n}\n```\n\n# Interfaces\n\nThe [`interfaces`](https://docs.rs/binary-util/latest/binary_utils/interfaces) module provides a way to implement reading and writing binary data with\ntwo traits, [`Reader`](https://docs.rs/binary-util/latest/binary_util/interfaces/trait.Reader.html) and [`Writer`](https://docs.rs/binary-util/latest/binary_util/interfaces/trait.Writer.html).\nGenerally, you will refer to using [`BinaryIo`](https://docs.rs/binary-util-derive/latest) when you are implementing or enum; However in the\nscenario you are implementing a type that may not be compatible with [`BinaryIo`](https://docs.rs/binary-util-derive/latest), you can use\nthese traits instead.\n\n**Example:**\nThe following example implements the [`Reader`](https://docs.rs/binary-util/latest/binary_util/interfaces/trait.Reader.html) and [`Writer`](https://docs.rs/binary-util/latest/binary_util/interfaces/trait.Writer.html) traits for a `HelloPacket` allowing\nit to be used with [`BinaryIo`](https://docs.rs/binary-util-derive/latest); this example also allows you to read and write the packet with an\neasier convention.\n\n```rust\nuse binary_util::interfaces::{Reader, Writer};\nuse binary_util::io::{ByteReader, ByteWriter};\npub struct HelloPacket {\n    pub name: String,\n    pub age: u8,\n    pub is_cool: bool,\n    pub friends: Vec\u003cString\u003e\n}\nimpl Reader\u003cHelloPacket\u003e for HelloPacket {\n    fn read(buf: \u0026mut ByteReader) -\u003e std::io::Result\u003cSelf\u003e {\n        Ok(Self {\n            name: buf.read_string()?,\n            age: buf.read_u8()?,\n            is_cool: buf.read_bool()?,\n            friends: Vec::\u003cString\u003e::read(buf)?\n        })\n    }\n}\nimpl Writer\u003cHelloPacket\u003e for HelloPacket {\n    fn write(\u0026self, buf: \u0026mut ByteWriter) -\u003e std::io::Result\u003c()\u003e {\n        buf.write_string(\u0026self.name);\n        buf.write_u8(self.age);\n        buf.write_bool(self.is_cool);\n        self.friends.write(buf)?;\n        Ok(())\n    }\n}\n```\n\nWith the example above, you now are able to read and write the packet with [`BinaryIo`](https://docs.rs/binary-util-derive/latest),\nas well as the added functionality of being able to read and write the packet with\neasier with the `read` and `write` methods that are now implemented.\n\n```rust\nfn main() {\n    let mut buf = ByteWriter::new();\n    let packet = HelloPacket {\n        name: \"John\".to_string(),\n        age: 18,\n        is_cool: true,\n        friends: vec![\"Bob\".to_string(), \"Joe\".to_string()]\n    };\n    buf.write_type(\u0026packet).unwrap();\n}\n```\n\n# Types\nThe [`types`](https://docs.rs/binary-util/latest/binary_utils/types) module provides a way to implement non-primitive types when using the [`BinaryIo`](https://docs.rs/binary-util/latest/binary_util/derive.BinaryIo.html) derive macro.\n\nThis module provides the following helper types:\n* [`varu32`](https://docs.rs/binary-util/latest/binary_util/types/struct.varu32.html - An unsigned 32-bit variable length integer\n* [`vari32`](https://docs.rs/binary-util/latest/binary_util/types/struct.vari32.html - A signed 32-bit variable length integer\n* [`varu64`](https://docs.rs/binary-util/latest/binary_util/types/struct.varu64.html - An unsigned 64-bit variable length integer\n* [`vari64`](https://docs.rs/binary-util/latest/binary_util/types/struct.vari64.html - A signed 64-bit variable length integer\n* [`u24`](https://docs.rs/binary-util/latest/binary_util/types/struct.u24.html) - A 24-bit unsigned integer\n* [`i24`](https://docs.rs/binary-util/latest/binary_util/types/struct.i24.html) - A 24-bit signed integer\n* [`LE`](https://docs.rs/binary-util/latest/binary_util/types/struct.LE.html) - A little endian type\n* [`BE`](https://docs.rs/binary-util/latest/binary_util/types/struct.BE.html) - A big endian type\n\n**General Usage:**\n ```rust\nuse binary_util::BinaryIo;\nuse binary_util::io::{ByteReader, ByteWriter};\nuse binary_util::types::{varu64, varu32, u24, i24, LE, BE};\n\n#[derive(BinaryIo)]\npub struct ProxyStatusPacket {\n    pub clients: u24,\n    pub max_clients: u24,\n    pub net_download: varu32,\n    pub net_upload: varu64,\n}\n\nfn main() {\n    let mut buf = ByteWriter::new();\n    let packet = ProxyStatusPacket {\n        clients: 10,\n        max_clients: 100,\n        net_download: 1000.into(),\n        net_upload: 1000.into()\n    };\n\n    buf.write_type(\u0026packet).unwrap();\n    let mut buf = ByteReader::from(buf.as_slice());\n    let packet = ProxyStatusPacket::read(\u0026mut buf).unwrap();\n    println!(\"Clients: {}\", packet.clients);\n    println!(\"Max Clients: {}\", packet.max_clients);\n    println!(\"Net Download: {}\", packet.net_download.0);\n    println!(\"Net Upload: {}\", packet.net_upload.0);\n}\n```\n\n# Codegen\n\nThe [`BinaryIo`](https://docs.rs/binary-util-derive/latest) derive macro provides a way to implement both [`Reader`](https://docs.rs/binary-util/latest/binary_util/interfaces/trait.Reader.html) and [`Writer`](https://docs.rs/binary-util/latest/binary_util/interfaces/trait.Writer.html) for a type.\nThis macro is extremely useful when you are trying to implement multiple data structures that you want\nto seemlessly read and write with the [`io`](https://docs.rs/binary-util/latest/binary_util/io) module.\n**Example:**\nThe following example implements the [`BinaryIo`](https://docs.rs/binary-util-derive/latest) trait for a `HelloPacket`, shortening the previous\nexample to just a few lines of code.\n\n```rust\nuse binary_util::BinaryIo;\n#[derive(BinaryIo)]\npub struct HelloPacket {\n    pub name: String,\n    pub age: u8,\n    pub is_cool: bool,\n    pub friends: Vec\u003cString\u003e\n}\nfn main() {\n    let mut buf = ByteWriter::new();\n    let packet = HelloPacket {\n        name: \"John\".to_string(),\n        age: 18,\n        is_cool: true,\n        friends: vec![\"Bob\".to_string(), \"Joe\".to_string()]\n    };\n    buf.write_type(\u0026packet).unwrap();\n}\n```\n\n## Structs\n\n`BinaryIo` supports both Named, and Unnamed structs. However, this derive macro does not support unit structs.\nThis macro will encode/decode the fields of the struct in the order they are defined, as long as they are not skipped;\nhowever as an additional requirement, each field **MUST** implement** the `Reader` and `Writer` traits, if they do not, this macro will fail.\n**Example:**\nThe following example will provide both a `Reader` and `Writer` implementation for the struct `ABC`, where each field is encoded as it's respective\ntype to the `Bytewriter`/`Bytereader`.\n\n```rust\nuse binary_util::interfaces::{Reader, Writer};\nuse binary_util::BinaryIo;\n#[derive(BinaryIo, Debug)]\nstruct ABC {\n   a: u8,\n   b: Option\u003cu8\u003e,\n   c: u8,\n}\n```\n\nSometimes it can be more optimal to use Unnamed fields, if you do not care about the field names, and only want to encode/decode the fields in the order they are defined.\nThe behavior of this macro is the same as the previous example, except the fields are unnamed.\n\n```rust\nuse binary_util::interfaces::{Reader, Writer};\nuse binary_util::BinaryIo;\n#[derive(BinaryIo, Debug)]\nstruct ABC(u8, Option\u003cu8\u003e, u8);\n```\n\n---\n\n## Enums\n\nEnums function a bit differently than structs, and have a few more exclusive attributes that allow you to adjust the behavior of the macro.\nIdentically to structs, this macro will encode/decode the fields of the enum in the order they are defined, as long as they are not skipped.\n\n\u003e **Note:** Enums require the `#[repr]` attribute to be used, and the `#[repr]` attribute must be a primitive type.\n\n### Unit Variants\n\nUnit variants are the simplest variant, of an enum and require the `#[repr(usize)]` attribute to be used. \u003cbr /\u003e\n**Example:**\nThe following example will encode the `ProtcolEnum` enum as a `u8`, where each variant is encoded, by default, starting from 0.\n\n```rust\nuse binary_util::BinaryIo;\nuse binary_util::{Reader, Writer};\n#[derive(BinaryIo, Debug)]\n#[repr(u8)]\npub enum ProtocolEnum {\n    Basic,\n    Advanced,\n    Complex\n}\n```\n\n### Unnamed Variants (Tuple)\n\nUnnamed variants allow you to encode the enum with a byte header specified by the discriminant. \u003cbr /\u003e\nHowever, this variant is limited to the same functionality as a struct. The containing data of each field\nwithin the variant must implement the `Reader` and `Writer` traits. Otherwise, this macro will fail with an error.\n**Example:**\nThe following example makes use of Unnamed variants, in this case `A` to encode both `B` and `C` retrospectively.\nWhere `A::JustC` will be encoded as `0x02` with the binary data of struct `B`.\n\n```rust\nuse binary_util::BinaryIo;\nuse binary_util::{Reader, Writer};\n#[derive(BinaryIo, Debug)]\npub struct B {\n    foo: String,\n    bar: Vec\u003cu8\u003e\n}\n#[derive(BinaryIo, Debug)]\npub struct C {\n    foobar: u32,\n}\n#[derive(BinaryIo, Debug)]\n#[repr(u8)]\npub enum A {\n    JustB(B) = 1,\n    JustC(C), // 2\n    Both(B, C) // 3\n}\nfn main() {\n    let a = A::JustC(C { foobar: 4 });\n    let buf = a.write_to_bytes().unwrap();\n    assert_eq!(buf, \u0026[2, 4, 0, 0, 0]);\n}\n```\n\n---\n\n## Attributes\n\nStructs and enums have a few exclusive attributes that can be used to control the encoding/decoding of the struct. \u003cbr /\u003e\nThese attributes control and modify the behavior of the `BinaryIo` macro.\n\u003cbr /\u003e\u003cbr /\u003e\n\n### Skip\n\nThe `#[skip]` attribute does as the name implies, and can be used to skip a field when encoding/decoding. \u003cbr /\u003e\n**Syntax:**\n\n```rust\n#[skip]\n```\n\n**Compatibility:**\n\n- ✅ Named Structs\n- ✅ Unnamed Structs\n- ✅ Enums\n\n\n  **Example:**\n  \n```rust\nuse binary_util::interfaces::{Reader, Writer};\nuse binary_util::BinaryIo;\n#[derive(BinaryIo, Debug)]\nstruct ABC {\n   a: u8,\n   #[skip]\n   b: Option\u003cu8\u003e,\n   c: u8\n}\n```\n  \n  ### Require\n  \n  This attribute explicitly requires a field to be present when either encoding, or decoding; and will fail if the field is not present. \u003cbr /\u003e\n  This can be useful if you want to ensure that an optional field is present when encoding, or decoding it.\n  **Syntax:**\n  \n  ```rust\n  #[require(FIELD)]\n  ```\n  \n  **Compatibility:**\n- ✅ Named Structs\n- ❌ Unnamed Structs\n- ❌ Enums\n\n**Example:**\nIn the following example, `b` is explicitly required to be present when encoding, or decoding `ABC`, and it's value is not allowed to be `None`.\n\n```rust\nuse binary_util::interfaces::{Reader, Writer};\nuse binary_util::BinaryIo;\n#[derive(BinaryIo, Debug)]\nstruct ABC {\n    a: u8,\n    b: Option\u003cu8\u003e,\n    #[require(b)]\n    c: Option\u003cu8\u003e\n}\n```\n\n### If Present\n\nThis attribute functions identically to `#[require]`, however it does not fail if the field is not present.\n\n### Satisfy\n\nThis attribute will fail if the expression provided does not evaluate to `true`. \u003cbr /\u003e\nThis attribute can be used to ensure that a field is only encoded/decoded if a certain condition is met.\nThis can be useful if you're sending something like `Authorization` or `Authentication` packets, and you want to ensure that the client is authenticated before\nsending the packet.\n**Syntax:**\n\n```rust\n#[satisfy(EXPR)]\n```\n\n**Compatibility:**\n\n- ✅ Named Structs\n- ❌ Unnamed Structs\n- ❌ Enums\n\n**Example:**\n  \n```rust\n#[derive(BinaryIo, Debug)]\nstruct ABC {\n   a: u8,\n   #[satisfy(self.a == 10)]\n   b: Option\u003cu8\u003e,\n   c: u8,\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnetrexmc%2Fbinary-utils","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnetrexmc%2Fbinary-utils","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnetrexmc%2Fbinary-utils/lists"}