reticulum-cpp/src/Bytes.h
attermann f9a679ffc2 WIP: Initial implementation of persistence
Persistence implemented with the help of the ArduinoJson library using
MessagePack format.
2023-12-05 11:06:49 -07:00

273 lines
8.1 KiB
C++

#pragma once
#include "Log.h"
#include <ArduinoJson.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <vector>
#include <string>
#include <memory>
namespace RNS {
#define COW
class Bytes {
private:
//typedef std::vector<uint8_t> Data;
using Data = std::vector<uint8_t>;
//typedef std::shared_ptr<Data> SharedData;
using SharedData = std::shared_ptr<Data>;
public:
enum NoneConstructor {
NONE
};
public:
Bytes() {
//mem("Bytes object created from default, this: " + std::to_string((uintptr_t)this) + ", data: " + std::to_string((unsigned long)_data.get()));
}
Bytes(const NoneConstructor none) {
//mem("Bytes object created from NONE, this: " + std::to_string((uintptr_t)this) + ", data: " + std::to_string((unsigned long)_data.get()));
}
Bytes(const Bytes& bytes) {
//mem("Bytes is using shared data");
assign(bytes);
//mem("Bytes object copy created from bytes \"" + toString() + "\", this: " + std::to_string((uintptr_t)this) + ", data: " + std::to_string((unsigned long)_data.get()));
}
Bytes(const uint8_t* chunk, size_t size) {
assign(chunk, size);
//mem(std::string("Bytes object created from chunk \"") + toString() + "\", this: " + std::to_string((uintptr_t)this) + ", data: " + std::to_string((unsigned long)_data.get()));
}
Bytes(const char* string) {
assign(string);
//mem(std::string("Bytes object created from string \"") + toString() + "\", this: " + std::to_string((uintptr_t)this) + ", data: " + std::to_string((unsigned long)_data.get()));
}
Bytes(const std::string& string) {
assign(string);
//mem(std::string("Bytes object created from std::string \"") + toString() + "\", this: " + std::to_string((uintptr_t)this) + ", data: " + std::to_string((unsigned long)_data.get()));
}
virtual ~Bytes() {
//mem(std::string("Bytes object destroyed \"") + toString() + "\", this: " + std::to_string((uintptr_t)this) + ", data: " + std::to_string((unsigned long)_data.get()));
}
inline const Bytes& operator = (const Bytes& bytes) {
assign(bytes);
return *this;
}
inline const Bytes& operator += (const Bytes& bytes) {
append(bytes);
return *this;
}
inline Bytes operator + (const Bytes& bytes) const {
Bytes newbytes(*this);
newbytes.append(bytes);
return newbytes;
}
inline bool operator == (const Bytes& bytes) const {
return compare(bytes) == 0;
}
inline bool operator != (const Bytes& bytes) const {
return compare(bytes) != 0;
}
inline bool operator < (const Bytes& bytes) const {
return compare(bytes) < 0;
}
inline bool operator > (const Bytes& bytes) const {
return compare(bytes) > 0;
}
inline operator bool() const {
return (_data && !_data->empty());
}
private:
inline SharedData shareData() const {
//mem("Bytes is sharing its own data");
_owner = false;
return _data;
}
inline void newData(size_t size = 0) {
//mem("Bytes is creating its own data");
if (size > 0) {
_data = SharedData(new Data(size));
}
else {
_data = SharedData(new Data());
}
_owner = true;
}
void ownData();
public:
inline void clear() {
_data = nullptr;
_owner = true;
}
inline void assign(const Bytes& bytes) {
#ifdef COW
_data = bytes.shareData();
_owner = false;
#else
// if assignment is empty then clear data and don't bother creating new
if (bytes.size() <= 0) {
_data = nullptr;
_owner = true;
return;
}
newData();
_data->insert(_data->begin(), bytes._data->begin(), bytes._data->end());
#endif
}
inline void assign(const uint8_t* chunk, size_t size) {
// if assignment is empty then clear data and don't bother creating new
if (chunk == nullptr || size <= 0) {
_data = nullptr;
_owner = true;
return;
}
newData();
_data->insert(_data->begin(), chunk, chunk + size);
}
inline void assign(const char* string) {
// if assignment is empty then clear data and don't bother creating new
if (string == nullptr || string[0] == 0) {
_data = nullptr;
_owner = true;
return;
}
newData();
_data->insert(_data->begin(), (uint8_t* )string, (uint8_t* )string + strlen(string));
}
inline void assign(const std::string& string) { assign(string.c_str()); }
void assignHex(const char* hex);
inline void append(const Bytes& bytes) {
// if append is empty then do nothing
if (bytes.size() <= 0) {
return;
}
ownData();
_data->insert(_data->end(), bytes._data->begin(), bytes._data->end());
}
inline void append(const uint8_t* chunk, size_t size) {
// if append is empty then do nothing
if (chunk == nullptr || size <= 0) {
return;
}
ownData();
_data->insert(_data->end(), chunk, chunk + size);
}
inline void append(const char* string) {
// if append is empty then do nothing
if (string == nullptr || string[0] == 0) {
return;
}
ownData();
_data->insert(_data->end(), (uint8_t* )string, (uint8_t* )string + strlen(string));
}
inline void append(const std::string& string) { append(string.c_str()); }
inline void append(uint8_t byte) {
ownData();
_data->push_back(byte);
}
void appendHex(const char* hex);
inline void resize(size_t newsize) {
// if size is unchanged then do nothing
if (newsize == size()) {
return;
}
ownData();
_data->resize(newsize);
}
inline uint8_t* writable(size_t size) {
newData(size);
return _data->data();
}
public:
int8_t compare(const Bytes& bytes) const;
inline size_t size() const { if (!_data) return 0; return _data->size(); }
inline bool empty() const { if (!_data) return true; return _data->empty(); }
inline size_t capacity() const { if (!_data) return 0; return _data->capacity(); }
inline const uint8_t* data() const { if (!_data) return nullptr; return _data->data(); }
inline std::string toString() const { if (!_data) return ""; return {(const char*)data(), size()}; }
std::string toHex(bool upper = true) const;
Bytes mid(size_t beginpos, size_t len) const;
Bytes mid(size_t beginpos) const;
inline Bytes left(size_t len) const { if (!_data) return NONE; if (len > size()) len = size(); return {data(), len}; }
inline Bytes right(size_t len) const { if (!_data) return NONE; if (len > size()) len = size(); return {data() + (size() - len), len}; }
// Python array indexing
// [8:16]
// pos 8 to pos 16
// mid(8, 8)
// [:16]
// start to pos 16 (same as first 16)
// left(16)
// [16:]
// pos 16 to end
// mid(16)
// [-16:]
// last 16
// right(16)
// [:-16]
// all except the last 16
// left(size()-16)
// mid(0, size()-16)
// [-1]
// last element
// [-2]
// second to last element
private:
SharedData _data;
mutable bool _owner = true;
};
// following array function doesn't work without size since it's past as a pointer to the array sizeof() is of the pointer
//inline Bytes bytesFromArray(const uint8_t arr[]) { return Bytes(arr, sizeof(arr)); }
//inline Bytes bytesFromChunk(const uint8_t* ptr, size_t len) { return Bytes(ptr, len); }
inline Bytes bytesFromChunk(const uint8_t* ptr, size_t len) { return {ptr, len}; }
//inline Bytes bytesFromString(const char* str) { return Bytes((uint8_t*)str, strlen(str)); }
inline Bytes bytesFromString(const char* str) { return {(uint8_t*)str, strlen(str)}; }
//z inline Bytes bytesFromInt(const int) { return {(uint8_t*)str, strlen(str)}; }
inline std::string stringFromBytes(const Bytes& bytes) { return bytes.toString(); }
inline std::string hexFromBytes(const Bytes& bytes) { return bytes.toHex(); }
std::string hexFromByte(uint8_t byte, bool upper = true);
}
inline RNS::Bytes& operator << (RNS::Bytes& lhbytes, const RNS::Bytes& rhbytes) {
lhbytes.append(rhbytes);
return lhbytes;
}
inline RNS::Bytes& operator << (RNS::Bytes& lhbytes, uint8_t rhbyte) {
lhbytes.append(rhbyte);
return lhbytes;
}
namespace ArduinoJson {
inline bool convertToJson(const RNS::Bytes& src, JsonVariant dst) {
return dst.set(src.toHex());
}
inline void convertFromJson(JsonVariantConst src, RNS::Bytes& dst) {
dst.assignHex(src.as<const char*>());
}
inline bool canConvertFromJson(JsonVariantConst src, const RNS::Bytes&) {
return src.is<const char*>();
}
}