我一直在研究一种服务器,它希望通过缓冲区接收数据。我有一个像这样定义的对象和一些修改缓冲区的过程:
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_ref
和GC_unref
,这对我的问题没有影响,也看着可能试图使用alloc0
,其中buf被定义为pointer
,但我似乎无法访问该指针的数据,这可能不是我应该首先做的事情。此外,如果我执行var data = p.buf
并传递data
的地址,我会得到不同的结果,但仍然不是预期结果。
所以我想我想要深究的是:
send
使用array[0..4096, int8]
而不是seq[int8]
使用newSeq
初始化后,即使它们看起来包含相同的数据,也能正常工作?答案 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必须清理的两个对象。您可以Packet
为seq[uint8]
设置别名(不含参考号):type Packet* = seq[uint8]
。或者,如果您希望稍后在Packet
内存储更多数据,则可以使用阵列版本。