【问题标题】:Copying a circular buffer to a larger buffer while preserving contents and modulus indices将循环缓冲区复制到更大的缓冲区,同时保留内容和模数索引
【发布时间】:2016-06-30 22:13:10
【问题描述】:

我将一个有效的循环缓冲区 buf 保留为一个数组和两个总读写计数 bufReadbufWrite 以便 bufRead % buf.lengthbufWrite % buf.length 是当前操作的缓冲区的正确索引。

现在我可能需要在某个时候“增长”数组,因为缓冲区大小会扩大。所以我想用一个新的更大的数组替换buf,但保留缓冲区的所有先前内容同时保留上述模数属性。因此,如果在旧缓冲区中的bufRead % buf.length 我们找到元素X,那么我希望在buf 之后的同一索引bufRead % buf.length 处再次找到这个元素X已更新。

例子:

trait Algorithm {
  var buf: Array[Double]
  var bufRead : Long  // this won't be needed in `resize`
  var bufWrite: Long  // this won't be needed in `resize`

  def resize(newLength: Int): Unit = {
    val newBuf = new Array[Double](newLength)
    ???
    buf = newBuf
  }
}

测试程序:

def test(in: Algorithm): Unit = {
  import in._
  import math.{min, random}
  val avail  = buf.length // (bufWrite - bufRead).toInt
  val data0  = Array.fill(avail)(random)
  val off0   = (bufRead % buf.length).toInt
  val chunk0 = min(avail, buf.length - off0)  
  val off1   = (off0 + chunk0) % buf.length
  val chunk1 = avail - chunk0

  System.arraycopy(data0, 0     , buf, off0, chunk0)
  System.arraycopy(data0, chunk0, buf, off1, chunk1)

  resize(avail * 2)  // arbitrary growth

  val data1  = new Array[Double](avail)
  val off2   = (bufRead % buf.length).toInt
  val chunk2 = min(avail, buf.length - off2)
  val off3   = (off2 + chunk2) % buf.length
  val chunk3 = avail - chunk2
  System.arraycopy(buf, off2, data1, 0     , chunk2)
  System.arraycopy(buf, off3, data1, chunk2, chunk3)

  assert(data0 sameElements data1)
}

【问题讨论】:

  • 你想要a % b = a % c,其中b < c。对我来说似乎不可能。您是否考虑过创建更多相同大小的缓冲区而不是扩大当前缓冲区?你当然需要做一些整理工作来弄清楚哪些缓冲区被推入了,但至少你可以保留你的模数属性。另一种选择是增加可以保存在单个缓冲区位置中的项目数量,但再次与您想要的不同......
  • @wookie919 不!索引可能会因更新而改变,但在更新前后计算的索引处的内容必须保持不变。因此,要保存的内容位于(bufRead until (bufRead + oldLength)) % buf.length

标签: algorithm scala circular-buffer


【解决方案1】:

有两种可能的方法:

  • 重新排序内容,使其适合新的模数

    for (i <- bufRead until bufWrite) {
        newBuf(i % newBuf.length) = buf(i % buf.length)
    }
    
  • 重置readwrite 指针以适应新数组

    var j = 0
    for (i <- bufRead until bufWrite) {
        newBuf(j) = buf(i % buf.length)
        j += 1
    }
    bufWrite -= bufRead
    bufRead = 0
    

我不确定您是否要跟踪缓冲区曾经存储的所有元素的数量,如果是,那么第二种方法当然不起作用。第一种方法,重新排序,不应该太麻烦,因为无论如何您都需要将内容从旧数组复制到新数组中。

【讨论】:

  • 谢谢。是的,第一个版本实际上可以工作。更改指针在我的情况下不起作用,因为它们跟踪传输的总帧数,修改后将变为无效。
【解决方案2】:

我认为以下是正确的:

class Impl(size0: Int) extends Algorithm {
  var buf = new Array[Double](size0)
  var bufRead  = 0L
  var bufWrite = 0L   // unused here

  override def resize(newLength: Int): Unit = {
    import math.{min, max}
    val newBuf    = new Array[Double](newLength)
    val oldLength = buf.length
    val off0      = (bufRead % oldLength).toInt
    val off1      = (bufRead % newLength).toInt
    val chunk0    = min(oldLength - off0, newLength - off1)
    System.arraycopy(buf, off0, newBuf, off1, chunk0)
    val off2      = (off0 + chunk0) % oldLength
    val off3      = (off1 + chunk0) % newLength
    val chunk1    = min(oldLength - max(chunk0, off2), newLength - off3)
    System.arraycopy(buf, off2, newBuf, off3, chunk1)
    val off4      = (off2 + chunk1) % oldLength
    val off5      = (off3 + chunk1) % newLength
    val chunk2    = oldLength - (chunk0 + chunk1)
    System.arraycopy(buf, off4, newBuf, off5, chunk2)
    buf = newBuf
  }
}

测试:

for (r <- 0 until 200) {
  val a = new Impl(100)
  a.bufRead = r   // test all possible offsets
  test(a)
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多