Причина сбоя модульного теста C ++ CycleBuffer? - PullRequest
0 голосов
/ 16 июня 2020

Я реализовал свой 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++;
        }
      }
    }
  }
}

...