字节向量到整数类型:通过联合移位和添加或隐式转换?
Byte vector to integer type: shift and add or implicit conversion through a union?
我目前正在实施 CBOR 并反复需要从字节数组中读取 1、2、4 或 8 个字节,然后需要将其组合为整数类型 1、2、4 或8 个字节。
对于 4 字节的情况,我目前使用这个模板函数(vec
是我正在读取的字节向量,current_idx
标记了我要开始读取的向量中的位置4 字节):
template<typename T, typename std::enable_if<sizeof(T) == 4, int>::type = 0>
static T get_from_vector(const std::vector<uint8_t>& vec, const size_t current_idx)
{
return static_cast<T>((static_cast<T>(vec[current_idx]) << 030) +
(static_cast<T>(vec[current_idx + 1]) << 020) +
(static_cast<T>(vec[current_idx + 2]) << 010) +
static_cast<T>(vec[current_idx + 3]));
}
(1、2、8字节的情况我分别有3个类似的函数。)
一个示例调用是
std::vector<uint8_t> vec {0x01, 0x00, 0x00, 0xff};
auto num = get_from_vector<uint32_t>(vec, 0);
assert(num == 0x10000FF);
虽然这里的性能似乎不是问题,但我仍然想知道以下代码是否更有效或至少更具可读性:
template<typename T, typename std::enable_if<sizeof(T) == 4, int>::type = 0>
static T get_from_vector(const std::vector<uint8_t>& vec, const size_t current_idx)
{
union U
{
T result_type;
uint8_t bytes[4];
} u;
u.bytes[3] = vec[current_idx];
u.bytes[2] = vec[current_idx + 1];
u.bytes[1] = vec[current_idx + 2];
u.bytes[0] = vec[current_idx + 3];
return u.result_type;
}
对此有什么想法吗?
我个人更喜欢你的第二个选择(使用联合),因为它似乎更快一点,更易读。
但是还有另一种定义函数的方法:使用指针。一个好处是您只需要定义一个函数而不是重载它。
template<typename T>
static T get_from_vector(const std::vector<uint8_t>& vec, const size_t current_index){
T result;
uint8_t *ptr = (uint8_t *) &result;
size_t idx = current_index + sizeof(T);
while(idx > current_index)
*ptr++ = vec[--idx];
return result;
}
改变你的调用示例:
int main(){
std::vector<uint8_t> vec {0x01, 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0xff};
auto byte1 = get_from_vector<uint8_t>(vec, 3);
assert(byte1 == 0xff);
auto byte2 = get_from_vector<uint16_t>(vec, 3);
assert(byte2 == 0xff01);
auto byte4 = get_from_vector<uint32_t>(vec, 4);
assert(byte4 == 0x010000ff);
auto byte8 = get_from_vector<uint64_t>(vec, 0);
assert(byte8 == 0x010000ff010000ffUL);
}
我目前正在实施 CBOR 并反复需要从字节数组中读取 1、2、4 或 8 个字节,然后需要将其组合为整数类型 1、2、4 或8 个字节。
对于 4 字节的情况,我目前使用这个模板函数(vec
是我正在读取的字节向量,current_idx
标记了我要开始读取的向量中的位置4 字节):
template<typename T, typename std::enable_if<sizeof(T) == 4, int>::type = 0>
static T get_from_vector(const std::vector<uint8_t>& vec, const size_t current_idx)
{
return static_cast<T>((static_cast<T>(vec[current_idx]) << 030) +
(static_cast<T>(vec[current_idx + 1]) << 020) +
(static_cast<T>(vec[current_idx + 2]) << 010) +
static_cast<T>(vec[current_idx + 3]));
}
(1、2、8字节的情况我分别有3个类似的函数。)
一个示例调用是
std::vector<uint8_t> vec {0x01, 0x00, 0x00, 0xff};
auto num = get_from_vector<uint32_t>(vec, 0);
assert(num == 0x10000FF);
虽然这里的性能似乎不是问题,但我仍然想知道以下代码是否更有效或至少更具可读性:
template<typename T, typename std::enable_if<sizeof(T) == 4, int>::type = 0>
static T get_from_vector(const std::vector<uint8_t>& vec, const size_t current_idx)
{
union U
{
T result_type;
uint8_t bytes[4];
} u;
u.bytes[3] = vec[current_idx];
u.bytes[2] = vec[current_idx + 1];
u.bytes[1] = vec[current_idx + 2];
u.bytes[0] = vec[current_idx + 3];
return u.result_type;
}
对此有什么想法吗?
我个人更喜欢你的第二个选择(使用联合),因为它似乎更快一点,更易读。
但是还有另一种定义函数的方法:使用指针。一个好处是您只需要定义一个函数而不是重载它。
template<typename T>
static T get_from_vector(const std::vector<uint8_t>& vec, const size_t current_index){
T result;
uint8_t *ptr = (uint8_t *) &result;
size_t idx = current_index + sizeof(T);
while(idx > current_index)
*ptr++ = vec[--idx];
return result;
}
改变你的调用示例:
int main(){
std::vector<uint8_t> vec {0x01, 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0xff};
auto byte1 = get_from_vector<uint8_t>(vec, 3);
assert(byte1 == 0xff);
auto byte2 = get_from_vector<uint16_t>(vec, 3);
assert(byte2 == 0xff01);
auto byte4 = get_from_vector<uint32_t>(vec, 4);
assert(byte4 == 0x010000ff);
auto byte8 = get_from_vector<uint64_t>(vec, 0);
assert(byte8 == 0x010000ff010000ffUL);
}