我编写了这个函数,该函数应该将数据结构写入字节缓冲区,然后返回已写入的数量。该函数执行以下操作:
[array of tiles] + [array of units]
问题 在最后一次迭代中,单元在图块之后进行编码,我收到 *bytes 的缓冲区溢出错误,在使用调试器和 asan 检查后,我在第五个单元编码后收到错误。
我认为我搞乱了写入数据的偏移量的计算,因为写入文件的输出缓冲区已满或垃圾,而不是我想要的数据,但我无法说出出了什么问题,因为这些转换对我来说看起来是正确的。
我期望的是跳转到我需要写入的字节,然后转换成我要写入的数据,然后我使用数据大小进行写入。
更多信息
for (TileSet_Size_t ti = 0; ti < tileset->count; ++ti)
上遇到了相同的错误,但我使用了不正确的铸造和偏移计算,修复后错误移到了突出显示的行上。这就是为什么我认为可能所有偏移计算都是错误的。如果问题不是字节偏移或转换,并且比我想象的更复杂,我将重现一个示例并共享它以进行调试。
#define TILE_ENCODED_SIZE ((sizeof(uint16_t) * 10) + 1)
typedef double Unit_t;
size_t
TileSet_encode (struct TileSet_s *tileset,
bytes_t **bytes, size_t bytes_offset, size_t bytes_size)
{
// next we collect the vertices, duplicated aren't stored so we need to collect
// them and then calculate.
struct UnitSet_s set;
if (!UnitSet_init(&set))
{
return 0;
}
for (TileSet_Size_t ti = 0; ti < tileset->count; ++ti)
{ // translation of this stuff:
// iterate each tile and iterate each vertex of each tile.
for (unsigned short vi = 0; vi < TILE_VERTICES_MAX; ++vi)
{ // dump the vertex into the set, if we fail terminate operation.
if (UnitSet_add(&set, tileset->tilearray[ti]->tiledata.vertices[vi]) == UNIT_SET_NOMEM)
{
UnitSet_destroy(&set);
return 0;
}
}
}
size_t size_tiles = tileset->count * TILE_ENCODED_SIZE;
size_t size_vertices = sizeof(Unit_t) * set.list.length;
size_t bytes_to_write = size_tiles + size_vertices;
if ((bytes_size - bytes_offset) < bytes_to_write)
{ // now we know how much space the whole thing takes.
// ensure to increase space if needed.
bytes_size = (bytes_size - bytes_offset) + bytes_to_write;
bytes_t *newbytes = realloc(*bytes, sizeof(**bytes) * (bytes_offset + bytes_size));
if (!newbytes)
{ // failed to increase bytes buffer.
return 0;
}
*bytes = newbytes;
}
for (TileSet_Size_t ti = 0; ti < tileset->count; ++ti)
{ // encodes tiles.
size_t tile_offset = bytes_offset + (ti * TILE_ENCODED_SIZE);
struct Tile_s *tile = &tileset->tilearray[ti]->tiledata;
*((TileSet_Id_t *)
&((*bytes)[tile_offset])) = tileset->tilearray[ti]->id;
for (size_t vi = 0; vi < TILE_VERTICES_MAX; ++vi)
{
((uint16_t *)
&((*bytes)[tile_offset + 1]))[sizeof(uint16_t) * vi]
= (uint16_t) UnitSet_search(&set.list, tile->vertices[vi], 0, set.list.length - 1);
}
}
for (size_t vi = 0; vi < set.list.length; ++vi)
{ // encode vertices.
// **********************************************************
// ERROR HERE! Buffer Overflow at *bytes after the fifth unit !!
((Unit_t *)
&((*bytes)[bytes_offset + size_tiles]))[sizeof(Unit_t) * vi]
= set.list.array[vi];
// **********************************************************
}
UnitSet_destroy(&set);
return bytes_to_write;
}
我尝试用asan调试它并更改转换。
指针双关很可能会调用未定义的行为,因为您可能违反严格的别名规则。
要将结构打包到 char 缓冲区中,反之亦然,您需要使用
memcpy
typedef struct
{
double d;
char c;
uint64_t u64;
}struct_t;
void *toPackedStream(struct_t *s, void *buff)
{
unsigned char *ucbuff = buff;
memcpy(ucbuff, &s -> d, sizeof(s -> d));
ucbuff += sizeof(s -> d);
*ucbuff++ = s -> c;
memcpy(ucbuff, &s -> u64, sizeof(s -> u64));
return buff;
}
struct_t *fromPackedStream(struct_t *s, void *buff)
{
unsigned char *ucbuff = buff;
memcpy(&s -> d, ucbuff, sizeof(s -> d));
ucbuff += sizeof(s -> d);
s -> c = *ucbuff++;
memcpy(&s -> u64, ucbuff, sizeof(s -> u64));
return s;
}
您可以通过指针字符大小的数据安全访问,但不能通过其他类型访问。