代码抄写自google quic项目,留为己用。
byte_codec.h
#pragma once
#include <algorithm>
#include <cstdint>
#include <type_traits>
namespace base{
enum Endianness {
NETWORK_BYTE_ORDER, // big endian
HOST_BYTE_ORDER // little endian
};
class DataWriter{
public:
// Creates a DataWriter where |buffer| is not owned
// using NETWORK_BYTE_ORDER endianness.
DataWriter(size_t size, char* buffer);
// Creates a DataWriter where |buffer| is not owned
// using the specified endianness.
DataWriter(size_t size, char* buffer, Endianness endianness);
DataWriter(const DataWriter&) = delete;
DataWriter& operator=(const DataWriter&) = delete;
~DataWriter();
// Returns the size of the DataWriter's data.
size_t length() const { return length_; }
// Retrieves the buffer from the DataWriter without changing ownership.
char* data();
// Methods for adding to the payload. These values are appended to the end
// of the DataWriter payload.
// Writes 8/16/32/64-bit unsigned integers.
bool WriteUInt8(uint8_t value);
bool WriteUInt16(uint16_t value);
bool WriteUInt32(uint32_t value);
bool WriteUInt64(uint64_t value);
bool WriteBytes(const void* data, size_t data_len);
// Advance the writer's position for writing by |length| bytes without writing
// anything. This method only makes sense to be used on a buffer that has
// already been written to (and is having certain parts rewritten).
bool Seek(size_t length);
size_t capacity() const { return capacity_; }
size_t remaining() const { return capacity_ - length_; }
protected:
// Returns the location that the data should be written at, or nullptr if
// there is not enough room. Call EndWrite with the returned offset and the
// given length to pad out for the next write.
char* BeginWrite(size_t length);
Endianness endianness() const { return endianness_; }
char* buffer() const { return buffer_; }
void IncreaseLength(size_t delta) {
length_ += delta;
}
private:
// TODO(fkastenholz, b/73004262) change buffer_, et al, to be uint8_t, not
// char.
char* buffer_;
size_t capacity_; // Allocation size of payload (or -1 if buffer is const).
size_t length_; // Current length of the buffer.
// The endianness to write integers and floating numbers.
Endianness endianness_;
};
class DataReader{
public:
// Constructs a reader using NETWORK_BYTE_ORDER endianness.
// Caller must provide an underlying buffer to work on.
DataReader(const char* data, const size_t len);
// Constructs a reader using the specified endianness.
// Caller must provide an underlying buffer to work on.
DataReader(const char* data,
const size_t len,
Endianness endianness);
DataReader(const DataReader&) = delete;
DataReader& operator=(const DataReader&) = delete;
// Empty destructor.
~DataReader() {}
// Reads an 8/16/32/64-bit unsigned integer into the given output
// parameter. Forwards the internal iterator on success. Returns true on
// success, false otherwise.
bool ReadUInt8(uint8_t* result);
bool ReadUInt16(uint16_t* result);
bool ReadUInt32(uint32_t* result);
bool ReadUInt64(uint64_t* result);
// Reads a given number of bytes into the given buffer. The buffer
// must be of adequate size.
// Forwards the internal iterator on success.
// Returns true on success, false otherwise.
bool ReadBytes(void* result, size_t size);
// Skips over |size| bytes from the buffer and forwards the internal iterator.
// Returns true if there are at least |size| bytes remaining to read, false
// otherwise.
bool Seek(size_t size);
// Returns true if the entirety of the underlying buffer has been read via
// Read*() calls.
bool IsDoneReading() const;
// Returns the number of bytes remaining to be read.
size_t BytesRemaining() const;
// DOES NOT forward the internal iterator.
uint8_t PeekByte() const;
protected:
// Returns true if the underlying buffer has enough room to read the given
// amount of bytes.
bool CanRead(size_t bytes) const;
// To be called when a read fails for any reason.
void OnFailure();
const char* data() const { return data_; }
size_t pos() const { return pos_; }
void AdvancePos(size_t amount) {
pos_ += amount;
}
Endianness endianness() const { return endianness_; }
private:
// TODO(fkastenholz, b/73004262) change buffer_, et al, to be uint8_t, not
// char. The data buffer that we're reading from.
const char* data_;
// The length of the data buffer that we're reading from.
size_t len_;
// The location of the next read from our data buffer.
size_t pos_;
// The endianness to read integers and floating numbers.
Endianness endianness_;
};
}
byte_codec.cc
#include <assert.h>
#include <memory.h>
#include "byte_codec.h"
namespace base{
// Provide utility functions that convert from/to network order (big endian)
// to/from host order (little endian).
class EndianUtil{
public:
// Convert |x| from host order (little endian) to network order (big endian).
#if defined(__clang__) || \
(defined(__GNUC__) && \
((__GNUC__ == 4 && __GNUC_MINOR__ >= 8) || __GNUC__ >= 5))
static uint16_t HostToNet16(uint16_t x) { return __builtin_bswap16(x); }
static uint32_t HostToNet32(uint32_t x) { return __builtin_bswap32(x); }
static uint64_t HostToNet64(uint64_t x) { return __builtin_bswap64(x); }
#else
static uint16_t HostToNet16(uint16_t x) { return PortableByteSwap(x); }
static uint32_t HostToNet32(uint32_t x) { return PortableByteSwap(x); }
static uint64_t HostToNet64(uint64_t x) { return PortableByteSwap(x); }
#endif
// Convert |x| from network order (big endian) to host order (little endian).
static uint16_t NetToHost16(uint16_t x) { return HostToNet16(x); }
static uint32_t NetToHost32(uint32_t x) { return HostToNet32(x); }
static uint64_t NetToHost64(uint64_t x) { return HostToNet64(x); }
// Left public for tests.
template <typename T>
static T PortableByteSwap(T input) {
static_assert(std::is_unsigned<T>::value, "T has to be uintNN_t");
union {
T number;
char bytes[sizeof(T)];
} value;
value.number = input;
std::reverse(std::begin(value.bytes), std::end(value.bytes));
return value.number;
}
};
DataWriter::DataWriter(size_t size, char* buffer)
: DataWriter(size, buffer, NETWORK_BYTE_ORDER) {}
DataWriter::DataWriter(size_t size,
char* buffer,
Endianness endianness)
: buffer_(buffer), capacity_(size), length_(0), endianness_(endianness) {}
DataWriter::~DataWriter() {}
char* DataWriter::data() {
return buffer_;
}
bool DataWriter::WriteUInt8(uint8_t value) {
return WriteBytes(&value, sizeof(value));
}
bool DataWriter::WriteUInt16(uint16_t value) {
if (endianness_ ==NETWORK_BYTE_ORDER) {
value = EndianUtil::HostToNet16(value);
}
return WriteBytes(&value, sizeof(value));
}
bool DataWriter::WriteUInt32(uint32_t value) {
if (endianness_ ==NETWORK_BYTE_ORDER) {
value =EndianUtil::HostToNet32(value);
}
return WriteBytes(&value, sizeof(value));
}
bool DataWriter::WriteUInt64(uint64_t value) {
if (endianness_ ==NETWORK_BYTE_ORDER) {
value = EndianUtil::HostToNet64(value);
}
return WriteBytes(&value, sizeof(value));
}
char* DataWriter::BeginWrite(size_t length) {
if (length_ > capacity_) {
return nullptr;
}
if (capacity_ - length_ < length) {
return nullptr;
}
return buffer_ + length_;
}
bool DataWriter::WriteBytes(const void* data, size_t data_len) {
char* dest = BeginWrite(data_len);
if (!dest) {
return false;
}
memcpy(dest, data, data_len);
length_ += data_len;
return true;
}
bool DataWriter::Seek(size_t length) {
if (!BeginWrite(length)) {
return false;
}
length_ += length;
return true;
}
DataReader::DataReader(const char* data, const size_t len)
: DataReader(data, len, NETWORK_BYTE_ORDER) {}
DataReader::DataReader(const char* data,
const size_t len,
Endianness endianness)
: data_(data), len_(len), pos_(0), endianness_(endianness) {}
bool DataReader::ReadUInt8(uint8_t* result) {
return ReadBytes(result, sizeof(*result));
}
bool DataReader::ReadUInt16(uint16_t* result) {
if (!ReadBytes(result, sizeof(*result))) {
return false;
}
if (endianness_ ==NETWORK_BYTE_ORDER) {
*result = EndianUtil::NetToHost16(*result);
}
return true;
}
bool DataReader::ReadUInt32(uint32_t* result) {
if (!ReadBytes(result, sizeof(*result))) {
return false;
}
if (endianness_ ==NETWORK_BYTE_ORDER) {
*result = EndianUtil::NetToHost32(*result);
}
return true;
}
bool DataReader::ReadUInt64(uint64_t* result) {
if (!ReadBytes(result, sizeof(*result))) {
return false;
}
if (endianness_ ==NETWORK_BYTE_ORDER) {
*result =EndianUtil::NetToHost64(*result);
}
return true;
}
bool DataReader::ReadBytes(void* result, size_t size) {
// Make sure that we have enough data to read.
if (!CanRead(size)) {
OnFailure();
return false;
}
// Read into result.
memcpy(result, data_ + pos_, size);
// Iterate.
pos_ += size;
return true;
}
bool DataReader::Seek(size_t size) {
if (!CanRead(size)) {
OnFailure();
return false;
}
pos_ += size;
return true;
}
bool DataReader::IsDoneReading() const {
return len_ == pos_;
}
size_t DataReader::BytesRemaining() const {
return len_ - pos_;
}
bool DataReader::CanRead(size_t bytes) const {
return bytes <= (len_ - pos_);
}
void DataReader::OnFailure() {
// Set our iterator to the end of the buffer so that further reads fail
// immediately.
pos_ = len_;
}
uint8_t DataReader::PeekByte() const {
if (pos_ >= len_) {
assert(0);
return 0;
}
return data_[pos_];
}
}
测试例子examples.cc
#include <iostream>
#include <memory.h>
#include "byte_codec.h"
using namespace std;
int main()
{
char buf[1500];
uint8_t a=1;
uint16_t b=1234;
uint32_t c=43217;
uint64_t d=1234567890;
double e=123.44;
uint64_t double_holder=0;
memcpy(&double_holder,&e,sizeof(double));
base::DataWriter w(1500,buf,base::NETWORK_BYTE_ORDER);
w.WriteUInt8(a);
w.WriteUInt16(b);
w.WriteUInt32(c);
w.WriteUInt64(d);
w.WriteUInt64(double_holder);
uint32_t occupied=w.length();
base::DataReader r(buf,occupied,base::NETWORK_BYTE_ORDER);
uint8_t a1=0;
r.ReadUInt8(&a1);
std::cout<<std::to_string(a1)<<std::endl;
uint16_t b1=1;
r.ReadUInt16(&b1);
std::cout<<"b1 "<<(b1)<<std::endl;
uint32_t c1=1;
r.ReadUInt32(&c1);
std::cout<<"c1 "<<c1<<std::endl;
uint64_t d1=0;
r.ReadUInt64(&d1);
std::cout<<"d1 "<<d1<<std::endl;
uint64_t double_holder1=0;
r.ReadUInt64(&double_holder1);
double e1=0.0;
memcpy(&e1,&double_holder1,sizeof(double));
printf("test %f",e1);
return 0;
}