Program Listing for File libcacaocommon.hpp

Return to documentation for file (libs/common/include/libcacaocommon.hpp)

#pragma once

#include <vector>
#include <istream>
#include <ostream>
#include <string>
#include <cstring>
#include <functional>
#include <stdexcept>

inline void CheckException(bool cond, std::string msg, std::function<void()> unwindFn = []() {}) {
    if(!cond) {
        unwindFn();
        throw std::runtime_error(msg);
    }
}

class bytestreambuf : public std::streambuf {
  public:
    bytestreambuf(std::vector<char>& data)
      : buffer(data) {
        setg(buffer.data(), buffer.data(), buffer.data() + buffer.size());
        setp(buffer.data(), buffer.data() + buffer.size());
    }

  protected:
    int overflow(int ch) override {
        if(ch == EOF) {
            return EOF;
        }

        //Expand buffer and ensure proper size
        std::ptrdiff_t offset = pptr() - pbase();
        buffer.push_back(static_cast<char>(ch));

        //Reset stream buffer pointers
        setp(buffer.data(), buffer.data() + buffer.size());
        pbump(static_cast<int>(offset + 1));

        return ch;
    }

    std::streamsize xsputn(const char* s, std::streamsize n) override {
        std::ptrdiff_t remaining = epptr() - pptr();
        if(n > remaining) {
            std::ptrdiff_t offset = pptr() - pbase();
            buffer.insert(buffer.end(), s, s + n);

            //Reset buffer pointers
            setp(buffer.data(), buffer.data() + buffer.size());
            pbump(static_cast<int>(offset + n));
        } else {
            std::memcpy(pptr(), s, n);
            pbump(static_cast<int>(n));
        }
        return n;
    }

    pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) override {
        std::streamoff newpos = -1;

        //Input modification
        if(which & std::ios_base::in) {
            //Get current pointers
            char* begin = eback();
            char* at = gptr();
            char* end = egptr();

            //Move to the appropriate position based on the direction
            switch(dir) {
                case std::ios::beg:
                    newpos = off;//Offset from the beginning needs no modifier
                    break;
                case std::ios::cur:
                    newpos = at - begin + off;//Get the index we are in and add the offset
                    break;
                case std::ios::end:
                    newpos = end - begin + off;//Find the end index and add the offset
                    break;
                default:
                    return -1;
            }

            //Bounds-check the offset
            if(newpos < 0 || newpos > (end - begin)) {
                return -1;
            }

            //Set the new get area
            setg(begin, begin + newpos, end);
        }

        //Output modification
        if(which & std::ios_base::out) {
            //Get current pointers
            char* begin = pbase();
            char* at = pptr();
            char* end = epptr();

            //Move to the appropriate position based on the direction
            switch(dir) {
                case std::ios::beg:
                    newpos = off;//Offset from the beginning needs no modifier
                    break;
                case std::ios::cur:
                    newpos = at - begin + off;//Get the index we are in and add the offset
                    break;
                case std::ios::end:
                    newpos = end - begin + off;//Find the end index and add the offset
                    break;
                default:
                    return -1;
            }

            //Bounds-check the offset
            if(newpos < 0 || newpos > (end - begin)) {
                return -1;
            }

            //Reset put area pointers
            setp(buffer.data(), buffer.data() + buffer.size());

            //Advance put cursor
            pbump(newpos);
        }

        return newpos;
    }

    pos_type seekpos(pos_type pos, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out) override {
        return seekoff(pos, std::ios::beg, which);
    }

  private:
    std::vector<char>& buffer;
};

class ibytestream : public std::istream {
  public:
    ibytestream(std::vector<char>& data)
      : std::istream(bufInit(data)) {
        rdbuf(buf);
        init(buf);
    }

    ~ibytestream() {
        delete buf;
    }

  private:
    bytestreambuf* buf;

    bytestreambuf* bufInit(std::vector<char>& data) {
        buf = new bytestreambuf(data);
        return buf;
    }
};

class obytestream : public std::ostream {
  public:
    obytestream(std::vector<char>& data)
      : std::ostream(bufInit(data)) {
        rdbuf(buf);
        init(buf);
    }

    ~obytestream() {
        delete buf;
    }

  private:
    bytestreambuf* buf;

    bytestreambuf* bufInit(std::vector<char>& data) {
        buf = new bytestreambuf(data);
        return buf;
    }
};