草庐IT

c++ - 实现循环缓冲区以在一次调用中写入/读取任意数量的数据

coder 2024-02-01 原文

大多数循环缓冲区假设每次只读/写一个对象,我发现以 (const char *bytes, size_t byte_count) 形式对二进制数据进行操作的唯一链接是 http://www.asawicki.info/news_1468_circular_buffer_of_raw_binary_data_in_c.html ,我觉得不对,而且有点长。什么是正确的实现方式?

我自己创建了一个。但它仍然很长。谁能分享一个更优雅的版本?或者您能指出我的代码有什么可以改进的地方吗?

class Pipe{
    Pipe(size_t capacity): _capacity(capacity){ init();  }
    ~Pipe(){delete [] _buf; }
    size_t read(char* data, size_t bytes);
    size_t write(const char* data, size_t bytes);
   private: 
    //only _capacity-1 is used, one is to identify full or empty.
    void init(){_buf = new char[_capacity]; 
     _wptr = 0; _rptr = 0; _used_size = 0; 
    }
    char* _buf;
    size_t _capacity, _wptr, _rptr, _used_size;
    bool isFull(){return (_wptr + 1 ) % (_capacity) == _rptr;} 
    bool isEmpty(){return _wptr == _rptr;} 
}; 
size_t Pipe::read(char* data, size_t bytes){
    if (isEmpty() || bytes == 0) return 0;
    size_t bytes_read1 = 0, bytes_read2 = 0;
    if (_rptr>=_wptr+1) { //two piece can be read
        bytes_read1 = min(bytes, _capacity - _rptr);
        memcpy(data, _buf + _rptr, bytes_read1);
        _rptr += bytes_read1;
        bytes -= bytes_read1;
        if (_rptr == _capacity) _rptr = 0;
        if (bytes > 0){
        bytes_read2 = min(bytes, _wptr);
            memcpy(_buf + _rptr, data, bytes_read2);
            _rptr += bytes_read2;
            bytes -= bytes_read2;
        }
    }
    else{//one piece can be read
    bytes_read1 = min(bytes, _wptr - _rptr); 
    memcpy(_buf + _wptr, data, bytes_read1);
    _rptr += bytes_read1;
    bytes -= bytes_read1;
    }
    return bytes_read1 + bytes_read2; 
}

size_t Pipe::write(const char* data, size_t bytes){
    if (isFull() || bytes == 0) return 0;
    size_t bytes_write1 = 0, bytes_write2 = 0;
    if (_wptr>=_rptr) { //two piece can be written
        bytes_write1 = min(bytes, _capacity - _wptr); 
        memcpy(_buf + _wptr, data, bytes_write1);
        _wptr += bytes_write1;
        bytes -= bytes_write1;
        if (_wptr == _capacity) _wptr = 0;
        if (bytes > 0){ //_wptr must be 0 here.
            bytes_write2 = min(bytes, _rptr-1);//-1 bcz there is one     
    slot to check empty/full
            memcpy(_buf + _wptr, data+ bytes_write1, bytes_write2);
            _wptr += bytes_write2;
            bytes -= bytes_write2;
        }
    }
    else{ //one piece can be written
        bytes_write1 = min(bytes, _rptr - _wptr -1); 
        memcpy(_buf + _wptr, data, bytes_write1);
        _wptr += bytes_write1;
        bytes -= bytes_write1;
    }
    return bytes_write1 + bytes_write2; 
}

最佳答案

OP 中的代码可以通过排除所有条件来简化。保留原始接口(interface)和用于实现的memcpy(只有constructor/destructor/read/write公开,未使用的_used_size可能会被丢弃)。

size_t Pipe::read(char* data, size_t bytes)
{
    bytes = min(bytes, getUsed());
    const size_t bytes_read1 = min(bytes, _capacity - _rptr);
    memcpy(data, _buf + _rptr, bytes_read1);
    memcpy(data + bytes_read1, _buf, bytes - bytes_read1);
    updateIndex(_rptr, bytes);
    return bytes;
}

size_t Pipe::write(const char* data, size_t bytes)
{
    bytes = min(bytes, getFree());
    const size_t bytes_write1 = min(bytes, _capacity - _wptr); 
    memcpy(_buf + _wptr, data, bytes_write1);
    memcpy(_buf, data + bytes_write1, bytes - bytes_write1);
    updateIndex(_wptr, bytes);
    return bytes;
}

这里使用的几个私有(private)方法可能有这个简单的实现:

size_t Pipe::getUsed()
{ return (_capacity - _rptr + _wptr) % _capacity; }

size_t Pipe::getFree()
{ return (_capacity - 1 - _wptr + _rptr) % _capacity; }

void Pipe::updateIndex(size_t& index, size_t bytes)
{ index = (index + bytes) % _capacity; }

此实现有一个缺点:当 _capacity 接近最大 size_t 值(因为溢出)时它会被破坏。这可以通过在免费/使用的计算和索引更新中用条件替换模数来解决。以下是对 read 中使用的方法的修改:

size_t Pipe::getUsed()
{
    if (_wptr >= _rptr)
        return _wptr - _rptr;
    else
        return _capacity - _rptr + _wptr;
}

void Pipe::updateIndex(size_t& index, size_t bytes)
{
    if (bytes >= _capacity - index)
        index = index + bytes - _capacity;
    else
        index = index + bytes;
}

关于c++ - 实现循环缓冲区以在一次调用中写入/读取任意数量的数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21245256/

有关c++ - 实现循环缓冲区以在一次调用中写入/读取任意数量的数据的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby - 树顶语法无限循环 - 2

    我脑子里浮现出一些关于一种新编程语言的想法,所以我想我会尝试实现它。一位friend建议我尝试使用Treetop(Rubygem)来创建一个解析器。Treetop的文档很少,我以前从未做过这种事情。我的解析器表现得好像有一个无限循环,但没有堆栈跟踪;事实证明很难追踪到。有人可以指出入门级解析/AST指南的方向吗?我真的需要一些列出规则、常见用法等的东西来使用像Treetop这样的工具。我的语法分析器在GitHub上,以防有人希望帮助我改进它。class{initialize=lambda(name){receiver.name=name}greet=lambda{IO.puts("He

  3. ruby-on-rails - 在 Ruby 中循环遍历多个数组 - 2

    我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代

  4. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  5. Ruby 写入和读取对象到文件 - 2

    好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信

  6. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  7. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  8. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  9. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  10. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

随机推荐