我想知道是否可以按位而不是字节来做 memcpy?
我正在为带有 VLAN 标记的以太网帧编写 C 代码,其中我需要为 VLAN 标头属性(PCP-3bits、DEI-1bit、VID-12bits)填充不同的值。
我怎样才能对这些位进行 memcpy,或者以任何其他方式将值填充到这些位中的属性。
提前致谢 !
不。位是不可寻址的(这意味着无法直接从内存中读取它们,只能读取它们。它们没有地址。只有字节有地址)。
您需要读取包含您感兴趣的位的字节或字并自己进行屏蔽。
我已经为客户端完成了帧的 802.1Q VLAN 标记,他们只有 802.3 以太网帧,但由于安装了新的 VLAN 感知交换机,因此希望迁移到 802.1Q。
首先,您不能复制位。我们已经使用 memcpy 以字节为单位复制了标签。
插图(有关字段的描述,请参阅 Wikipedia):-
VLAN 标记 = 4 字节;由 TPID(2 字节)和 TCI(2 字节)组成。
TPID 很简单,始终为 0x8100,表示带有 VLAN 标记的帧。
TCI由PCP-3bits、DEI-1bit、VID-12bits组成。将 TCI 分解为半字节,即 4 位。默认情况下,半字节(PCP+DEI) = 0x0 假设优先级被禁用并且 DEI = 0。剩余的 3 个半字节(12 位)用于 VLAN-ID 本身。假设您想为 VLAN-ID = 123 标记一个帧。在十六进制中,这将是 = 0x07B。
将半字节组合在一起,您就有了 2 字节的 TCI 字段,现在可以视为 0x007B。
然后,您可以执行以下操作。(代码未编译)
unsigned short int vlanTPID, vlanTCI;
unsigned char *dest, *src;
// Set the VLAN Tag
vlanTPID = 0x8100;
vlanTCI = 0x007B;
// Pointer to the TPID position of ethernet frame
dest = &vlanTagPosition;
src = &vlanTPID;
memcpy(dest, src, sizeof(vlanTPID));
// Increment dest pointer by 2 bytes to insert TCI in the ethernet frame
dest += 2;
src = &vlanTCI;
memcpy(dest, src, sizeof(vlanTCI));
如果需要填充字段,可以使用带有 的C位字段struct
,如下所示:
struct box_props {
unsigned first : 1;
unsigned second : 3;
unsigned : 4;
};
例如,其中1
表示该字段的长度为 1 位。最后一个(未命名的)字段表示:4 位填充。
定义它并读取字段struct
,memcpy
就好像它们在哪里一样unsigned
。写作也一样。
注意:始终填充到整数字节,否则memcpy
可能会产生不良影响。
只是实现复制任何任意位集。最后一个实现是tacklelib
库的一部分:
https://github.com/andry81/tacklelib
https://github.com/andry81/tacklelib/blob/trunk/include/tacklelib/utility/memory.hpp
优点:
memcpy
在可以推断的情况下动态使用函数。缺点:
// Bitwise memory copy.
// Both buffers must be padded to 7 bytes remainder to be able to read/write the last 8-bit block as 64-bit block.
// Buffers must not overlap.
//
FORCE_INLINE void memcpy_bitwise64(uint8_t * to_padded_int64_buf, uint64_t to_first_bit_offset, uint8_t * from_padded_int64_buf, uint64_t from_first_bit_offset, uint64_t bit_size)
{
assert(bit_size);
uint64_t bit_offset = 0;
uint32_t from_byte_offset = uint32_t(from_first_bit_offset / 8);
uint32_t to_byte_offset = uint32_t(to_first_bit_offset / 8);
uint32_t remainder_from_bit_offset = uint32_t(from_first_bit_offset % 8);
uint32_t remainder_to_bit_offset = uint32_t(to_first_bit_offset % 8);
while (bit_offset < bit_size) {
if (remainder_to_bit_offset >= remainder_from_bit_offset && (remainder_to_bit_offset || remainder_from_bit_offset)) {
const uint64_t from_bit_block = *(uint64_t *)&from_padded_int64_buf[from_byte_offset];
uint64_t & to_bit_block = *(uint64_t *)&to_padded_int64_buf[to_byte_offset];
const uint32_t to_first_bit_delta_offset = remainder_to_bit_offset - remainder_from_bit_offset;
const uint64_t to_bit_block_inversed_mask = uint64_t(~0) << remainder_to_bit_offset;
to_bit_block = ((from_bit_block << to_first_bit_delta_offset) & to_bit_block_inversed_mask) | (to_bit_block & ~to_bit_block_inversed_mask);
const uint32_t bit_size_copied = 64 - remainder_to_bit_offset;
bit_offset += bit_size_copied;
from_first_bit_offset += bit_size_copied;
to_first_bit_offset += bit_size_copied;
if (remainder_to_bit_offset != remainder_from_bit_offset) {
from_byte_offset += 7;
to_byte_offset += 8;
remainder_from_bit_offset = 8 - to_first_bit_delta_offset;
remainder_to_bit_offset = 0;
}
else {
from_byte_offset += 8;
to_byte_offset += 8;
remainder_from_bit_offset = 0;
remainder_to_bit_offset = 0;
}
}
else if (remainder_to_bit_offset < remainder_from_bit_offset) {
const uint64_t from_bit_block = *(uint64_t *)&from_padded_int64_buf[from_byte_offset];
uint64_t & to_bit_block = *(uint64_t *)&to_padded_int64_buf[to_byte_offset];
const uint32_t to_first_bit_delta_offset = remainder_from_bit_offset - remainder_to_bit_offset;
const uint64_t to_bit_block_inversed_mask = uint64_t(~0) << remainder_to_bit_offset;
to_bit_block = ((from_bit_block >> to_first_bit_delta_offset) & to_bit_block_inversed_mask) | (to_bit_block & ~to_bit_block_inversed_mask);
const uint32_t bit_size_copied = 64 - remainder_from_bit_offset;
bit_offset += bit_size_copied;
from_first_bit_offset += bit_size_copied;
to_first_bit_offset += bit_size_copied;
from_byte_offset += 8;
to_byte_offset += 7;
remainder_from_bit_offset = 0;
remainder_to_bit_offset = (8 - to_first_bit_delta_offset);
}
// optimization
else {
const uint64_t bit_size_remain = bit_size - bit_offset;
const uint32_t byte_size_remain = uint32_t(bit_size_remain / 8);
if (byte_size_remain + 1 > 8) {
memcpy(to_padded_int64_buf + to_byte_offset, from_padded_int64_buf + from_byte_offset, byte_size_remain + 1);
}
// optimization
else {
*(uint64_t *)&to_padded_int64_buf[to_byte_offset] = *(uint64_t *)&from_padded_int64_buf[from_byte_offset];
}
break;
}
assert(from_byte_offset == uint32_t(from_first_bit_offset / 8));
assert(remainder_from_bit_offset == uint32_t(from_first_bit_offset % 8));
assert(to_byte_offset == uint32_t(to_first_bit_offset / 8));
assert(remainder_to_bit_offset == uint32_t(to_first_bit_offset % 8));
}
}