Я реализовал свой CycleBuffer и провел несколько модульных тестов. Но он не работает при случайной операции чтения / записи, и я не могу понять, почему. Пожалуйста, помогите мне. Я использую стандарт C ++ 14.
Мой CycleBuffer полагается на libfmt и Catch2 .
CycleBuffer.h
// Copyright 2019- <shepherd-lang>
// Apache License Version 2.0
#pragma once
#include <cstdio>
namespace detail {
/**
* CycleBuffer memory status
*
* positive: tail >= head
* 1)
* buf = head = tail = nullptr
*
* 2)
* head tail
* | |
* |-----------------------||
* |
* buf
*
* 3)
* head
* |
* |---------||-------------|
* | |
* buf tail
*
* 4)
* head tail
* | |
* |----|---------------|---|
* |
* buf
*
* negative: tail < head
* 1)
* tail head
* | |
* |----|---------------|---|
* |
* buf
*
* 2)
* tail head
* | |
* |----------------|-------|
* |
* buf
*
* 3)
* tail head
* | |
* |----|------------------||
* |
* buf
*
* 4)
* tail head
* | |
* |-----------------------||
* |
* buf
*
* impossible)
* tail head
* | |
* |------------------------|
* |
* buf
*/
template <unsigned int D> class CycleBuffer {
public:
CycleBuffer();
virtual ~CycleBuffer();
virtual int capacity() const;
virtual int size() const;
virtual bool empty() const;
virtual bool full() const;
virtual void reset();
// next position start from <position>
virtual char *next(char *position, int distance = 1) const;
virtual const char *next(const char *position, int distance = 1) const;
// previous position start from <position>
virtual char *prev(char *position, int distance = 1) const;
virtual const char *prev(const char *position, int distance = 1) const;
virtual char *begin();
virtual const char *begin() const;
virtual char *rbegin();
virtual const char *rbegin() const;
virtual char *end();
virtual const char *end() const;
virtual char *rend();
virtual const char *rend() const;
virtual bool contain(const char *position) const;
virtual std::string toString() const;
// write at most <n> bytes to <buf>
// @return bytes really write
virtual int write(char *buf, int n);
// read at most <n> bytes from <buf>
// @return bytes really read
virtual int read(const char *buf, int n);
// write all bytes to <fp>
// @return bytes really write
virtual int writefile(FILE *fp);
// write at most <n> bytes to <fp>
virtual int writefile(FILE *fp, int n);
// read all bytes from <fp>
// @return bytes really read
virtual int readfile(FILE *fp);
// read at most <n> bytes from <fp>
// @return bytes really read
virtual int readfile(FILE *fp, int n);
protected:
virtual char *nextImpl(char *position, int distance = 1) const;
virtual char *prevImpl(char *position, int distance = 1) const;
virtual void release();
virtual int expand(int n);
virtual char *bufEnd();
virtual const char *bufEnd() const;
// if positive direction
virtual bool positive() const;
// positive direction
virtual long pSize() const;
virtual bool pContain(const char *position) const;
// negative direction
virtual long nLeftSize() const;
virtual long nRightSize() const;
virtual bool nLeftContain(const char *position) const;
virtual bool nRightContain(const char *position) const;
virtual int writeImpl(void *src, int n,
int (*writeHandler)(void *, void *, int));
virtual int readImpl(void *src, int n,
int (*readHandler)(void *, void *, int, int));
char *buf_;
char *head_;
char *tail_;
int capacity_;
};
} // namespace detail
class DynamicBuffer : public detail::CycleBuffer<1> {
public:
DynamicBuffer(int capacity = 0);
virtual ~DynamicBuffer() = default;
virtual std::string toString() const;
};
class FixedBuffer : public detail::CycleBuffer<0> {
public:
FixedBuffer(int capacity);
virtual ~FixedBuffer() = default;
virtual std::string toString() const;
};
CycleBuffer. cpp
// Copyright 2019- <shepherd-lang>
// Apache License Version 2.0
#include "CycleBuffer.h"
#include "fmt/format.h"
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <tuple>
#define ALIGN(n) (n < 8 ? 8 : (n % 8 == 0 ? n : ((n / 8 + 1) * 8)))
#define MIN(a, b) (std::min<int>(a, b))
#define BUF_SIZE 1024
namespace detail {
template <unsigned int D> char *CycleBuffer<D>::bufEnd() {
return buf_ + capacity_;
}
template <unsigned int D> const char *CycleBuffer<D>::bufEnd() const {
return buf_ + capacity_;
}
// is positive direction
template <unsigned int D> bool CycleBuffer<D>::positive() const {
return tail_ >= head_;
}
template <unsigned int D> long CycleBuffer<D>::pSize() const {
return tail_ - head_;
}
template <unsigned int D>
bool CycleBuffer<D>::pContain(const char *position) const {
return position >= head_ && position < tail_;
}
template <unsigned int D> long CycleBuffer<D>::nLeftSize() const {
return tail_ - buf_;
}
template <unsigned int D> long CycleBuffer<D>::nRightSize() const {
return bufEnd() - head_;
}
template <unsigned int D>
bool CycleBuffer<D>::nLeftContain(const char *position) const {
return position >= buf_ && position < tail_;
}
template <unsigned int D>
bool CycleBuffer<D>::nRightContain(const char *position) const {
return position >= head_ && position < bufEnd();
}
template <unsigned int D>
CycleBuffer<D>::CycleBuffer()
: buf_(nullptr), head_(nullptr), tail_(nullptr), capacity_(0) {}
template <unsigned int D> CycleBuffer<D>::~CycleBuffer() { reset(); }
template <unsigned int D> int CycleBuffer<D>::capacity() const {
return capacity_ > 0 ? capacity_ - 1 : 0;
}
template <unsigned int D> int CycleBuffer<D>::size() const {
return positive() ? pSize() : (nLeftSize() + nRightSize());
}
template <unsigned int D> bool CycleBuffer<D>::empty() const {
return size() == 0;
}
template <unsigned int D> bool CycleBuffer<D>::full() const {
return size() == capacity();
}
template <unsigned int D> void CycleBuffer<D>::reset() {
release();
head_ = nullptr;
tail_ = nullptr;
capacity_ = 0;
}
template <unsigned int D>
char *CycleBuffer<D>::nextImpl(char *position, int distance) const {
if (position == end() || !position) {
return (char *)end();
}
char *np = position + distance;
if (positive()) {
return np > tail_ ? (char *)end() : np;
} else {
if (nLeftContain(position)) {
return np > tail_ ? (char *)end() : np;
} else {
if (np < bufEnd()) {
return np;
}
np = (char *)buf_ + (distance - (bufEnd() - position));
return np > tail_ ? (char *)end() : np;
}
}
}
template <unsigned int D>
char *CycleBuffer<D>::next(char *position, int distance) const {
return nextImpl(position, distance);
}
template <unsigned int D>
const char *CycleBuffer<D>::next(const char *position, int distance) const {
return nextImpl((char *)position, distance);
}
template <unsigned int D>
char *CycleBuffer<D>::prevImpl(char *position, int distance) const {
if (position == rend() || !position) {
return (char *)rend();
}
char *np = position - distance;
if (positive()) {
return np < head_ ? (char *)rend() : np;
} else {
if (nLeftContain(position)) {
if (np >= buf_) {
return np;
}
np = (char *)bufEnd() - (distance - (position - buf_));
return np < head_ ? (char *)rend() : np;
} else {
return np < head_ ? (char *)rend() : np;
}
}
}
template <unsigned int D>
char *CycleBuffer<D>::prev(char *position, int distance) const {
return prevImpl(position, distance);
}
template <unsigned int D>
const char *CycleBuffer<D>::prev(const char *position, int distance) const {
return prevImpl((char *)position, distance);
}
template <unsigned int D> char *CycleBuffer<D>::begin() { return head_; }
template <unsigned int D> const char *CycleBuffer<D>::begin() const {
return head_;
}
template <unsigned int D> char *CycleBuffer<D>::rbegin() {
return tail_ == buf_ ? bufEnd() - 1 : tail_ - 1;
}
template <unsigned int D> const char *CycleBuffer<D>::rbegin() const {
return tail_ == buf_ ? bufEnd() - 1 : tail_ - 1;
}
template <unsigned int D> char *CycleBuffer<D>::end() { return tail_; }
template <unsigned int D> const char *CycleBuffer<D>::end() const {
return tail_;
}
template <unsigned int D> char *CycleBuffer<D>::rend() { return head_ - 1; }
template <unsigned int D> const char *CycleBuffer<D>::rend() const {
return head_ - 1;
}
template <unsigned int D>
bool CycleBuffer<D>::contain(const char *position) const {
return positive() ? pContain(position)
: (nLeftContain(position) || nRightContain(position));
}
template <unsigned int D> std::string CycleBuffer<D>::toString() const {
return fmt::format("buf_:{}, head_:{}, tail_:{}, capacity_:{}", (void *)buf_,
(void *)head_, (void *)tail_, capacity_);
}
template <unsigned int D> void CycleBuffer<D>::release() {
if (buf_) {
std::free(buf_);
buf_ = nullptr;
}
}
static int writeMemHandler(void *src, void *buf, int n) {
std::memcpy(src, buf, n);
return n;
}
static int writeFileHandler(void *src, void *buf, int n) {
size_t r = std::fwrite(buf, 1, n, (FILE *)src);
return (int)r;
}
#define WINC(n) \
do { \
head_ = next(head_, n); \
writen += n; \
} while (0)
template <unsigned int D>
int CycleBuffer<D>::writeImpl(void *src, int n,
int (*writeHandler)(void *, void *, int)) {
EX_ASSERT(n >= 0, "n {} < 0", n);
if (!src || !n) {
return 0;
}
if (empty()) {
return 0;
}
int writen = 0;
if (positive()) {
int fn = MIN(size(), n);
int fnr = writeHandler(src, head_, fn);
WINC(fnr);
} else {
int fn = MIN(bufEnd() - head_, n);
int fnr = writeHandler(src, head_, fn);
WINC(fnr);
if (n > writen) {
int sn = MIN(n - writen, pSize());
int snr = writeHandler(src, head_, sn);
WINC(snr);
}
}
return writen;
}
template <unsigned int D> int CycleBuffer<D>::write(char *buf, int n) {
return writeImpl(buf, n, writeMemHandler);
}
template <unsigned int D> int CycleBuffer<D>::writefile(FILE *fp, int n) {
return writeImpl(fp, n, writeFileHandler);
}
template <unsigned int D> int CycleBuffer<D>::writefile(FILE *fp) {
int n = 0;
int tmp;
do {
tmp = writefile(fp, BUF_SIZE);
n += tmp;
} while (tmp > 0);
return n;
}
#define RINC(n) \
do { \
tail_ += n; \
readn += n; \
} while (0)
static int readMemHandler(void *src, void *buf, int n, int readn) {
char *src2 = (char *)src + readn;
std::memcpy(buf, src2, n);
return n;
}
static int readFileHandler(void *src, void *buf, int n, int readn) {
size_t r = std::fread(buf, 1, n, (FILE *)src);
return (int)r;
}
template <unsigned int D>
int CycleBuffer<D>::readImpl(void *src, int n,
int (*readHandler)(void *, void *, int, int)) {
if (!src || !n) {
return 0;
}
if (D) {
if (capacity() - size() < n) {
int c1 = capacity() + n + 1;
int c2 = capacity() * 2 + 1;
expand(c1 > c2 ? ALIGN(c1) : ALIGN(c2));
}
}
if (full()) {
return 0;
}
int readn = 0;
if (positive()) {
int fn = MIN(bufEnd() - tail_ - (head_ == buf_ ? 1 : 0), n);
int fnr = readHandler(src, tail_, fn, readn);
RINC(fnr);
if (tail_ == bufEnd()) {
tail_ = buf_;
}
if (n > readn && head_ != buf_) {
int sn = MIN(n - readn, head_ - tail_ - 1);
int snr = readHandler(src, tail_, sn, readn);
RINC(snr);
}
} else {
int fn = MIN(n, head_ - tail_ - 1);
int fnr = readHandler(src, tail_, fn, readn);
RINC(fnr);
}
return readn;
}
template <unsigned int D> int CycleBuffer<D>::read(const char *buf, int n) {
return readImpl((void *)buf, n, readMemHandler);
}
template <unsigned int D> int CycleBuffer<D>::readfile(FILE *fp, int n) {
return readImpl(fp, n, readFileHandler);
}
template <unsigned int D> int CycleBuffer<D>::readfile(FILE *fp) {
int n = 0;
int tmp;
do {
tmp = readfile(fp, BUF_SIZE);
n += tmp;
} while (tmp > 0);
return n;
}
template <unsigned int D> int CycleBuffer<D>::expand(int n) {
if (n <= capacity_) {
return 0;
}
char *newbuf = (char *)std::malloc(n);
if (!newbuf) {
return -1;
}
int sz = size();
if (positive()) {
std::memcpy(newbuf, head_, pSize());
} else {
std::memcpy(newbuf, head_, nRightSize());
std::memcpy(newbuf + nRightSize(), buf_, nLeftSize());
}
release();
buf_ = newbuf;
capacity_ = n;
head_ = buf_;
tail_ = buf_ + sz;
return 0;
}
} // namespace detail
DynamicBuffer::DynamicBuffer(int capacity) {
if (capacity > 0) {
expand(ALIGN(capacity + 1));
}
}
std::string DynamicBuffer::toString() const {
return fmt::format("[@DynamicBuffer {}]", detail::CycleBuffer<1>::toString());
}
FixedBuffer::FixedBuffer(int capacity) {
if (capacity > 0) {
// precise capacity
expand(capacity + 1);
}
}
std::string FixedBuffer::toString() const {
return fmt::format("[@FixedBuffer {}]", detail::CycleBuffer<0>::toString());
}
CycleBufferTest. cpp
// Copyright 2019- <shepherd-lang>
// Apache License Version 2.0
#include "CycleBuffer.h"
#include "catch2/catch.hpp"
#include "fmt/format.h"
#include <algorithm>
#include <cstdlib>
#define C_MIN 0
#define C_MAX 100
#define RAND (rand() % (C_MAX + 1))
TEST_CASE("container/CycleBuffer", "[container/CycleBuffer]") {
SECTION("random read/write") {
{
std::vector<char> v;
DynamicBuffer db;
int rc = 0, wc = 0;
for (int i = C_MIN; i < C_MAX; i++) {
int rn = RAND;
std::vector<char> rbuf(rn);
for (int j = 0; j < rn; j++) {
rbuf[j] = (char)rn;
v.push_back(rbuf[j]);
rc++;
LOG_INFO("DynamicBuffer random rbuf[{}]:{}", rc, (int)rbuf[j]);
}
REQUIRE(db.read(rbuf.data(), rn) == rn);
int wn = std::min(RAND, rn);
std::vector<char> wbuf(wn);
REQUIRE(db.write(wbuf.data(), wn) == wn);
for (int j = 0; j < wn; j++) {
LOG_INFO("DynamicBuffer random wbuf[{}]:{}", wc, (int)wbuf[j]);
if (wbuf[j] != v[wc]) {
LOG_INFO("DynamicBuffer random wbuf[{}]:{}", wc, (int)wbuf[j]); // here's the error place!
}
REQUIRE(wbuf[j] == v[wc]);
wc++;
}
}
}
}
}