Skip to content

Instantly share code, notes, and snippets.

@leo60228
Last active November 19, 2018 01:00
Show Gist options
  • Save leo60228/2d8df597116e7a15a55eebe72b19cb09 to your computer and use it in GitHub Desktop.
Save leo60228/2d8df597116e7a15a55eebe72b19cb09 to your computer and use it in GitHub Desktop.
#pragma once
#include "vendor/gsl-lite/include/gsl.hpp"
#include <cstdint>
#include <array>
#include <stdexcept>
#include <iostream>
template<std::size_t BufSize>
class buffer_window : public gsl::span<std::uint8_t> {
gsl::span<std::uint8_t> backing_buffer;
explicit buffer_window(
const gsl::span<std::uint8_t>& buf,
std::size_t index_,
std::size_t size
) : backing_buffer(buf),
gsl::span<std::uint8_t>(&buf[index_], size),
index(index_) {}
template<std::size_t size>
friend buffer_window<size> operator+(const buffer_window<size>& window, std::size_t increased);
template<std::size_t size>
friend buffer_window<size> operator-(const buffer_window<size>& window, std::size_t decreased);
public:
gsl::span<uint8_t> full() const {
return backing_buffer;
}
std::size_t index;
buffer_window(const gsl::span<std::uint8_t>& buf) : backing_buffer(buf), index(0) {}
void push_back(std::uint8_t byte) {
full()[index + size()] = byte;
gsl::span<std::uint8_t>::operator=(full().subspan(index, size() + 1));
}
void move(std::ptrdiff_t distance, std::size_t size) {
index += distance;
gsl::span<std::uint8_t>::operator=(full().subspan(index, size));
}
void move(std::ptrdiff_t distance) {
move(distance, size());
}
void move_after(std::size_t new_size) {
move(size(), new_size);
}
template<typename T>
void insert_back(T begin, const T& end) {
while (begin != end) {
push_back(*begin);
++begin;
}
}
template<typename T>
void insert_at(std::size_t index, T begin, const T& end) {
while (begin != end) {
(*this)[index] = *begin;
++begin;
++index;
}
}
buffer_window<BufSize>& operator=(const buffer_window<BufSize>& source) {
backing_buffer = source.backing_buffer;
gsl::span<std::uint8_t>::operator=(source);
return *this;
}
buffer_window<BufSize>& operator=(const gsl::span<std::uint8_t>& source) {
if (source.data() < full().data() || source.data() + source.size() > full().data() + full().size()) {
allocating_exception<std::out_of_range>("Source is unrelated to the buffer").lazy_throw();
}
gsl::span<std::uint8_t>::operator=(source);
return *this;
}
buffer_window<BufSize>& operator+=(std::size_t increased) {
*this = *this + increased;
return *this;
}
buffer_window<BufSize>& operator-=(std::size_t decreased) {
*this = *this - decreased;
return *this;
}
};
template<std::size_t BufSize>
buffer_window<BufSize> operator+(const buffer_window<BufSize>& window, std::size_t increased) {
return buffer_window<BufSize>(window.full(), window.index, window.size() + increased);
}
template<std::size_t BufSize>
buffer_window<BufSize> operator-(const buffer_window<BufSize>& window, std::size_t decreased) {
return buffer_window<BufSize>(window.full(), window.index, window.size() - decreased);
}
template<std::size_t BufSize>
buffer_window<BufSize> make_buffer_window(std::uint8_t(& buf)[BufSize]) {
return buffer_window<BufSize>(buf);
}
#ifdef LEO_UTIL_DEMO
#ifdef LEO_UTIL_NEVER_ALLOCATE
#include "alloca.h"
#endif
void buffer_window_demo() {
std::cout << "# BUFFER_WINDOW DEMO" << std::endl;
gsl::span<uint8_t> buffer;
#ifndef LEO_UTIL_NEVER_ALLOCATE
std::cout << "allocating 2KB buffer on heap (bypassing DISABLE_ALLOCATION)" << std::endl;
{
heap_enabling_context heap_enabler;
buffer = gsl::span<uint8_t>(new uint8_t[2048], 2048);
}
buffer_window<2048> span(buffer);
#else
std::cout << "allocating 2KB buffer on stack (MAY SEGFAULT ON SOME SYSTEMS)" << std::endl;
buffer = gsl::span<uint8_t>(new(alloca(2048)) uint8_t[2048], 2048);
buffer_window<2048> span(buffer);
#endif
std::cout << "initializing with 1kb of 0x05" << std::endl;
span.insert_back(byte_iterator(1024, 5), byte_iterator(1024, 5).end());
std::cout << "span[512] is currently " << (int) span[512] << std::endl;
std::cout << "size is " << span.size() << ", decreasing by 512" << std::endl;
span -= 512;
std::cout << "size is now " << span.size() << ", pushing 6" << std::endl;
span.push_back(6);
std::cout << "size is now " << span.size() << ", increasing by 511" << std::endl;
span += 511;
std::cout << "size is again " << span.size() << std::endl;
std::cout << "span[512] is now " << (int) span[512] << std::endl;
std::cout << "moving forward" << std::endl;
span.move_after(512);
std::cout << "size is now " << span.size() << ", and span[0] is " << (int) span[0] << std::endl;
std::cout << "inserting 512x 9s" << std::endl;
span.insert_at(0, byte_iterator(512, 9), byte_iterator(512, 9).end());
std::cout << "size is still " << span.size() << ", moving to first 2048 via operator=" << std::endl;
span = span.full().first(2048);
std::cout << "size is now " << span.size() << ", span[0] is " << (int) span[0];
std::cout << ", span[512] is " << (int) span[512];
std::cout << ", span[1024] is " << (int) span[1024];
std::cout << ", and span[1540] is " << (int) span[1540] << std::endl;
std::cout << "trying to move to full buffer with operator=, should succeed" << std::endl;
span = span.full();
std::cout << "succeeded with size of " << span.size() << std::endl;
#ifndef LEO_UTIL_NEVER_ALLOCATE
std::cout << "trying to move to new buffer with operator=, should fail..." << std::endl;
uint8_t bad_buffer[20];
try {
span = bad_buffer;
std::cout << "succeeded (incorrect) with new size of " << span.size();
} catch (std::exception& ex) {
std::cout << "errored with: " << ex.what() << std::endl;
}
#else
std::cout << "skipping tests involving exceptions..." << std::endl;
#endif
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment