{"id":17013652,"url":"https://github.com/craftablescience/bufferstream","last_synced_at":"2025-03-22T14:16:06.377Z","repository":{"id":232339351,"uuid":"728420393","full_name":"craftablescience/BufferStream","owner":"craftablescience","description":"A C++20 library to quickly read objects from a buffer","archived":false,"fork":false,"pushed_at":"2024-05-07T22:32:49.000Z","size":6,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-05-07T23:28:49.340Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/craftablescience.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","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},"funding":{"ko_fi":"craftablescience"}},"created_at":"2023-12-06T22:47:50.000Z","updated_at":"2024-05-30T06:19:24.379Z","dependencies_parsed_at":"2024-04-09T08:27:58.224Z","dependency_job_id":"ff86b854-3323-4da7-b955-7baf4086d9c5","html_url":"https://github.com/craftablescience/BufferStream","commit_stats":null,"previous_names":["craftablescience/bufferstream"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/craftablescience%2FBufferStream","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/craftablescience%2FBufferStream/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/craftablescience%2FBufferStream/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/craftablescience%2FBufferStream/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/craftablescience","download_url":"https://codeload.github.com/craftablescience/BufferStream/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244966525,"owners_count":20539797,"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-10-14T06:13:53.597Z","updated_at":"2025-03-22T14:16:06.356Z","avatar_url":"https://github.com/craftablescience.png","language":"C++","funding_links":["https://ko-fi.com/craftablescience"],"categories":[],"sub_categories":[],"readme":"# BufferStream\nA header-only C++20 library to quickly read/write objects from/to a buffer.\n\n## Usage\n\n### Open\n\nCreate a stream from any contiguous STL type:\n```cpp\n// Vector\nstd::vector buffer\u003cunsigned char\u003e;\nBufferStream stream{buffer};\n\n// String\nstd::string chars;\nBufferStream stream{chars};\n```\n\nOr create a stream from a C array:\n```cpp\n// 1D\nstd::byte buffer[10] {};\nBufferStream stream{buffer};\n\n// 2D\nstd::byte buffer[1][1] {{{}}};\nBufferStream stream{buffer};\n```\n\nOr create a stream from a raw pointer:\n```cpp\nstd::byte* buffer = ...;\nstd::uint64_t length = ...;\nBufferStream stream{buffer, length};\n```\n\n### Read\n\nReading can be done by value:\n```cpp\nauto value = stream.read\u003cstd::uint32_t\u003e();\n```\n\nOr by reference:\n```cpp\nstd::uint32_t value;\n\nstream.read(value);\n// -- OR --\nstream \u003e\u003e value;\n```\n\nIt's possible to read STL containers by value or reference:\n```cpp\nauto ints = stream.read\u003cint, 4\u003e(); // std::array\nauto floats = stream.read\u003cstd::deque\u003cfloat\u003e\u003e(4);\n\nstd::vector\u003cchar\u003e chars;\nstream.read(chars, 3);\n// The following line pushes a char to the end of chars\nstream \u003e\u003e chars;\n```\n\nStrings are supported:\n```cpp\n// Read string up to null terminator\nstd::string str = stream.read_string();\n\n// Read string up to null terminator or char length\n// If null terminator is encountered, stop processing chars and skip the rest of the length of the string\nstd::string str_specific_length = stream.read_string(12);\n\n// Read string up to char length, including null terminators\nstd::string str_null_terms = stream.read_string(12, false);\n\n// All of those functions also work by reference\nstd::string str_ref;\nstream.read(str_ref, 12);\n\n// Equivalent to stream.read(str_ref)\nstream \u003e\u003e str_ref;\n```\n\nIt's possible to read arrays and vectors of `std::byte` values with the earlier functions,\nbut convenience functions are provided since this is such a common operation:\n```cpp\n// Equivalent to:         bytesArray = stream.read\u003cstd::byte, 10\u003e();\nstd::array\u003cstd::byte, 10\u003e bytesArray = stream.read_bytes\u003c10\u003e();\n\n// Equivalent to:      bytesVector = stream.read\u003cstd::vector\u003cstd::byte\u003e\u003e(10);\nstd::vector\u003cstd::byte\u003e bytesVector = stream.read_bytes(10);\n```\n\n### Write\n\nWriting is done much the same way as reading:\n```cpp\nstd::uint32_t value;\n\nstream.write(value);\n// -- OR --\nstream \u003c\u003c value;\n```\n\nIt's possible to write from STL containers:\n```cpp\nstd::array\u003cint, 4\u003e ints;\nstd::deque\u003cfloat\u003e floats;\n...\nstream \u003c\u003c ints;\nstream \u003c\u003c floats;\n```\n\nStrings are supported:\n```cpp\n// Write string and null terminator\nstd::string str = \"Hello world\";\nstream \u003c\u003c str;\n\n// Write string without null terminator\nstream.write(str, false);\n\n// Write string with null terminator, with a maximum length of 6 characters\nstream.write(str, true, 6);\n// Stream stores \"Hello\\0\"\n```\n\nIf writing is not desired, or creating the stream with a const pointer is required,\nuse the BufferStreamReadOnly class to avoid this potential impasse. It hides all\nthe functions that write, allowing the code to compile alright.\n```cpp\nconst std::byte* buffer = ...;\nstd::uint64_t size = ...;\nBufferStreamReadOnly stream{buffer, size}; // Works!\n```\n\n### Seek\n\nSeeking works similarly to how it works in an istream:\n```cpp\nstream.seek(0); // Seek to beginning of the stream\nstream.seek(1, std::ios::beg); // Seek 1 byte ahead of the beginning of the stream\nstream.seek(1, std::ios::cur); // Seek 1 byte forward\nstream.seek(1, std::ios::end); // Seek 1 byte behind the end of the stream\n```\n\nTell reports the cursor's position from the beginning of the stream:\n```cpp\nstream.seek(2);\nstream.tell(); // Returns 2\n```\n\nPeek can read an object directly after the cursor without advancing:\n```cpp\nstream.peek(); // Returns byte after the cursor\nstream.peek\u003cuint16_t\u003e(); // Returns 2-byte integer after the cursor\n```\n\nAt is a more advanced version of peek that can read an object from anywhere in the stream.\nIt supports everything `BufferStream::read` does, but with two extra arguments for offset and offset direction:\n```cpp\nstream.at(0); // Returns the 0-th byte\nstream.at(1, std::ios::cur); // Returns the byte 1 byte after the cursor\nstream.at\u003cuint16_t\u003e(0); // Returns the 2-byte integer starting at the start of the stream\nstream.at\u003cuint16_t\u003e(-1, std::ios::cur); // Returns the 2-byte integer starting 1 byte before the cursor\n```\n\nSkip can skip over a given type:\n```cpp\nstream.skip(); // Skips 1 byte\nstream.skip(2); // Skips 2 bytes\n\nstream.skip\u003cstd::uint16_t\u003e(); // Skips 2 bytes (uint16 is 2 bytes wide)\nstream.skip\u003cstd::uint16_t\u003e(2); // Skips 4 bytes (uint16 is 2 bytes wide)\n```\n\nEvery seek method has a corresponding unsigned variant for convenience.\nUse these methods if you are getting warnings implicitly casting a `uint64_t` to `int64_t`:\n\n```cpp\nstream.seek(-1, std::ios::cur); // Seek 1 byte backward\nstream.seek_u(1u, std::ios::cur); // Seek 1 byte forward\n\nstream.peek(-2); // Returns byte 2 bytes before the cursor\nstream.peek_u(2u); // Returns byte 2 bytes after the cursor\n\nstream.skip(-2); // Skips 2 bytes backward (equivalent to stream.seek(-2, std::ios::cur))\nstream.skip_u(2u); // Skips 2 bytes forward (equivalent to stream.seek_u(2u, std::ios::cur))\n```\n\n### Miscellaneous\n\nMethods that accept a reference or otherwise have no useful return value may be chained:\n```cpp\nstream\n\t.read(x)\n\t.skip\u003cstd::uint32_t\u003e()\n\t.read(y)\n\t.seek(4, std::ios::end)\n\t.write(z);\n\n// When read or write are the only methods being called in the chain, this works too\nstream \u003e\u003e x \u003e\u003e y \u003e\u003e z \u003c\u003c w;\n```\n\nAttempting to read OOB will result in a `std::overflow_error` exception by default. If a resize\ncallback is not set (it's set automatically for resizable std container types), writing OOB will\nresult in this exception as well. If an exception is thrown during a call to a method on a stream,\nthe stream state will not be modified. This behavior can be disabled:\n```cpp\nstd::vector\u003cstd::byte\u003e buffer{{}}; // size 1\nBufferStream stream{buffer};\n\nstream.set_exceptions_enabled(false);\nstream.read\u003cstd::int32_t\u003e(); // Won't throw an exception, but don't ever do this please - it's UB\n```\n\nBig-Endian conversion may be enabled as follows. Keep in mind that if big-endian support is enabled,\nPOD types composed of other types such as structs will need to be decomposed when reading or writing\nthem. If they are not decomposed, the data will not be modified to fit the desired endianness, and\nif exceptions are enabled `std::invalid_argument` will be thrown:\n```cpp\nint x = 0xAB'CD'EF'00;\nBufferStream stream{reinterpret_cast\u003cstd::byte*\u003e(\u0026x), sizeof(int)};\nstream.set_big_endian(true);\nstream.read\u003cint\u003e(); // 0x00'EF'CD'AB\n\nstruct Vector {\n\tstd::int64_t x;\n\tstd::int64_t y;\n};\n\nauto vec = stream.read\u003cVector\u003e(); // INCORRECT - Will throw exception!\n\nVector vec{};\nstream \u003e\u003e vec.x \u003e\u003e vec.y; // Correct\n```\n\nWhen writing to a std container, the stream will automatically resize the container by\npowers of two when it needs more space. **Keep in mind if you are reading spans or views\nover the data in the stream, they will be invalidated if the container is resized!**\nAutomatic resizing on writes that would otherwise exceed the capacity of the container\ncan be disabled by adding an argument to the constructor:\n```cpp\nstd::vector\u003cstd::byte\u003e buffer(32);\nBufferStream stream{buffer, false};\n```\n\nAdditionally, since the container size is adjusted by powers of two, do not treat the size\nof the container as the size of the stream! When the program is finished writing to the stream,\nresize the buffer to the stream's size before using it anywhere:\n```cpp\nstd::vector\u003cstd::byte\u003e buffer;\nBufferStream stream{buffer};\n...\n// Keep in mind the stream size is measured in bytes\nbuffer.resize(stream.size());\n...\n// It may also be convenient to copy the data to a new container\nstd::vector\u003cstd::byte\u003e newData = stream.seek(0).read_bytes(stream.size());\n```\n\nFiles can be opened using the `FileStream` class. Keep in mind this was created for convenience,\nand realistically the `BufferStream` class is better for many more use cases. See the comment at\nthe top of the header for a more complete list of missing features compared to `BufferStream`.\n```cpp\nFileStream stream{\"path/to/file.bin\"};\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcraftablescience%2Fbufferstream","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcraftablescience%2Fbufferstream","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcraftablescience%2Fbufferstream/lists"}