修改数组与seq的数据,并将数组地址与seq传递给asyncnet proc`send`

时间:2017-08-25 04:31:23

标签: nim

我一直在研究一种服务器,它希望通过缓冲区接收数据。我有一个像这样定义的对象和一些修改缓冲区的过程:

Packet* = ref object
  buf*: seq[int8]
  #buf*: array[0..4096, int8]
  pos*: int

proc newPacket*(size: int): Packet =
  result = Packet(buf: newSeq[int8](size))
  #result = Packet()

proc sendPacket*(s: AsyncSocket, p: Packet) =
  aSyncCheck s.send(addr(p.buf), p.pos)

现在我有两行注释的原因是因为那是我最初使用的代码,但创建一个每次初始化一个包含4096个元素的数组的对象可能对性能不是很好。但是,它可以工作,而seq [int8]版本则没有。

奇怪的是,如果我使用旧的静态缓冲区buf*: array[0..4096, int8],我当前的代码将完全正常工作。在sendPacket中,我确保检查缓冲区中包含的数据,以确保数组和seq [int8]版本相同,并且它们是相同的。 (或者至少看起来像是)。换句话说,如果我要做var p = createPacket(17)并用恰好17个字节写入p.buf,则两个版本中元素的值看起来都是相同的。

因此,尽管两个版本中的数据看起来都相同,但在传递缓冲区地址时调用send时会得到不同的结果。

如果重要,数据将按如下方式读取:

result = p.buf[p.pos]
inc(p.pos)

写得像这样:

p.buf[p.pos] = cast[int8](value)
inc(p.pos)

我调查过的一些事情,可能与我的问题无关:我看了GC_refGC_unref,这对我的问题没有影响,也看着可能试图使用alloc0,其中buf被定义为pointer,但我似乎无法访问该指针的数据,这可能不是我应该首先做的事情。此外,如果我执行var data = p.buf并传递data的地址,我会得到不同的结果,但仍然不是预期结果。

所以我想我想要深究的是:

  1. 为什么send使用array[0..4096, int8]而不是seq[int8]使用newSeq初始化后,即使它们看起来包含相同的数据,也能正常工作?
  2. 我目前用于接收和写入数据的布局是否在Nim(或任何语言)这样的语言中有意义?还有更好的方法吗?

1 个答案:

答案 0 :(得分:1)

为了不初始化数组,您可以像这样使用noinit编译指示:

buf* {.noinit.}: array[0..4096, int8]

您可能正在使用指向seq的指针,而不是指向seq内部数据的指针,因此请尝试使用addr(p.buf[0])

如果您使用的是pos版本,则seq字段无用,因为您已经有p.buf.len,但您可能已经知道并且只是将其留在了数组中。如果您想使用seq并期望大数据包,请务必使用newSeqOfCap仅分配一次内存。

另外,你的数组是1字节太大,从0到4096包括在内!相反,您可以使用[0..4095, int8][4096, int8]

我个人更喜欢在buf中使用uint8类型,这样你就可以输入0到255之间的值而不是-128到127

在ref对象中使用seq意味着在访问buf时有两层间接,以及GC必须清理的两个对象。您可以Packetseq[uint8]设置别名(不含参考号):type Packet* = seq[uint8]。或者,如果您希望稍后在Packet内存储更多数据,则可以使用阵列版本。