2025-01-12 19:51:34 +08:00

287 lines
8.0 KiB
C++

// Copyright (c) 2023, AgiBot Inc.
// All rights reserved.
#pragma once
#include <bit>
#include <cinttypes>
#include <cstring>
#include <limits>
#include <span>
#include <stdexcept>
#include <string>
namespace aimrt::common::util {
// Store the uin64_t type as a small terminal in buf
inline void SetBufFromUint64(char *p, uint64_t n) {
p[0] = (char)(n & 0xFF);
p[1] = (char)((n >> 8) & 0xFF);
p[2] = (char)((n >> 16) & 0xFF);
p[3] = (char)((n >> 24) & 0xFF);
p[4] = (char)((n >> 32) & 0xFF);
p[5] = (char)((n >> 40) & 0xFF);
p[6] = (char)((n >> 48) & 0xFF);
p[7] = (char)((n >> 56) & 0xFF);
}
// Retrieve the uin64_t type from buf in the form of a small terminal
inline uint64_t GetUint64FromBuf(const char *p) {
return (uint64_t)((uint8_t)(p[0])) +
((uint64_t)((uint8_t)(p[1])) << 8) +
((uint64_t)((uint8_t)(p[2])) << 16) +
((uint64_t)((uint8_t)(p[3])) << 24) +
((uint64_t)((uint8_t)(p[4])) << 32) +
((uint64_t)((uint8_t)(p[5])) << 40) +
((uint64_t)((uint8_t)(p[6])) << 48) +
((uint64_t)((uint8_t)(p[7])) << 56);
}
// Store the uint32_t type as a small terminal in buf
inline void SetBufFromUint32(char *p, uint32_t n) {
if constexpr (std::endian::native == std::endian::little) {
memcpy(p, &n, 4);
} else {
p[0] = (char)(n & 0xFF);
p[1] = (char)((n >> 8) & 0xFF);
p[2] = (char)((n >> 16) & 0xFF);
p[3] = (char)((n >> 24) & 0xFF);
}
}
// Retrieve the uint32_t type from buf in the form of a small terminal
inline uint32_t GetUint32FromBuf(const char *p) {
if constexpr (std::endian::native == std::endian::little) {
return *((uint32_t *)p);
} else {
return (uint32_t)((uint8_t)(p[0])) +
((uint32_t)((uint8_t)(p[1])) << 8) +
((uint32_t)((uint8_t)(p[2])) << 16) +
((uint32_t)((uint8_t)(p[3])) << 24);
}
}
// Store the uint16_t type as a small terminal in buf
inline void SetBufFromUint16(char *p, uint16_t n) {
if constexpr (std::endian::native == std::endian::little) {
memcpy(p, &n, 2);
} else {
p[0] = (char)(n & 0xFF);
p[1] = (char)((n >> 8) & 0xFF);
}
}
// Retrieve the uint16_t type from buf in the form of a small terminal
inline uint16_t GetUint16FromBuf(const char *p) {
if constexpr (std::endian::native == std::endian::little) {
return *((uint16_t *)p);
} else {
return (uint16_t)((uint8_t)(p[0])) +
((uint16_t)((uint8_t)(p[1])) << 8);
}
}
enum class BufferLenType : size_t {
kUInt8 = 1,
kUInt16 = 2,
kUInt32 = 4,
kUInt64 = 8
};
class BufferOperator {
public:
BufferOperator(char *ptr, size_t len)
: start_(ptr), end_(start_ + len), cur_(start_) {}
~BufferOperator() = default;
BufferOperator(const BufferOperator &) = delete;
BufferOperator &operator=(const BufferOperator &) = delete;
void SetUint8(uint8_t n) {
if (cur_ + sizeof(n) > end_) [[unlikely]]
throw std::runtime_error("Out of bounds when 'SetUint8'.");
*cur_ = n;
cur_ += sizeof(n);
}
void SetUint16(uint16_t n) {
if (cur_ + sizeof(n) > end_) [[unlikely]]
throw std::runtime_error("Out of bounds when 'SetUint16'.");
SetBufFromUint16(cur_, n);
cur_ += sizeof(n);
}
void SetUint32(uint32_t n) {
if (cur_ + sizeof(n) > end_) [[unlikely]]
throw std::runtime_error("Out of bounds when 'SetUint32'.");
SetBufFromUint32(cur_, n);
cur_ += sizeof(n);
}
void SetUint64(uint64_t n) {
if (cur_ + sizeof(n) > end_) [[unlikely]]
throw std::runtime_error("Out of bounds when 'SetUint64'.");
SetBufFromUint64(cur_, n);
cur_ += sizeof(n);
}
void SetBuffer(const char *data, size_t len) {
if (cur_ + len > end_) [[unlikely]]
throw std::runtime_error("Out of bounds when 'SetBuffer'.");
memcpy(cur_, data, len);
cur_ += len;
}
void SetBuffer(std::span<const char> buffer) {
SetBuffer(buffer.data(), buffer.size());
}
void SetString(std::string_view s, BufferLenType len_type = BufferLenType::kUInt64) {
size_t str_len = s.size();
if (cur_ + static_cast<size_t>(len_type) + str_len > end_) [[unlikely]]
throw std::runtime_error("Out of bounds when 'SetString'.");
switch (len_type) {
case BufferLenType::kUInt8:
if (str_len > std::numeric_limits<uint8_t>::max()) [[unlikely]] {
throw std::runtime_error("String length out of range.");
}
SetUint8(static_cast<uint8_t>(str_len));
break;
case BufferLenType::kUInt16:
if (str_len > std::numeric_limits<uint16_t>::max()) [[unlikely]] {
throw std::runtime_error("String length out of range.");
}
SetUint16(static_cast<uint16_t>(str_len));
break;
case BufferLenType::kUInt32:
if (str_len > std::numeric_limits<uint32_t>::max()) [[unlikely]] {
throw std::runtime_error("String length out of range.");
}
SetUint32(static_cast<uint32_t>(str_len));
break;
case BufferLenType::kUInt64:
if (str_len > std::numeric_limits<uint64_t>::max()) [[unlikely]] {
throw std::runtime_error("String length out of range.");
}
SetUint64(static_cast<uint64_t>(str_len));
break;
default:
throw std::runtime_error("Invalid buffer len type.");
}
memcpy(cur_, s.data(), str_len);
cur_ += str_len;
}
size_t GetRemainingSize() const {
return end_ - cur_;
}
private:
char *start_;
char *end_;
char *cur_;
};
class ConstBufferOperator {
public:
ConstBufferOperator(const char *ptr, size_t len)
: start_(ptr), end_(start_ + len), cur_(start_) {}
~ConstBufferOperator() = default;
ConstBufferOperator(const ConstBufferOperator &) = delete;
ConstBufferOperator &operator=(const ConstBufferOperator &) = delete;
uint8_t GetUint8() {
if (cur_ + sizeof(uint8_t) > end_) [[unlikely]]
throw std::runtime_error("Out of bounds when 'GetUint8'.");
uint8_t n = *cur_;
cur_ += sizeof(uint8_t);
return n;
}
uint16_t GetUint16() {
if (cur_ + sizeof(uint16_t) > end_) [[unlikely]]
throw std::runtime_error("Out of bounds when 'GetUint16'.");
uint16_t n = GetUint16FromBuf(cur_);
cur_ += sizeof(uint16_t);
return n;
}
uint32_t GetUint32() {
if (cur_ + sizeof(uint32_t) > end_) [[unlikely]]
throw std::runtime_error("Out of bounds when 'GetUint32'.");
uint32_t n = GetUint32FromBuf(cur_);
cur_ += sizeof(uint32_t);
return n;
}
uint64_t GetUint64() {
if (cur_ + sizeof(uint64_t) > end_) [[unlikely]]
throw std::runtime_error("Out of bounds when 'GetUint64'.");
uint64_t n = GetUint64FromBuf(cur_);
cur_ += sizeof(uint64_t);
return n;
}
std::span<const char> GetBuffer(size_t len) {
if (cur_ + len > end_) [[unlikely]]
throw std::runtime_error("Out of bounds when 'GetBuffer'.");
std::span<const char> buffer(cur_, cur_ + len);
cur_ += len;
return buffer;
}
void GetBuffer(char *dst, size_t len) {
if (cur_ + len > end_) [[unlikely]]
throw std::runtime_error("Out of bounds when 'GetBuffer'.");
memcpy(dst, cur_, len);
cur_ += len;
}
std::string_view GetString(BufferLenType len_type = BufferLenType::kUInt64) {
size_t str_len = 0;
switch (len_type) {
case BufferLenType::kUInt8:
str_len = GetUint8();
break;
case BufferLenType::kUInt16:
str_len = GetUint16();
break;
case BufferLenType::kUInt32:
str_len = GetUint32();
break;
case BufferLenType::kUInt64:
str_len = GetUint64();
break;
default:
throw std::runtime_error("Invalid buffer len type.");
}
if (cur_ + str_len > end_) [[unlikely]]
throw std::runtime_error("Out of bounds when 'GetString'.");
std::string_view s = std::string_view(cur_, str_len);
cur_ += str_len;
return s;
}
size_t GetRemainingSize() const {
return end_ - cur_;
}
std::span<const char> GetRemainingBuffer() {
return GetBuffer(GetRemainingSize());
}
private:
const char *const start_;
const char *const end_;
const char *cur_;
};
} // namespace aimrt::common::util