{"id":41141079,"url":"https://github.com/dallison/cpp_toolbelt","last_synced_at":"2026-03-11T01:15:48.234Z","repository":{"id":169868138,"uuid":"645945767","full_name":"dallison/cpp_toolbelt","owner":"dallison","description":"Collection of useful C++ classes","archived":false,"fork":false,"pushed_at":"2026-01-27T02:29:47.000Z","size":220,"stargazers_count":4,"open_issues_count":1,"forks_count":4,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-14T04:51:15.374Z","etag":null,"topics":["cplusplus","cplusplus-17","libraries","linux","macos","macosx","sockets","unix","utilities","utilities-library"],"latest_commit_sha":null,"homepage":"","language":"C++","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/dallison.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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-05-26T20:31:54.000Z","updated_at":"2026-01-27T02:23:46.000Z","dependencies_parsed_at":null,"dependency_job_id":"e8744b31-0b5d-4ed2-8a4e-1e81e9352f8c","html_url":"https://github.com/dallison/cpp_toolbelt","commit_stats":null,"previous_names":["dallison/cpp_toolbelt"],"tags_count":51,"template":false,"template_full_name":null,"purl":"pkg:github/dallison/cpp_toolbelt","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dallison%2Fcpp_toolbelt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dallison%2Fcpp_toolbelt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dallison%2Fcpp_toolbelt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dallison%2Fcpp_toolbelt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dallison","download_url":"https://codeload.github.com/dallison/cpp_toolbelt/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dallison%2Fcpp_toolbelt/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30365352,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-10T21:41:54.280Z","status":"ssl_error","status_checked_at":"2026-03-10T21:40:59.357Z","response_time":106,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["cplusplus","cplusplus-17","libraries","linux","macos","macosx","sockets","unix","utilities","utilities-library"],"created_at":"2026-01-22T18:49:39.914Z","updated_at":"2026-03-11T01:15:48.197Z","avatar_url":"https://github.com/dallison.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cpp_toolbelt\n\nA collection of useful C++ classes and utility functions for systems programming.\n\n## Table of Contents\n\n- [Installation](#installation)\n- [Classes](#classes)\n  - [FileDescriptor](#filedescriptor)\n  - [Socket Classes](#socket-classes)\n    - [InetAddress](#inetaddress)\n    - [VirtualAddress](#virtualaddress)\n    - [SocketAddress](#socketaddress)\n    - [Socket](#socket)\n    - [UnixSocket](#unixsocket)\n    - [TCPSocket](#tcpsocket)\n    - [UDPSocket](#udpsocket)\n    - [VirtualStreamSocket](#virtualstreamsocket)\n    - [StreamSocket](#streamsocket)\n  - [Logger](#logger)\n  - [MutexLock and RWLock](#mutexlock-and-rwlock)\n  - [BitSet](#bitset)\n  - [Pipe](#pipe)\n  - [Table](#table)\n  - [PayloadBuffer](#payloadbuffer)\n  - [TriggerFd](#triggerfd)\n- [Utility Functions](#utility-functions)\n  - [Now](#now)\n  - [Hexdump](#hexdump)\n  - [PrintCurrentStack](#printcurrentstack)\n\n## Installation\n\n### Using Bazel\n\nAdd this to your `MODULE.bazel` or `WORKSPACE` file:\n\n```python\nhttp_archive(\n    name = \"toolbelt\",\n    urls = [\"https://github.com/dallison/cpp_toolbelt/archive/refs/tags/A.B.C.tar.gz\"],\n    strip_prefix = \"cpp_toolbelt-A.B.C\",\n)\n```\n\nReplace `A.B.C` with the version you want (e.g., `1.0.3`).\n\nAdd a dependency to your `BUILD.bazel` targets:\n\n```python\ndeps = [\n    # ...\n    \"@toolbelt//toolbelt\",\n],\n```\n\n### Using CMake\n\n```cmake\ninclude(FetchContent)\nFetchContent_Declare(\n    toolbelt\n    GIT_REPOSITORY https://github.com/dallison/cpp_toolbelt.git\n    GIT_TAG \u003cversion-tag\u003e\n)\nFetchContent_MakeAvailable(toolbelt)\n\ntarget_link_libraries(your_target PRIVATE toolbelt)\n```\n\n## Classes\n\n### FileDescriptor\n\nA reference-counted wrapper around UNIX file descriptors. Automatically closes the file descriptor when all references are destroyed.\n\n**Header:** `toolbelt/fd.h`\n\n#### API\n\n```cpp\nclass FileDescriptor {\npublic:\n    FileDescriptor();\n    explicit FileDescriptor(int fd, bool owned = true);\n    FileDescriptor(const FileDescriptor \u0026f);\n    FileDescriptor(FileDescriptor \u0026\u0026f);\n    \n    void Close();\n    bool IsOpen() const;\n    bool IsATTY() const;\n    int RefCount() const;\n    struct pollfd GetPollFd();\n    bool Valid() const;\n    int Fd() const;\n    void SetFd(int fd, bool owned = true);\n    void Reset();\n    void Release();\n    void ForceClose();\n    bool IsNonBlocking() const;\n    absl::Status SetNonBlocking();\n    absl::Status SetCloseOnExec();\n    absl::StatusOr\u003cssize_t\u003e Read(void* buffer, size_t length, \n                                  const co::Coroutine* c = nullptr);\n    absl::StatusOr\u003cssize_t\u003e Write(const void* buffer, size_t length,\n                                  const co::Coroutine* c = nullptr);\n};\n```\n\n#### Example\n\n```cpp\n#include \"toolbelt/fd.h\"\n\n// Create from an existing file descriptor\nint fd = open(\"/path/to/file\", O_RDONLY);\ntoolbelt::FileDescriptor file(fd);\n\n// Copy creates a new reference (cheap, just increments ref count)\ntoolbelt::FileDescriptor file2 = file;\nassert(file.RefCount() == 2);\n\n// File descriptor is automatically closed when last reference is destroyed\nfile.Close();  // Still open, file2 still references it\n// file2 goes out of scope -\u003e file descriptor is closed\n\n// Read from file descriptor\nchar buffer[1024];\nauto result = file.Read(buffer, sizeof(buffer));\nif (result.ok()) {\n    size_t bytes_read = *result;\n    // Process data...\n}\n```\n\n### Socket Classes\n\nThe socket classes provide a high-level interface to various socket types. They are coroutine-aware and work with the [co coroutine library](https://github.com/dallison/co).\n\n**Header:** `toolbelt/sockets.h`\n\n#### InetAddress\n\nRepresents an IPv4 internet address and port.\n\n```cpp\nclass InetAddress {\npublic:\n    InetAddress();\n    InetAddress(int port);  // INADDR_ANY with given port\n    InetAddress(const in_addr \u0026ip, int port);\n    InetAddress(const std::string \u0026hostname, int port);\n    InetAddress(const struct sockaddr_in \u0026addr);\n    \n    const sockaddr_in \u0026GetAddress() const;\n    socklen_t GetLength() const;\n    bool Valid() const;\n    in_addr IpAddress() const;  // Host byte order\n    int Port() const;           // Host byte order\n    void SetPort(int port);\n    std::string ToString() const;\n    \n    static InetAddress BroadcastAddress(int port);\n    static InetAddress AnyAddress(int port);\n};\n```\n\n#### Example\n\n```cpp\n#include \"toolbelt/sockets.h\"\n\n// Create address from hostname and port\ntoolbelt::InetAddress addr(\"localhost\", 8080);\n\n// Create address for any interface on port 8080\nauto any_addr = toolbelt::InetAddress::AnyAddress(8080);\n\n// Get address components\nin_addr ip = addr.IpAddress();\nint port = addr.Port();\nstd::string str = addr.ToString();  // \"127.0.0.1:8080\"\n```\n\n#### VirtualAddress\n\nRepresents a virtual socket address (VSOCK) used for communication between VMs and the host.\n\n```cpp\nclass VirtualAddress {\npublic:\n    VirtualAddress();\n    VirtualAddress(uint32_t port);\n    VirtualAddress(uint32_t cid, uint32_t port);\n    \n    uint32_t Cid() const;\n    uint32_t Port() const;\n    void SetPort(uint32_t port);\n    std::string ToString() const;\n    \n    static VirtualAddress HypervisorAddress(uint32_t port);\n    static VirtualAddress HostAddress(uint32_t port);\n    static VirtualAddress AnyAddress(uint32_t port);\n    #if defined(__linux__)\n    static VirtualAddress LocalAddress(uint32_t port);\n    #endif\n};\n```\n\n#### SocketAddress\n\nA variant type that can hold an `InetAddress`, `VirtualAddress`, or Unix socket path (string).\n\n```cpp\nclass SocketAddress {\npublic:\n    SocketAddress();\n    SocketAddress(const InetAddress \u0026addr);\n    SocketAddress(const VirtualAddress \u0026addr);\n    SocketAddress(const std::string \u0026addr);  // Unix socket path\n    \n    const InetAddress \u0026GetInetAddress() const;\n    const VirtualAddress \u0026GetVirtualAddress() const;\n    const std::string \u0026GetUnixAddress() const;\n    std::string ToString() const;\n    bool Valid() const;\n    int Type() const;  // kAddressInet, kAddressVirtual, or kAddressUnix\n    int Port() const;\n};\n```\n\n#### Socket\n\nBase class for all socket types. Provides common functionality for sending and receiving data.\n\n```cpp\nclass Socket {\npublic:\n    void Close();\n    bool Connected() const;\n    absl::StatusOr\u003cssize_t\u003e Receive(char *buffer, size_t buflen,\n                                    const co::Coroutine *c = nullptr);\n    absl::StatusOr\u003cssize_t\u003e Send(const char *buffer, size_t length,\n                                 const co::Coroutine *c = nullptr);\n    absl::StatusOr\u003cssize_t\u003e ReceiveMessage(char *buffer, size_t buflen,\n                                           const co::Coroutine *c = nullptr);\n    absl::StatusOr\u003cstd::vector\u003cchar\u003e\u003e ReceiveVariableLengthMessage(\n        const co::Coroutine *c = nullptr);\n    absl::StatusOr\u003cssize_t\u003e SendMessage(char *buffer, size_t length,\n                                        const co::Coroutine *c = nullptr);\n    absl::Status SetNonBlocking();\n    FileDescriptor GetFileDescriptor() const;\n    absl::Status SetCloseOnExec();\n    bool IsNonBlocking() const;\n    bool IsBlocking() const;\n};\n```\n\n#### UnixSocket\n\nA Unix Domain socket for inter-process communication.\n\n```cpp\nclass UnixSocket : public Socket {\npublic:\n    UnixSocket();\n    explicit UnixSocket(int fd, bool connected = false);\n    \n    absl::Status Bind(const std::string \u0026pathname, bool listen);\n    absl::Status Connect(const std::string \u0026pathname);\n    absl::StatusOr\u003cUnixSocket\u003e Accept(const co::Coroutine *c = nullptr) const;\n    absl::Status SendFds(const std::vector\u003cFileDescriptor\u003e \u0026fds,\n                        const co::Coroutine *c = nullptr);\n    absl::Status ReceiveFds(std::vector\u003cFileDescriptor\u003e \u0026fds,\n                            const co::Coroutine *c = nullptr);\n    std::string BoundAddress() const;\n    absl::StatusOr\u003cstd::string\u003e GetPeerName() const;\n    absl::StatusOr\u003cstd::string\u003e LocalAddress() const;\n};\n```\n\n#### Example\n\n```cpp\n#include \"toolbelt/sockets.h\"\n\n// Server side\ntoolbelt::UnixSocket server;\nserver.Bind(\"/tmp/mysocket\", true);  // true = listen\n\n// In a coroutine or event loop\nauto client = server.Accept(coroutine);\nif (client.ok()) {\n    char buffer[1024];\n    auto result = client-\u003eReceive(buffer, sizeof(buffer), coroutine);\n    // Process received data...\n}\n\n// Client side\ntoolbelt::UnixSocket client;\nclient.Connect(\"/tmp/mysocket\");\nclient.Send(\"Hello\", 5, coroutine);\n```\n\n#### TCPSocket\n\nA TCP socket for network communication.\n\n```cpp\nclass TCPSocket : public NetworkSocket {\npublic:\n    TCPSocket();\n    explicit TCPSocket(int fd, bool connected = false);\n    \n    absl::Status Bind(const InetAddress \u0026addr, bool listen);\n    absl::StatusOr\u003cTCPSocket\u003e Accept(const co::Coroutine *c = nullptr) const;\n    absl::StatusOr\u003cInetAddress\u003e LocalAddress(int port) const;\n    absl::StatusOr\u003cInetAddress\u003e GetPeerName() const;\n};\n```\n\n#### Example\n\n```cpp\n#include \"toolbelt/sockets.h\"\n\n// Server\ntoolbelt::TCPSocket server;\ntoolbelt::InetAddress addr(8080);\nserver.Bind(addr, true);  // Listen on port 8080\n\n// Accept connections\nauto client = server.Accept(coroutine);\nif (client.ok()) {\n    char buffer[1024];\n    auto result = client-\u003eReceive(buffer, sizeof(buffer), coroutine);\n}\n\n// Client\ntoolbelt::TCPSocket client;\ntoolbelt::InetAddress server_addr(\"example.com\", 8080);\nclient.Connect(server_addr);\nclient.Send(\"Hello\", 5, coroutine);\n```\n\n#### UDPSocket\n\nA UDP socket for datagram communication.\n\n```cpp\nclass UDPSocket : public NetworkSocket {\npublic:\n    UDPSocket();\n    explicit UDPSocket(int fd, bool connected = false);\n    \n    absl::Status Bind(const InetAddress \u0026addr);\n    absl::Status JoinMulticastGroup(const InetAddress \u0026addr);\n    absl::Status LeaveMulticastGroup(const InetAddress \u0026addr);\n    absl::Status SendTo(const InetAddress \u0026addr, const void *buffer,\n                       size_t length, const co::Coroutine *c = nullptr);\n    absl::StatusOr\u003cssize_t\u003e Receive(void *buffer, size_t buflen,\n                                    const co::Coroutine *c = nullptr);\n    absl::StatusOr\u003cssize_t\u003e ReceiveFrom(InetAddress \u0026sender, void *buffer,\n                                        size_t buflen,\n                                        const co::Coroutine *c = nullptr);\n    absl::Status SetBroadcast();\n    absl::Status SetMulticastLoop();\n};\n```\n\n#### Example\n\n```cpp\n#include \"toolbelt/sockets.h\"\n\n// Server\ntoolbelt::UDPSocket socket;\ntoolbelt::InetAddress addr(8080);\nsocket.Bind(addr);\n\ntoolbelt::InetAddress sender;\nchar buffer[1024];\nauto result = socket.ReceiveFrom(sender, buffer, sizeof(buffer), coroutine);\nif (result.ok()) {\n    // Process datagram from sender\n}\n\n// Client\ntoolbelt::UDPSocket socket;\ntoolbelt::InetAddress server(\"example.com\", 8080);\nsocket.SendTo(server, \"Hello\", 5, coroutine);\n```\n\n#### VirtualStreamSocket\n\nA virtual stream socket for VM-to-host communication using VSOCK.\n\n```cpp\nclass VirtualStreamSocket : public Socket {\npublic:\n    VirtualStreamSocket();\n    explicit VirtualStreamSocket(int fd, bool connected = false);\n    \n    absl::Status Connect(const VirtualAddress \u0026addr);\n    absl::Status Bind(const VirtualAddress \u0026addr, bool listen);\n    absl::StatusOr\u003cVirtualStreamSocket\u003e Accept(\n        const co::Coroutine *c = nullptr) const;\n    absl::StatusOr\u003cVirtualAddress\u003e LocalAddress(uint32_t port) const;\n    const VirtualAddress \u0026BoundAddress() const;\n    absl::StatusOr\u003cVirtualAddress\u003e GetPeerName() const;\n    uint32_t Cid() const;\n};\n```\n\n#### StreamSocket\n\nA type-erased wrapper that can hold a `TCPSocket`, `VirtualStreamSocket`, or `UnixSocket`. Useful when you need to work with different socket types uniformly.\n\n```cpp\nclass StreamSocket {\npublic:\n    StreamSocket();\n    \n    absl::Status Bind(const SocketAddress \u0026addr, bool listen);\n    absl::Status Connect(const SocketAddress \u0026addr);\n    absl::StatusOr\u003cStreamSocket\u003e Accept(const co::Coroutine *c = nullptr) const;\n    \n    TCPSocket \u0026GetTCPSocket();\n    VirtualStreamSocket \u0026GetVirtualStreamSocket();\n    UnixSocket \u0026GetUnixSocket();\n    \n    SocketAddress BoundAddress() const;\n    void Close();\n    bool Connected() const;\n    absl::StatusOr\u003cssize_t\u003e Receive(char *buffer, size_t buflen,\n                                    const co::Coroutine *c = nullptr);\n    absl::StatusOr\u003cssize_t\u003e Send(const char *buffer, size_t length,\n                                 const co::Coroutine *c = nullptr);\n    // ... other Socket methods\n};\n```\n\n#### Example\n\n```cpp\n#include \"toolbelt/sockets.h\"\n\n// Works with any socket type\ntoolbelt::StreamSocket socket;\n\n// Can bind to TCP, Unix, or VSOCK addresses\ntoolbelt::SocketAddress addr = toolbelt::InetAddress(8080);\n// or: toolbelt::SocketAddress addr(\"/tmp/socket\");\n// or: toolbelt::SocketAddress addr(toolbelt::VirtualAddress(1234));\n\nsocket.Bind(addr, true);\nauto client = socket.Accept(coroutine);\n```\n\n### Logger\n\nA level-aware logger that supports colored output, multiple output modes, and themes.\n\n**Header:** `toolbelt/logging.h`\n\n#### API\n\n```cpp\nenum class LogLevel {\n    kVerboseDebug,\n    kDebug,\n    kInfo,\n    kWarning,\n    kError,\n    kFatal,\n};\n\nenum class LogDisplayMode {\n    kPlain,\n    kColor,\n    kColumnar,\n};\n\nenum class LogTheme {\n    kDefault,\n    kLight,\n    kDark,\n};\n\nclass Logger {\npublic:\n    Logger();\n    Logger(const std::string \u0026subsystem, bool enabled = true,\n           LogTheme theme = LogTheme::kDefault,\n           LogDisplayMode mode = LogDisplayMode::kPlain);\n    Logger(LogLevel min);\n    \n    void Enable();\n    void Disable();\n    absl::Status SetTeeFile(const std::string \u0026filename, bool truncate = true);\n    void SetTeeStream(FILE *stream);\n    void Log(LogLevel level, const char *fmt, ...);\n    void VLog(LogLevel level, const char *fmt, va_list ap);\n    void Log(LogLevel level, uint64_t timestamp,\n             const std::string \u0026source, std::string text);\n    void SetTheme(LogTheme theme);\n    void SetLogLevel(LogLevel l);\n    void SetLogLevel(const std::string \u0026s);  // \"verbose\", \"debug\", \"info\", etc.\n    LogLevel GetLogLevel() const;\n    void SetOutputStream(FILE *stream);\n};\n```\n\n#### Example\n\n```cpp\n#include \"toolbelt/logging.h\"\n\n// Create logger with subsystem name\ntoolbelt::Logger logger(\"myapp\", true, toolbelt::LogTheme::kDefault,\n                       toolbelt::LogDisplayMode::kColor);\n\n// Set minimum log level\nlogger.SetLogLevel(toolbelt::LogLevel::kInfo);\n// or\nlogger.SetLogLevel(\"info\");\n\n// Log messages\nlogger.Log(toolbelt::LogLevel::kInfo, \"Application started\");\nlogger.Log(toolbelt::LogLevel::kDebug, \"Debug value: %d\", 42);\nlogger.Log(toolbelt::LogLevel::kWarning, \"Warning: %s\", \"Something happened\");\nlogger.Log(toolbelt::LogLevel::kError, \"Error code: %d\", errno);\n\n// Tee output to a file\nlogger.SetTeeFile(\"/var/log/myapp.log\");\n\n// Disable logging temporarily\nlogger.Disable();\n// ... do something ...\nlogger.Enable();\n```\n\n### MutexLock and RWLock\n\nRAII wrappers for pthread mutexes and read-write locks.\n\n**Header:** `toolbelt/mutex.h`\n\n#### API\n\n```cpp\nclass MutexLock {\npublic:\n    MutexLock(pthread_mutex_t *mutex);\n    ~MutexLock();\n};\n\nclass RWLock {\npublic:\n    RWLock(pthread_rwlock_t *lock, bool read);\n    ~RWLock();\n    \n    void ReadLock();\n    void WriteLock();\n    void Unlock();\n};\n```\n\n#### Example\n\n```cpp\n#include \"toolbelt/mutex.h\"\n#include \u003cpthread.h\u003e\n\npthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;\n\n{\n    toolbelt::MutexLock lock(\u0026mutex);\n    // Critical section - mutex is automatically unlocked when lock goes out of scope\n    // Do work...\n}\n\npthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;\n\n{\n    toolbelt::RWLock lock(\u0026rwlock, true);  // true = read lock\n    // Multiple readers allowed\n    // Read data...\n}\n\n{\n    toolbelt::RWLock lock(\u0026rwlock, false);  // false = write lock\n    // Exclusive write access\n    // Write data...\n}\n```\n\n### BitSet\n\nA fixed-size bitset that allows allocation of individual bits.\n\n**Header:** `toolbelt/bitset.h`\n\n#### API\n\n```cpp\ntemplate \u003cint Size\u003e\nclass BitSet {\npublic:\n    BitSet();\n    void Init();\n    absl::StatusOr\u003cint\u003e Allocate(const std::string \u0026type);\n    bool IsEmpty() const;\n    void Set(int b);\n    void Clear(int b);\n    bool IsSet(int b) const;\n};\n```\n\n#### Example\n\n```cpp\n#include \"toolbelt/bitset.h\"\n\n// Create a bitset with 256 bits\ntoolbelt::BitSet\u003c256\u003e bitset;\n\n// Allocate the first available bit\nauto result = bitset.Allocate(\"resource\");\nif (result.ok()) {\n    int bit_index = *result;\n    // Use the bit...\n    \n    // Check if bit is set\n    if (bitset.IsSet(bit_index)) {\n        // ...\n    }\n    \n    // Clear the bit when done\n    bitset.Clear(bit_index);\n}\n\n// Check if all bits are clear\nif (bitset.IsEmpty()) {\n    // All resources freed\n}\n```\n\n### Pipe\n\nA pipe for inter-process or inter-coroutine communication. Supports both blocking and non-blocking I/O, and can work with coroutines.\n\n**Header:** `toolbelt/pipe.h`\n\n#### API\n\n```cpp\nclass Pipe {\npublic:\n    static absl::StatusOr\u003cPipe\u003e Create();\n    static absl::StatusOr\u003cPipe\u003e CreateWithFlags(int flags);\n    static absl::StatusOr\u003cPipe\u003e Create(int r, int w);\n    \n    Pipe();\n    Pipe(int r, int w);\n    \n    absl::Status Open(int flags = 0);\n    FileDescriptor \u0026ReadFd();\n    FileDescriptor \u0026WriteFd();\n    void SetReadFd(int fd);\n    void SetWriteFd(int fd);\n    void Close();\n    void ForceClose();\n    absl::Status SetNonBlocking(bool read, bool write);\n    absl::StatusOr\u003csize_t\u003e GetPipeSize();\n    absl::Status SetPipeSize(size_t size);\n    absl::StatusOr\u003cssize_t\u003e Read(char *buffer, size_t length,\n                                 const co::Coroutine *c = nullptr);\n    absl::StatusOr\u003cssize_t\u003e Write(const char *buffer, size_t length,\n                                  const co::Coroutine *c = nullptr);\n};\n\ntemplate \u003ctypename T\u003e\nclass SharedPtrPipe : public Pipe {\npublic:\n    static absl::StatusOr\u003cSharedPtrPipe\u003cT\u003e\u003e Create();\n    static absl::StatusOr\u003cSharedPtrPipe\u003cT\u003e\u003e Create(int r, int w);\n    \n    absl::StatusOr\u003cstd::shared_ptr\u003cT\u003e\u003e Read(const co::Coroutine *c = nullptr);\n    absl::Status Write(std::shared_ptr\u003cT\u003e p, const co::Coroutine *c = nullptr);\n};\n```\n\n#### Example\n\n```cpp\n#include \"toolbelt/pipe.h\"\n\n// Create a pipe\nauto pipe_result = toolbelt::Pipe::Create();\nif (!pipe_result.ok()) {\n    // Handle error\n    return;\n}\ntoolbelt::Pipe pipe = *pipe_result;\n\n// Set non-blocking mode\npipe.SetNonBlocking(true, true);\n\n// Write data (in a coroutine or thread)\nchar data[] = \"Hello, World!\";\nauto write_result = pipe.Write(data, strlen(data), coroutine);\n\n// Read data (in another coroutine or thread)\nchar buffer[1024];\nauto read_result = pipe.Read(buffer, sizeof(buffer), coroutine);\nif (read_result.ok()) {\n    size_t bytes_read = *read_result;\n    // Process data...\n}\n\n// SharedPtrPipe for sending shared_ptr objects (same process only)\nauto shared_pipe = toolbelt::SharedPtrPipe\u003cMyClass\u003e::Create();\nif (shared_pipe.ok()) {\n    auto obj = std::make_shared\u003cMyClass\u003e();\n    shared_pipe-\u003eWrite(obj, coroutine);\n    \n    auto received = shared_pipe-\u003eRead(coroutine);\n    if (received.ok()) {\n        std::shared_ptr\u003cMyClass\u003e obj = *received;\n        // Use object...\n    }\n}\n```\n\n### Table\n\nA table formatter for displaying tabular data with colors and sorting.\n\n**Header:** `toolbelt/table.h`\n\n#### API\n\n```cpp\nclass Table {\npublic:\n    struct Cell {\n        std::string data;\n        color::Color color;\n    };\n    \n    Table(const std::vector\u003cstd::string\u003e titles, ssize_t sort_column = 0,\n          std::function\u003cbool(const std::string \u0026, const std::string \u0026)\u003e comp = nullptr);\n    \n    void AddRow(const std::vector\u003cstd::string\u003e cells);\n    void AddRow(const std::vector\u003cstd::string\u003e cells, color::Color color);\n    void AddRowWithColors(const std::vector\u003cCell\u003e cells);\n    void AddRow();\n    void SetCell(size_t col, Cell \u0026\u0026cell);\n    void Print(int width, std::ostream \u0026os);\n    void Clear();\n    void SortBy(size_t column,\n                 std::function\u003cbool(const std::string \u0026, const std::string \u0026)\u003e comp);\n    void SortBy(size_t column);\n    \n    static Cell MakeCell(std::string data, color::Color color = {...});\n};\n```\n\n#### Example\n\n```cpp\n#include \"toolbelt/table.h\"\n#include \u003ciostream\u003e\n\n// Create table with column titles\ntoolbelt::Table table({\"Name\", \"Age\", \"City\"}, 0);  // Sort by first column\n\n// Add rows\ntable.AddRow({\"Alice\", \"30\", \"New York\"});\ntable.AddRow({\"Bob\", \"25\", \"London\"});\ntable.AddRow({\"Charlie\", \"35\", \"Paris\"});\n\n// Add row with color\nauto red = toolbelt::color::Color{.mod = toolbelt::color::kRed};\ntable.AddRow({\"David\", \"40\", \"Tokyo\"}, red);\n\n// Add row with per-cell colors\nstd::vector\u003ctoolbelt::Table::Cell\u003e cells = {\n    toolbelt::Table::MakeCell(\"Eve\", toolbelt::color::Color{.mod = toolbelt::color::kGreen}),\n    toolbelt::Table::MakeCell(\"28\"),\n    toolbelt::Table::MakeCell(\"Berlin\")\n};\ntable.AddRowWithColors(cells);\n\n// Sort by age column (column 1)\ntable.SortBy(1, [](const std::string \u0026a, const std::string \u0026b) {\n    return std::stoi(a) \u003c std::stoi(b);\n});\n\n// Print table\ntable.Print(80, std::cout);\n```\n\n### PayloadBuffer\n\nA memory buffer with built-in allocation and deallocation. Useful for message serialization and wire protocols. Supports both fixed-size and resizable buffers, with optional bitmap-based small block allocator.\n\n**Header:** `toolbelt/payload_buffer.h`\n\n#### Key Types\n\n```cpp\nusing BufferOffset = uint32_t;\n\nstruct PayloadBuffer {\n    // Constructors\n    PayloadBuffer(uint32_t size, bool bitmap_allocator = true);\n    PayloadBuffer(uint32_t initial_size, Resizer r, bool bitmap_allocator = true);\n    \n    // Allocation\n    static void *Allocate(PayloadBuffer **buffer, uint32_t n,\n                         bool clear = true, bool enable_small_block = true);\n    void Free(void *p);\n    static void *Realloc(PayloadBuffer **buffer, void *p, uint32_t n,\n                        bool clear = true, bool enable_small_block = true);\n    \n    // String operations\n    static char *SetString(PayloadBuffer **self, const char *s, size_t len,\n                          BufferOffset header_offset);\n    std::string GetString(BufferOffset header_offset) const;\n    std::string_view GetStringView(BufferOffset header_offset) const;\n    \n    // Vector operations\n    template \u003ctypename T\u003e\n    static void VectorPush(PayloadBuffer **self, VectorHeader *hdr, T v,\n                          bool enable_small_block = true);\n    template \u003ctypename T\u003e\n    static void VectorReserve(PayloadBuffer **self, VectorHeader *hdr, size_t n,\n                             bool enable_small_block = true);\n    template \u003ctypename T\u003e\n    T VectorGet(const VectorHeader *hdr, size_t index) const;\n    \n    // Address conversion\n    template \u003ctypename T = void\u003e\n    T *ToAddress(BufferOffset offset, size_t size = 0);\n    template \u003ctypename T = void\u003e\n    BufferOffset ToOffset(T *addr, size_t size = 0);\n    \n    // Utilities\n    size_t Size() const;\n    void Dump(std::ostream \u0026os);\n    bool IsValidMagic() const;\n    bool IsMoveable() const;\n};\n```\n\n#### Example\n\n```cpp\n#include \"toolbelt/payload_buffer.h\"\n\n// Create a fixed-size buffer\nchar buffer_memory[4096];\ntoolbelt::PayloadBuffer *pb = new (buffer_memory) toolbelt::PayloadBuffer(4096);\n\n// Allocate memory in the buffer\nvoid *data = toolbelt::PayloadBuffer::Allocate(\u0026pb, 100);\n// Use data...\n\n// Free memory\npb-\u003eFree(data);\n\n// Create a resizable buffer\nauto resizer = [](toolbelt::PayloadBuffer **b, size_t old_size, size_t new_size) {\n    char *new_mem = new char[new_size];\n    memcpy(new_mem, *b, old_size);\n    delete[] reinterpret_cast\u003cchar*\u003e(*b);\n    *b = reinterpret_cast\u003ctoolbelt::PayloadBuffer*\u003e(new_mem);\n};\ntoolbelt::PayloadBuffer *resizable = new toolbelt::PayloadBuffer(1024, resizer);\n\n// Allocate string\ntoolbelt::BufferOffset str_offset = 100;\ntoolbelt::PayloadBuffer::SetString(\u0026resizable, \"Hello\", 5, str_offset);\nstd::string str = resizable-\u003eGetString(str_offset);\n\n// Vector operations\ntoolbelt::VectorHeader vec_hdr = {0, 0};\ntoolbelt::PayloadBuffer::VectorPush\u003cint\u003e(\u0026resizable, \u0026vec_hdr, 42);\ntoolbelt::PayloadBuffer::VectorPush\u003cint\u003e(\u0026resizable, \u0026vec_hdr, 100);\nint value = resizable-\u003eVectorGet\u003cint\u003e(\u0026vec_hdr, 0);  // Returns 42\n```\n\n### TriggerFd\n\nA file descriptor that can be used to trigger events. Implemented as an `eventfd` on Linux or a pipe on other systems.\n\n**Header:** `toolbelt/triggerfd.h`\n\n#### API\n\n```cpp\nclass TriggerFd {\npublic:\n    TriggerFd();\n    TriggerFd(const FileDescriptor \u0026poll_fd, const FileDescriptor \u0026trigger_fd);\n    \n    absl::Status Open();\n    static absl::StatusOr\u003cTriggerFd\u003e Create();\n    static absl::StatusOr\u003cTriggerFd\u003e Create(const FileDescriptor \u0026poll_fd,\n                                           const FileDescriptor \u0026trigger_fd);\n    \n    void Close();\n    void SetPollFd(FileDescriptor fd);\n    void SetTriggerFd(FileDescriptor fd);\n    void Trigger();\n    bool Clear();  // Clears trigger and returns true if it was triggered\n    FileDescriptor \u0026GetPollFd();\n    FileDescriptor \u0026GetTriggerFd();\n    void AddPollFd(std::vector\u003cstruct pollfd\u003e \u0026fds);\n};\n```\n\n#### Example\n\n```cpp\n#include \"toolbelt/triggerfd.h\"\n#include \u003csys/poll.h\u003e\n\n// Create a trigger file descriptor\nauto trigger_result = toolbelt::TriggerFd::Create();\nif (!trigger_result.ok()) {\n    return;\n}\ntoolbelt::TriggerFd trigger = *trigger_result;\n\n// Add to poll set\nstd::vector\u003cstruct pollfd\u003e fds;\ntrigger.AddPollFd(fds);\n\n// In event loop\nint result = poll(fds.data(), fds.size(), -1);\nif (result \u003e 0 \u0026\u0026 (fds[0].revents \u0026 POLLIN)) {\n    if (trigger.Clear()) {\n        // Trigger was set, handle event\n    }\n}\n\n// Trigger from another thread/coroutine\ntrigger.Trigger();\n```\n\n## Utility Functions\n\n### Now\n\nGet the current monotonic time in nanoseconds.\n\n**Header:** `toolbelt/clock.h`\n\n```cpp\nuint64_t Now();\n```\n\n#### Example\n\n```cpp\n#include \"toolbelt/clock.h\"\n\nuint64_t start = toolbelt::Now();\n// Do work...\nuint64_t end = toolbelt::Now();\nuint64_t elapsed_ns = end - start;\ndouble elapsed_ms = elapsed_ns / 1e6;\n```\n\n### Hexdump\n\nDump memory in hexadecimal format.\n\n**Header:** `toolbelt/hexdump.h`\n\n```cpp\nvoid Hexdump(const void* addr, size_t length, FILE* out = stdout);\n```\n\n#### Example\n\n```cpp\n#include \"toolbelt/hexdump.h\"\n\nchar buffer[64] = \"Hello, World!\";\ntoolbelt::Hexdump(buffer, sizeof(buffer));\n// Output:\n// 00000000  48 65 6c 6c 6f 2c 20 57  6f 72 6c 64 21 00 00 00  |Hello, World!...|\n// ...\n```\n\n### PrintCurrentStack\n\nPrint the current stack trace.\n\n**Header:** `toolbelt/stacktrace.h`\n\n```cpp\nvoid PrintCurrentStack(std::ostream \u0026os);\n```\n\n#### Example\n\n```cpp\n#include \"toolbelt/stacktrace.h\"\n#include \u003ciostream\u003e\n\nvoid some_function() {\n    // Print stack trace\n    toolbelt::PrintCurrentStack(std::cerr);\n}\n```\n\n## Dependencies\n\n- [Abseil](https://github.com/abseil/abseil-cpp) - Status types, strings, formatting\n- [co](https://github.com/dallison/co) - Coroutine library (for socket classes)\n\n## License\n\nSee LICENSE file for licensing information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdallison%2Fcpp_toolbelt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdallison%2Fcpp_toolbelt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdallison%2Fcpp_toolbelt/lists"}