feat(internal): add packed_int_vector template

This commit is contained in:
Marcus Holland-Moritz 2024-08-22 23:55:04 +02:00
parent 6d6e6662f7
commit ac53f364a7
3 changed files with 301 additions and 0 deletions

View File

@ -433,6 +433,7 @@ if(WITH_TESTS)
lazy_value_test
metadata_requirements_test
options_test
packed_int_vector_test
pcm_sample_transformer_test
pcmaudio_categorizer_test
speedometer_test

View File

@ -0,0 +1,177 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/**
* \author Marcus Holland-Moritz (github@mhxnet.de)
* \copyright Copyright (c) Marcus Holland-Moritz
*
* This file is part of dwarfs.
*
* dwarfs is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* dwarfs is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with dwarfs. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <cstdint>
#include <limits>
#include <vector>
#include <folly/lang/BitsClass.h>
namespace dwarfs::internal {
template <typename T>
class packed_int_vector {
public:
using value_type = T;
using bits_type = folly::Bits<T>;
using underlying_type = typename bits_type::UnderlyingType;
using size_type = size_t;
static constexpr size_type bits_per_block{bits_type::bitsPerBlock};
class value_proxy {
public:
value_proxy(packed_int_vector& vec, size_type i)
: vec_{vec}
, i_{i} {}
operator T() const { return vec_.get(i_); }
value_proxy& operator=(T value) {
vec_.set(i_, value);
return *this;
}
private:
packed_int_vector& vec_;
size_type i_;
};
packed_int_vector() = default;
packed_int_vector(size_type bits)
: bits_{bits} {}
packed_int_vector(size_type bits, size_type size)
: size_{size}
, bits_{bits}
, data_{min_data_size(size, bits)} {}
packed_int_vector(packed_int_vector const&) = default;
packed_int_vector(packed_int_vector&&) = default;
packed_int_vector& operator=(packed_int_vector const&) = default;
packed_int_vector& operator=(packed_int_vector&&) = default;
void reset(size_type bits = 0, size_type size = 0) {
size_ = size;
bits_ = bits;
data_.clear();
data_.resize(min_data_size(size, bits));
}
void resize(size_type size) {
size_ = size;
data_.resize(min_data_size(size, bits_));
}
void reserve(size_type size) { data_.reserve(min_data_size(size, bits_)); }
void shrink_to_fit() { data_.shrink_to_fit(); }
size_type capacity() const {
return bits_ > 0 ? (data_.capacity() * bits_per_block) / bits_ : 0;
}
void clear() {
size_ = 0;
data_.clear();
}
size_type size() const { return size_; }
size_type bits() const { return bits_; }
size_type size_in_bytes() const {
return data_.size() * sizeof(underlying_type);
}
bool empty() const { return size_ == 0; }
T operator[](size_type i) const { return this->get(i); }
T at(size_type i) const {
if (i >= size_) {
throw std::out_of_range("packed_int_vector::at");
}
return this->get(i);
}
T get(size_type i) const {
return bits_ > 0 ? bits_type::get(data_.data(), i * bits_, bits_) : 0;
}
value_proxy operator[](size_type i) { return value_proxy{*this, i}; }
value_proxy at(size_type i) {
if (i >= size_) {
throw std::out_of_range("packed_int_vector::at");
}
return this->operator[](i);
}
void set(size_type i, T value) {
if (bits_ > 0) {
bits_type::set(data_.data(), i * bits_, bits_, value);
}
}
void push_back(T value) {
if (min_data_size(size_ + 1, bits_) > data_.size()) {
data_.resize(data_.size() + 1);
}
set(size_++, value);
}
void pop_back() {
if (size_ > 0) {
--size_;
}
if (min_data_size(size_, bits_) < data_.size()) {
data_.resize(data_.size() - 1);
}
}
T back() const { return get(size_ - 1); }
value_proxy back() { return this->operator[](size_ - 1); }
T front() const { return get(0); }
value_proxy front() { return this->operator[](0); }
std::vector<T> unpack() const {
std::vector<T> result(size_);
for (size_type i = 0; i < size_; ++i) {
result[i] = get(i);
}
return result;
}
private:
static constexpr size_type min_data_size(size_type size, size_type bits) {
return (size * bits + bits_per_block - 1) / bits_per_block;
}
size_type size_{0};
size_type bits_{0};
std::vector<underlying_type> data_;
};
} // namespace dwarfs::internal

View File

@ -0,0 +1,123 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/**
* \author Marcus Holland-Moritz (github@mhxnet.de)
* \copyright Copyright (c) Marcus Holland-Moritz
*
* This file is part of dwarfs.
*
* dwarfs is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* dwarfs is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with dwarfs. If not, see <https://www.gnu.org/licenses/>.
*/
#include <gtest/gtest.h>
#include <dwarfs/internal/packed_int_vector.h>
using namespace dwarfs::internal;
TEST(packed_int_vector, basic) {
packed_int_vector<uint32_t> vec(5);
vec.push_back(1);
vec.push_back(31);
vec.push_back(0);
vec.push_back(5);
vec.push_back(3);
vec.push_back(25);
EXPECT_EQ(vec.size(), 6);
EXPECT_EQ(vec.size_in_bytes(), 4);
EXPECT_EQ(vec[0], 1);
EXPECT_EQ(vec[1], 31);
EXPECT_EQ(vec[2], 0);
EXPECT_EQ(vec[3], 5);
EXPECT_EQ(vec[4], 3);
EXPECT_EQ(vec[5], 25);
vec[0] = 11;
EXPECT_EQ(vec[0], 11);
vec.at(5) = 0;
EXPECT_EQ(vec[5], 0);
vec.resize(10);
EXPECT_EQ(vec[1], 31);
EXPECT_THROW(vec.at(10), std::out_of_range);
EXPECT_THROW(vec.at(10) = 17, std::out_of_range);
auto const& cvec = vec;
EXPECT_EQ(cvec[0], 11);
EXPECT_EQ(cvec[5], 0);
EXPECT_THROW(cvec.at(10), std::out_of_range);
vec.resize(4);
vec.shrink_to_fit();
EXPECT_EQ(vec.capacity(), 6);
EXPECT_EQ(vec[0], 11);
EXPECT_FALSE(vec.empty());
vec.clear();
EXPECT_EQ(vec.size(), 0);
EXPECT_TRUE(vec.empty());
vec.shrink_to_fit();
EXPECT_EQ(vec.capacity(), 0);
EXPECT_EQ(vec.size_in_bytes(), 0);
}
TEST(packed_int_vector, signed_int) {
packed_int_vector<int64_t> vec(13);
for (int64_t i = -4096; i < 4096; ++i) {
vec.push_back(i);
}
EXPECT_EQ(vec.size(), 8192);
EXPECT_EQ(vec.size_in_bytes(), 13312);
EXPECT_EQ(vec.front(), -4096);
EXPECT_EQ(vec.back(), 4095);
vec.resize(4096);
for (int64_t i = 0; i < 4096; ++i) {
EXPECT_EQ(vec[i], i - 4096);
}
auto unpacked = vec.unpack();
for (int64_t i = 0; i < 4096; ++i) {
EXPECT_EQ(unpacked[i], i - 4096);
}
}
TEST(packed_int_vector, zero_bits) {
packed_int_vector<uint32_t> vec(0);
for (uint32_t i = 0; i < 100; ++i) {
vec.push_back(0);
}
EXPECT_EQ(vec.size(), 100);
EXPECT_EQ(vec.size_in_bytes(), 0);
for (uint32_t i = 0; i < 100; ++i) {
EXPECT_EQ(vec[i], 0);
}
}