Logo

dev-resources.site

for different kinds of informations.

Bag: A header-only, simplistic, C++20, zipping & unzipping library.

Published at
1/10/2025
Categories
cpp
programming
gamedev
showdev
Author
rechie_kho
Categories
4 categories in total
cpp
open
programming
open
gamedev
open
showdev
open
Author
10 person written this
rechie_kho
open
Bag: A header-only, simplistic, C++20, zipping & unzipping library.

Hello! It has been a while since the last time I did something for entertainment and learning purposes. So I take up a fairly simple C++ project that hopefully could be useful for other fellow C++ devs.

Problem Statement

As I play around with raylib, the small, easy, and exceptionally cool game library, I met a small issue. There is no elegant way of packing files together. When I use a texture or audio, or any external resources, I either need to convert it into a C++ header that declares a byte array filled with that resource, or making sure the the user having the resource at the particular place. The first approach is no good when comes to loading new resource after compilation, while the latter approach is kind of awkward and too easy to break.

I'm quite fond of the embedded zip approach. Zip files having a quirky file structure in which the header and metadata is at the end of the file in contrast with other file structure, such as executables, which is at the beginning of the file. This quirk gives a possibility, which is appending the zip file at the end of the executable. This makes the file behave like an executable, and a zip file at the same time.

I believe there is definitely other zip library that did what I requested. But I would like to have some fun in C++20. So let's get started.

Overview of bag

With the aforementioned reasoning in mind, I wrote libbag, a header-only library for simple zipping and unzipping. It also includes the zipping (bag) and unzipping (unbag) utilities.

By itself, it is sort of like a serialiser and deserialiser for key-value pairs, with key being a simple C-style string and value as byte array. The bag file structure is fairly simple:

|------------------------|
| key 0 (C-style string) |
|------------------------|
| Content 0 (Byte array) |
|------------------------|
|       ......           |
|------------------------|
| key N (C-style string) |
|------------------------|
| Content N (Byte array) |
|------------------------|
| indices                |
|------------------------|
| metadata               |
|------------------------|
Enter fullscreen mode Exit fullscreen mode

So the key-value pairs are tightly packed together, and some data is appended at the end of the pack. Metadata specifies the whole bag's byte count (to ignore any excess byte in front), position and length of indices, and lastly some magic bytes to indicate it is a bag.

From the length and position of indices specified in metadata, we can query the memory to get the indices slice in bag. There it resides a list of position and length for each of the key-value pairs saved.

To get the keys, just treat each key-value pairs as a C-style string since there is a null-terminator that marks the end of the key string. To query the content, just skip 'till the first null-terminator (basically the whole key), and then you get the content begin position, with some pointer arithmetic using the position and length from the indices, the end position of the content can be calculated.

Here is some example usage.

#include<libbag.hpp>
#include<sstring>


int main() {
    std::map<libbag::key_type, libbag::content_type> input_map;
    /// Add items into `input_map`.
    std::basic_ostreamstring<libbag::unit_type> packed_stream; // Can be replace with std::basic_ofstream to output to file.
    libbag::pack(input_map, packed_stream); // Pack.

    libbag::unit_string_type packed = packed_stream.str();
    std::map<libbag::key_type, libbag::content_type> result;
    libbag::unpack_all(libbag::bag_type(packed.begin(), packed.end()), std::inserter(result, result.end())); // Unpack.
    /// Do something about the unpacked data.
}

Enter fullscreen mode Exit fullscreen mode

The API depends on iterators, stream and inserters to reduce allocations.

I also wrote a simple CLI to zip and unzip files, which is great for referencing as well:

showdev Article's
30 articles in total
Favicon
Automate vCluster Management in EKS with Sveltos and Helm
Favicon
Breweries App
Favicon
Build Your Perfect Roadmap with Roadmap Creator - A Simple and Intuitive Tool
Favicon
Building a Developer-Focused Search Engine in Rust: Lessons Learned and Challenges Overcome 🚀
Favicon
Sample Programs Repo Celebrates 1,000 Code Snippets
Favicon
Bag: A header-only, simplistic, C++20, zipping & unzipping library.
Favicon
[Boost]
Favicon
I built Zeet. A Git-like version Control System
Favicon
AnyAppStart
Favicon
App for sharing time slots for meetings
Favicon
Rust-Powered Password Decrypter: Find the String Behind the Hash! 🦀🔒
Favicon
Turn Your Life into a Real-Life RPG and Level Up Like a Pro
Favicon
Building a webhook tester from scratch
Favicon
Built a todo app
Favicon
366 days of launch weeks
Favicon
How to Enhance Notion Templates with AI for Smarter Workflows
Favicon
Promoting my project https://github.com/oitcode/samarium in this year 2025. Happy new year all.
Favicon
vogen - Value Object Generator in golang
Favicon
Building a No Code AI Platform and the BFS Algorithm
Favicon
Practice SQL using Cricket Dataset (Moneybowl)
Favicon
🎁 The Dev-First OpenGraph (OG) Image Tool You Need (50% Off Lifetime Access!)
Favicon
Config-file-validator v1.8.0 released!
Favicon
Rusty Journal: A Minimal and Efficient CLI Journal & ToDo App
Favicon
Plangs! Your Guide to Programming Languages
Favicon
Introducing an AI Reels Creator with Stock Assets
Favicon
working on my drawing board app. + 1 screenshot. Will share more soon
Favicon
🤯 #NODES24: a practical path to Cloud-Native Knowledge Graph Automation & AI Agents
Favicon
Meet Your New Productivity Partner: NoteBook-App
Favicon
Initial commit
Favicon
Festive New Year Celebration with Fireworks, Confetti, and More! 🎉

Featured ones: