v1.0.0: Boundary mode with bidirectional LoRa↔TCP transport
Vendor microReticulum library with boundary mode transport fixes: - Two-whitelist system gates backbone traffic (local addresses + mentioned addresses from local devices) - Allow control_hashes and local destinations through boundary filter (fixes backbone→LoRa path discovery) - Fix get_cached_packet() to call unpack() instead of update_hash() (fixes empty destination_hash in path responses) - LRPROOF Identity::recall null guard - remaining_hops HEADER_1/BROADCAST fix for final-hop delivery - PROOF packets excluded from boundary wrapping - Iterator invalidation fix in transport table cleanup - is_backbone flag replaces string matching for interface identification Firmware changes: - Set is_backbone(true) on backbone TCP interface - Rename default TcpInterface name to BackboneInterface - Update comments for dual-use TcpInterface (backbone + local AP) - Use vendored lib/microReticulum instead of PlatformIO registry
This commit is contained in:
16
lib/microReticulum/src/Utilities/Crc.cpp
Executable file
16
lib/microReticulum/src/Utilities/Crc.cpp
Executable file
@@ -0,0 +1,16 @@
|
||||
#include "Crc.h"
|
||||
|
||||
using namespace RNS::Utilities;
|
||||
|
||||
/*static*/ uint32_t Crc::crc32(uint32_t crc, const uint8_t* buf, size_t size) {
|
||||
const unsigned char *data = (const unsigned char *)buf;
|
||||
if (data == NULL)
|
||||
return 0;
|
||||
crc ^= 0xffffffff;
|
||||
while (size--) {
|
||||
crc ^= *data++;
|
||||
for (unsigned k = 0; k < 8; k++)
|
||||
crc = crc & 1 ? (crc >> 1) ^ 0xedb88320 : crc >> 1;
|
||||
}
|
||||
return crc ^ 0xffffffff;
|
||||
}
|
||||
17
lib/microReticulum/src/Utilities/Crc.h
Executable file
17
lib/microReticulum/src/Utilities/Crc.h
Executable file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace RNS { namespace Utilities {
|
||||
|
||||
class Crc {
|
||||
|
||||
public:
|
||||
static uint32_t crc32(uint32_t crc, const uint8_t* buffer, size_t size);
|
||||
static inline uint32_t crc32(uint32_t crc, uint8_t byte) { return crc32(crc, &byte, sizeof(byte)); }
|
||||
static inline uint32_t crc32(uint32_t crc, const char* str) { return crc32(crc, (const uint8_t*)str, strlen(str)); }
|
||||
|
||||
};
|
||||
|
||||
} }
|
||||
289
lib/microReticulum/src/Utilities/OS.cpp
Executable file
289
lib/microReticulum/src/Utilities/OS.cpp
Executable file
@@ -0,0 +1,289 @@
|
||||
#include "OS.h"
|
||||
|
||||
#include "../Type.h"
|
||||
#include "../Log.h"
|
||||
|
||||
using namespace RNS;
|
||||
using namespace RNS::Utilities;
|
||||
|
||||
|
||||
#if defined(RNS_USE_ALLOCATOR)
|
||||
|
||||
#if defined(RNS_USE_TLSF)
|
||||
#if defined(ESP32)
|
||||
//#define BUFFER_SIZE 1024 * 80
|
||||
#define BUFFER_SIZE 0
|
||||
#define BUFFER_FRACTION 0.8
|
||||
#elif defined(ARDUINO_ARCH_NRF52) || defined(ARDUINO_NRF52_ADAFRUIT)
|
||||
//#define BUFFER_SIZE 1024 * 80
|
||||
#define BUFFER_SIZE 0
|
||||
#define BUFFER_FRACTION 0.8
|
||||
#else
|
||||
#define BUFFER_SIZE 1024 * 1000
|
||||
#define BUFFER_FRACTION 0
|
||||
#endif
|
||||
|
||||
bool _tlsf_init = false;
|
||||
//char _tlsf_msg[256] = "";
|
||||
size_t _buffer_size = BUFFER_SIZE;
|
||||
size_t _contiguous_size = 0;
|
||||
|
||||
/*static*/ //tlsf_t OS::_tlsf = tlsf_create_with_pool(malloc(1024 * 1024), 1024 * 1024);
|
||||
/*static*/ tlsf_t OS::_tlsf = nullptr;
|
||||
#endif
|
||||
|
||||
uint32_t _new_count = 0;
|
||||
uint32_t _new_fault = 0;
|
||||
uint64_t _new_size = 0;
|
||||
uint32_t _delete_count = 0;
|
||||
uint32_t _delete_fault = 0;
|
||||
size_t _min_size = 0;
|
||||
size_t _max_size = 0;
|
||||
|
||||
// CBA Added attribute weak to avoid collision with new override on nrf52
|
||||
void* operator new(size_t size) {
|
||||
//__attribute__((weak)) void* operator new(size_t size) {
|
||||
#if defined(RNS_USE_TLSF)
|
||||
//if (OS::_tlsf == nullptr) {
|
||||
if (!_tlsf_init) {
|
||||
_tlsf_init = true;
|
||||
#if defined(ESP32)
|
||||
// CBA Still unknown why the call to tlsf_create_with_pool() is so flaky on ESP32 with calculated buffer size. Reuires more research and unit tests.
|
||||
_contiguous_size = ESP.getMaxAllocHeap();
|
||||
TRACEF("contiguous_size: %u", _contiguous_size);
|
||||
if (_buffer_size == 0) {
|
||||
// CBA NOTE Using fp mathhere somehow causes tlsf_create_with_pool() to fail.
|
||||
//_buffer_size = (size_t)(_contiguous_size * BUFFER_FRACTION);
|
||||
// Compute 80% exactly using integers
|
||||
_buffer_size = (_contiguous_size * 4) / 5;
|
||||
}
|
||||
// Round DOWN to TLSF alignment
|
||||
size_t align = tlsf_align_size();
|
||||
_buffer_size &= ~(align - 1);
|
||||
void* raw_buffer = (void*)aligned_alloc(align, _buffer_size);
|
||||
#elif defined(ARDUINO_ARCH_NRF52) || defined(ARDUINO_NRF52_ADAFRUIT)
|
||||
_contiguous_size = dbgHeapFree();
|
||||
TRACEF("contiguous_size: %u", _contiguous_size);
|
||||
if (_buffer_size == 0) {
|
||||
_buffer_size = (size_t)(_contiguous_size * BUFFER_FRACTION);
|
||||
}
|
||||
// For NRF52 round to kB
|
||||
_buffer_size = (size_t)(_buffer_size / 1024) * 1024;
|
||||
TRACEF("buffer_size: %u", _buffer_size);
|
||||
void* raw_buffer = malloc(_buffer_size);
|
||||
#else
|
||||
_buffer_size = (size_t)BUFFER_SIZE;
|
||||
TRACEF("buffer_size: %u", _buffer_size);
|
||||
void* raw_buffer = malloc(_buffer_size);
|
||||
#endif
|
||||
if (raw_buffer == nullptr) {
|
||||
ERROR("-- allocation for tlsf FAILED");
|
||||
//strcpy(_tlsf_msg, "-- allocation for tlsf FAILED!!!");
|
||||
}
|
||||
else {
|
||||
#if 1
|
||||
OS::_tlsf = tlsf_create_with_pool(raw_buffer, _buffer_size);
|
||||
//if (OS::_tlsf == nullptr) {
|
||||
// sprintf(_tlsf_msg, "initialization of tlsf with align=%d, contiguous=%d, size=%d FAILED!!!", tlsf_align_size(), _contiguous_size, _buffer_size);
|
||||
//}
|
||||
//else {
|
||||
// sprintf(_tlsf_msg, "initialization of tlsf with align=%d, contiguous=%d, size=%d SUCCESSFUL!!!", tlsf_align_size(), _contiguous_size, _buffer_size);
|
||||
//}
|
||||
#else
|
||||
Serial.print("raw_buffer: ");
|
||||
Serial.println((long)raw_buffer);
|
||||
Serial.print("align_size: ");
|
||||
Serial.println((long)tlsf_align_size());
|
||||
void* aligned_buffer = (void*)(((size_t)raw_buffer + (tlsf_align_size() - 1)) & ~(tlsf_align_size() - 1));
|
||||
Serial.print("aligned_buffer: ");
|
||||
Serial.println((long)aligned_buffer);
|
||||
OS::_tlsf = tlsf_create_with_pool(aligned_buffer, BUFFER_SIZE-(size_t)((uint32_t)aligned_buffer - (uint32_t)raw_buffer));
|
||||
//tlfs = tlsf_create_with_pool(aligned_buffer, buffer_size--(size_t)((uint32_t)aligned_buffer - (uint32_t)raw_buffer));
|
||||
#endif
|
||||
if (OS::_tlsf == nullptr) {
|
||||
ERROR("-- initialization of tlsf FAILED");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
++_new_count;
|
||||
_new_size += size;
|
||||
if (size < _min_size || _min_size == 0) {
|
||||
_min_size = size;
|
||||
}
|
||||
//if (size > _max_size) {
|
||||
if (size < 4192 && size > _max_size) {
|
||||
_max_size = size;
|
||||
}
|
||||
void* p;
|
||||
#if defined(RNS_USE_TLSF)
|
||||
if (OS::_tlsf != nullptr) {
|
||||
//TRACEF("--- allocating memory from tlsf (%u bytes)", size);
|
||||
p = tlsf_malloc(OS::_tlsf, size);
|
||||
//TRACEF("--- allocated memory from tlsf (%u bytes) (addr=%lx)", size, p);
|
||||
}
|
||||
else {
|
||||
//TRACEF("--- allocating memory (%u bytes)", size);
|
||||
p = malloc(size);
|
||||
//TRACEF("--- allocated memory (%u bytes) (addr=%lx)", size, p);
|
||||
++_new_fault;
|
||||
}
|
||||
#else
|
||||
//TRACEF("--- allocating memory (%u bytes)", size);
|
||||
p = malloc(size);
|
||||
//TRACEF("--- allocated memory (%u bytes) (addr=%lx)", size, p);
|
||||
#endif
|
||||
return p;
|
||||
}
|
||||
|
||||
// CBA Added attribute weak to avoid collision with new override on nrf52
|
||||
void operator delete(void* p) {
|
||||
//__attribute__((weak)) void operator delete(void* p) {
|
||||
#if defined(RNS_USE_TLSF)
|
||||
if (OS::_tlsf != nullptr) {
|
||||
//TRACEF("--- freeing memory from tlsf (addr=%lx)", p);
|
||||
tlsf_free(OS::_tlsf, p);
|
||||
}
|
||||
else {
|
||||
//TRACEF("--- freeing memory (addr=%lx)", p);
|
||||
free(p);
|
||||
++_delete_fault;
|
||||
}
|
||||
#else
|
||||
//TRACEF("--- freeing memory (addr=%lx)", p);
|
||||
//TRACE("--- freeing memory");
|
||||
free(p);
|
||||
#endif
|
||||
++_delete_count;
|
||||
#if defined(RNS_USE_TLSF)
|
||||
//if (_delete_count == _new_count) {
|
||||
// TRACE("TLFS deinitializing");
|
||||
// OS::dump_memory_stats();
|
||||
// tlsf_destroy(OS::_tlsf);
|
||||
// OS::_tlsf = nullptr;
|
||||
//}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(RNS_USE_TLSF)
|
||||
uint32_t _tlsf_used_count = 0;
|
||||
uint32_t _tlsf_used_size = 0;
|
||||
uint32_t _tlsf_free_count = 0;
|
||||
uint32_t _tlsf_free_size = 0;
|
||||
uint32_t _tlsf_free_max_size = 0;
|
||||
void tlsf_mem_walker(void* ptr, size_t size, int used, void* user)
|
||||
{
|
||||
if (used) {
|
||||
_tlsf_used_count++;
|
||||
_tlsf_used_size += size;
|
||||
}
|
||||
else {
|
||||
_tlsf_free_count++;
|
||||
_tlsf_free_size += size;
|
||||
if (size > _tlsf_free_max_size) {
|
||||
_tlsf_free_max_size = size;
|
||||
}
|
||||
}
|
||||
}
|
||||
void dump_tlsf_stats() {
|
||||
_tlsf_used_count = 0;
|
||||
_tlsf_used_size = 0;
|
||||
_tlsf_free_count = 0;
|
||||
_tlsf_free_size = 0;
|
||||
_tlsf_free_max_size = 0;
|
||||
//TRACEF("TLSF Message: %s", _tlsf_msg);
|
||||
if (OS::_tlsf == nullptr) {
|
||||
return;
|
||||
}
|
||||
tlsf_walk_pool(tlsf_get_pool(OS::_tlsf), tlsf_mem_walker, nullptr);
|
||||
HEAD("TLSF Stats", LOG_TRACE);
|
||||
TRACEF("Buffer Size: %u", _buffer_size);
|
||||
TRACEF("Contiguous Size: %u", _contiguous_size);
|
||||
TRACEF("Used Count: %u", _tlsf_used_count);
|
||||
TRACEF("Used Size: %u (%u%% used)", _tlsf_used_size, (unsigned)((double)_tlsf_used_size / (double)_buffer_size * 100.0));
|
||||
TRACEF("Free Count: %u", _tlsf_free_count);
|
||||
TRACEF("Free Size: %u (%u%% free)", _tlsf_free_size, (unsigned)((double)_tlsf_free_size / (double)_buffer_size * 100.0));
|
||||
TRACEF("Max Free Size: %u (%u%% fragmented)\n", _tlsf_free_max_size, (unsigned)(100.0 - (double)_tlsf_free_max_size / (double)_tlsf_free_size * 100.0));
|
||||
}
|
||||
#endif
|
||||
|
||||
/*static*/ void OS::dump_allocator_stats() {
|
||||
HEAD("Allocator Stats", LOG_TRACE);
|
||||
TRACEF("New Count: %u", _new_count);
|
||||
TRACEF("New Fault: %u", _new_fault);
|
||||
TRACEF("Delete Count: %u", _delete_count);
|
||||
TRACEF("Delete Fault: %u", _delete_fault);
|
||||
TRACEF("Active Count: %u", (_new_count - _delete_count));
|
||||
TRACEF("Min Size: %u", _min_size);
|
||||
TRACEF("Max Size: %u", _max_size);
|
||||
TRACEF("Avg Size: %u\n", (size_t)(_new_size / _new_count));
|
||||
#if defined(RNS_USE_TLSF)
|
||||
dump_tlsf_stats();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // RNS_USE_ALLOCATOR
|
||||
|
||||
|
||||
size_t maxContiguousAllocation() {
|
||||
// Brute-force determine maximum allocation size
|
||||
//const size_t block_size = 256;
|
||||
const size_t block_size = 32;
|
||||
size_t block_count;
|
||||
for (block_count = 1; ; block_count++) {
|
||||
void* ptr = malloc(block_count * block_size);
|
||||
if (ptr == nullptr) {
|
||||
break;
|
||||
}
|
||||
free(ptr);
|
||||
}
|
||||
return (block_count - 1) * block_size;
|
||||
}
|
||||
|
||||
/*static*/ FileSystem OS::_filesystem = {Type::NONE};
|
||||
/*static*/ uint64_t OS::_time_offset = 0;
|
||||
|
||||
/*static*/ size_t OS::heap_size() {
|
||||
#if defined(ESP32)
|
||||
return ESP.getHeapSize();
|
||||
#elif defined(ARDUINO_ARCH_NRF52) || defined(ARDUINO_NRF52_ADAFRUIT)
|
||||
return dbgHeapTotal();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*static*/ size_t OS::heap_available() {
|
||||
#if defined(ESP32)
|
||||
return ESP.getFreeHeap();
|
||||
//return ESP.getMaxAllocHeap();
|
||||
#elif defined(ARDUINO_ARCH_NRF52) || defined(ARDUINO_NRF52_ADAFRUIT)
|
||||
return dbgHeapFree();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*static*/ void OS::dump_heap_stats() {
|
||||
HEAD("Heap Stats", LOG_TRACE);
|
||||
#if defined(ESP32)
|
||||
TRACEF("Heap size: %u", ESP.getHeapSize());
|
||||
TRACEF("Heap free: %u (%u%% free)", ESP.getFreeHeap(), (unsigned)((double)ESP.getFreeHeap() / (double)ESP.getHeapSize() * 100.0));
|
||||
//TRACEF("Heap free: %u (%u%% free)", xPortGetFreeHeapSize(), (unsigned)((double)xPortGetFreeHeapSize() / (double)xPort * 100.0));
|
||||
TRACEF("Heap min free: %u", ESP.getMinFreeHeap());
|
||||
//TRACEF("Heap min free: %u", xPortGetMinimumEverFreeHeapSize());
|
||||
TRACEF("Heap max alloc: %u (%u%% fragmented)", ESP.getMaxAllocHeap(), (unsigned)(100.0 - (double)ESP.getMaxAllocHeap() / (double)ESP.getFreeHeap() * 100.0));
|
||||
//TRACEF("Heap max alloc: %u (%u%% fragmented)", ESP.getMaxAllocHeap(), (unsigned)(100.0 - (double)ESP.getMaxAllocHeap() / (double)xPortGetFreeHeapSize() * 100.0));
|
||||
TRACEF("PSRAM size: %u", ESP.getPsramSize());
|
||||
TRACEF("PSRAM free: %u (%u%% free)", ESP.getFreePsram(), (ESP.getPsramSize() > 0) ? (unsigned)((double)ESP.getFreePsram() / (double)ESP.getPsramSize() * 100.0) : 0);
|
||||
TRACEF("PSRAM min free: %u", ESP.getMinFreePsram());
|
||||
TRACEF("PSRAM max alloc: %u (%u%% fragmented)", ESP.getMaxAllocPsram(), (ESP.getFreePsram() > 0) ? (unsigned)(100.0 - (double)ESP.getMaxAllocPsram() / (double)ESP.getFreePsram() * 100.0) : 0);
|
||||
#elif defined(ARDUINO_ARCH_NRF52) || defined(ARDUINO_NRF52_ADAFRUIT)
|
||||
if (loglevel() == LOG_TRACE) {
|
||||
dbgMemInfo();
|
||||
}
|
||||
#endif
|
||||
#if defined(RNS_USE_ALLOCATOR)
|
||||
OS::dump_allocator_stats();
|
||||
#endif
|
||||
}
|
||||
234
lib/microReticulum/src/Utilities/OS.h
Executable file
234
lib/microReticulum/src/Utilities/OS.h
Executable file
@@ -0,0 +1,234 @@
|
||||
#pragma once
|
||||
|
||||
#include "../FileSystem.h"
|
||||
#include "../FileStream.h"
|
||||
#include "../Bytes.h"
|
||||
|
||||
#include "tlsf.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#ifdef ARDUINO
|
||||
#include <Arduino.h>
|
||||
#endif
|
||||
|
||||
#undef round
|
||||
|
||||
namespace RNS { namespace Utilities {
|
||||
|
||||
class OS {
|
||||
|
||||
private:
|
||||
static FileSystem _filesystem;
|
||||
static uint64_t _time_offset;
|
||||
|
||||
public:
|
||||
static tlsf_t _tlsf;
|
||||
|
||||
public:
|
||||
static inline uint64_t getTimeOffset() { return _time_offset; }
|
||||
static inline void setTimeOffset(uint64_t offset) { _time_offset = offset; }
|
||||
|
||||
#ifdef ARDUINO
|
||||
// return current time in milliseconds since startup
|
||||
static inline uint64_t ltime() {
|
||||
// handle roll-over of 32-bit millis (approx. 49 days)
|
||||
static uint32_t low32, high32;
|
||||
uint32_t new_low32 = millis();
|
||||
if (new_low32 < low32) high32++;
|
||||
low32 = new_low32;
|
||||
return ((uint64_t)high32 << 32 | low32) + _time_offset;
|
||||
}
|
||||
#else
|
||||
// return current time in milliseconds since 00:00:00, January 1, 1970 (Unix Epoch)
|
||||
static inline uint64_t ltime() { timeval time; ::gettimeofday(&time, NULL); return (uint64_t)(time.tv_sec * 1000) + (uint64_t)(time.tv_usec / 1000); }
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO
|
||||
// return current time in float seconds since startup
|
||||
static inline double time() { return (double)(ltime() / 1000.0); }
|
||||
#else
|
||||
// return current time in float seconds since 00:00:00, January 1, 1970 (Unix Epoch)
|
||||
static inline double time() { timeval time; ::gettimeofday(&time, NULL); return (double)time.tv_sec + ((double)time.tv_usec / 1000000); }
|
||||
#endif
|
||||
|
||||
// sleep for specified milliseconds
|
||||
//static inline void sleep(float seconds) { ::sleep(seconds); }
|
||||
#ifdef ARDUINO
|
||||
static inline void sleep(float seconds) { delay((uint32_t)(seconds * 1000)); }
|
||||
#else
|
||||
static inline void sleep(float seconds) { timespec time; time.tv_sec = (time_t)(seconds); time.tv_nsec = (seconds - (float)time.tv_sec) * 1000000000; ::nanosleep(&time, nullptr); }
|
||||
#endif
|
||||
//static inline void sleep(uint32_t milliseconds) { ::sleep((float)milliseconds / 1000.0); }
|
||||
|
||||
// round decimal number to specified precision
|
||||
//static inline float round(float value, uint8_t precision) { return std::round(value / precision) * precision; }
|
||||
//static inline double round(double value, uint8_t precision) { return std::round(value / precision) * precision; }
|
||||
static inline double round(double value, uint8_t precision) { return std::round(value / precision) * precision; }
|
||||
|
||||
static inline uint64_t from_bytes_big_endian(const uint8_t* data, size_t len) {
|
||||
uint64_t result = 0;
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
result = (result << 8) | data[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Detect endianness at runtime
|
||||
static int is_big_endian(void) {
|
||||
uint16_t test = 0x0102;
|
||||
return ((uint8_t*)&test)[0] == 0x01;
|
||||
}
|
||||
|
||||
// Byte swap functions
|
||||
static uint16_t swap16(uint16_t val) {
|
||||
return (val << 8) | (val >> 8);
|
||||
}
|
||||
|
||||
static uint32_t swap32(uint32_t val) {
|
||||
return ((val << 24) & 0xFF000000) |
|
||||
((val << 8) & 0x00FF0000) |
|
||||
((val >> 8) & 0x0000FF00) |
|
||||
((val >> 24) & 0x000000FF);
|
||||
}
|
||||
|
||||
// Platform-independent replacements
|
||||
|
||||
static uint16_t portable_htons(uint16_t val) {
|
||||
return is_big_endian() ? val : swap16(val);
|
||||
}
|
||||
|
||||
static uint32_t portable_htonl(uint32_t val) {
|
||||
return is_big_endian() ? val : swap32(val);
|
||||
}
|
||||
|
||||
static uint16_t portable_ntohs(uint16_t val) {
|
||||
return is_big_endian() ? val : swap16(val);
|
||||
}
|
||||
|
||||
static uint32_t portable_ntohl(uint32_t val) {
|
||||
return is_big_endian() ? val : swap32(val);
|
||||
}
|
||||
|
||||
#if defined(RNS_USE_ALLOCATOR)
|
||||
static void dump_allocator_stats();
|
||||
#endif
|
||||
|
||||
inline static void register_filesystem(FileSystem& filesystem) {
|
||||
TRACE("Registering filesystem...");
|
||||
_filesystem = filesystem;
|
||||
}
|
||||
|
||||
/*
|
||||
inline static void register_filesystem(FileSystemImpl* filesystemimpl) {
|
||||
TRACE("Registering filesystem...");
|
||||
_filesystem = filesystemimpl;
|
||||
}
|
||||
*/
|
||||
|
||||
inline static void deregister_filesystem() {
|
||||
TRACE("Deregistering filesystem...");
|
||||
_filesystem = {Type::NONE};
|
||||
}
|
||||
|
||||
inline static FileSystem& get_filesystem() {
|
||||
return _filesystem;
|
||||
}
|
||||
|
||||
|
||||
inline static bool file_exists(const char* file_path) {
|
||||
if (!_filesystem) {
|
||||
WARNING("file_exists: filesystem not registered");
|
||||
throw std::runtime_error("FileSystem has not been registered");
|
||||
}
|
||||
return _filesystem.file_exists(file_path);
|
||||
}
|
||||
|
||||
inline static size_t read_file(const char* file_path, Bytes& data) {
|
||||
if (!_filesystem) {
|
||||
throw std::runtime_error("FileSystem has not been registered");
|
||||
}
|
||||
return _filesystem.read_file(file_path, data);
|
||||
}
|
||||
|
||||
inline static size_t write_file(const char* file_path, const Bytes& data) {
|
||||
if (!_filesystem) {
|
||||
throw std::runtime_error("FileSystem has not been registered");
|
||||
}
|
||||
return _filesystem.write_file(file_path, data);
|
||||
}
|
||||
|
||||
inline static FileStream open_file(const char* file_path, RNS::FileStream::MODE file_mode) {
|
||||
if (!_filesystem) {
|
||||
throw std::runtime_error("FileSystem has not been registered");
|
||||
}
|
||||
return _filesystem.open_file(file_path, file_mode);
|
||||
}
|
||||
|
||||
inline static bool remove_file(const char* file_path) {
|
||||
if (!_filesystem) {
|
||||
throw std::runtime_error("FileSystem has not been registered");
|
||||
}
|
||||
return _filesystem.remove_file(file_path);
|
||||
}
|
||||
|
||||
inline static bool rename_file(const char* from_file_path, const char* to_file_path) {
|
||||
if (!_filesystem) {
|
||||
throw std::runtime_error("FileSystem has not been registered");
|
||||
}
|
||||
return _filesystem.rename_file(from_file_path, to_file_path);
|
||||
}
|
||||
|
||||
inline static bool directory_exists(const char* directory_path) {
|
||||
if (!_filesystem) {
|
||||
throw std::runtime_error("FileSystem has not been registered");
|
||||
}
|
||||
return _filesystem.directory_exists(directory_path);
|
||||
}
|
||||
|
||||
inline static bool create_directory(const char* directory_path) {
|
||||
if (!_filesystem) {
|
||||
throw std::runtime_error("FileSystem has not been registered");
|
||||
}
|
||||
return _filesystem.create_directory(directory_path);
|
||||
}
|
||||
|
||||
inline static bool remove_directory(const char* directory_path) {
|
||||
if (!_filesystem) {
|
||||
throw std::runtime_error("FileSystem has not been registered");
|
||||
}
|
||||
return _filesystem.remove_directory(directory_path);
|
||||
}
|
||||
|
||||
inline static std::list<std::string> list_directory(const char* directory_path) {
|
||||
if (!_filesystem) {
|
||||
throw std::runtime_error("FileSystem has not been registered");
|
||||
}
|
||||
return _filesystem.list_directory(directory_path);
|
||||
}
|
||||
|
||||
inline static size_t storage_size() {
|
||||
if (!_filesystem) {
|
||||
throw std::runtime_error("FileSystem has not been registered");
|
||||
}
|
||||
return _filesystem.storage_size();
|
||||
}
|
||||
|
||||
inline static size_t storage_available() {
|
||||
if (!_filesystem) {
|
||||
throw std::runtime_error("FileSystem has not been registered");
|
||||
}
|
||||
return _filesystem.storage_available();
|
||||
}
|
||||
|
||||
static size_t heap_size();
|
||||
static size_t heap_available();
|
||||
static void dump_heap_stats();
|
||||
|
||||
};
|
||||
|
||||
} }
|
||||
9
lib/microReticulum/src/Utilities/Persistence.cpp
Executable file
9
lib/microReticulum/src/Utilities/Persistence.cpp
Executable file
@@ -0,0 +1,9 @@
|
||||
#include "Persistence.h"
|
||||
|
||||
#include "Bytes.h"
|
||||
|
||||
using namespace RNS;
|
||||
|
||||
/*static*/ //DynamicJsonDocument _document(Type::Persistence::DOCUMENT_MAXSIZE);
|
||||
/*static*/ JsonDocument _document;
|
||||
/*static*/ Bytes _buffer(Type::Persistence::BUFFER_MAXSIZE);
|
||||
699
lib/microReticulum/src/Utilities/Persistence.h
Executable file
699
lib/microReticulum/src/Utilities/Persistence.h
Executable file
@@ -0,0 +1,699 @@
|
||||
#pragma once
|
||||
|
||||
// CBA NOTE If headers for classes referenced in this file are not included here,
|
||||
// then they MUST be included BEFORE this header is included.
|
||||
#include "Transport.h"
|
||||
#include "Type.h"
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
namespace ArduinoJson {
|
||||
|
||||
// ArduinoJSON serialization support for std::vector<T>
|
||||
template <typename T>
|
||||
struct Converter<std::vector<T>> {
|
||||
static void toJson(const std::vector<T>& src, JsonVariant dst) {
|
||||
TRACE("<<< Serializing vector");
|
||||
JsonArray array = dst.to<JsonArray>();
|
||||
for (T item : src)
|
||||
array.add(item);
|
||||
TRACE("<<< Finished serializing vector");
|
||||
}
|
||||
static std::vector<T> fromJson(JsonVariantConst src) {
|
||||
TRACE("<<< Deserializing vector");
|
||||
std::vector<T> dst;
|
||||
for (T item : src.as<JsonArrayConst>())
|
||||
dst.push_back(item);
|
||||
TRACE("<<< Finished deserializing vector");
|
||||
return dst;
|
||||
}
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
JsonArrayConst array = src;
|
||||
bool result = array;
|
||||
for (JsonVariantConst item : array)
|
||||
result &= item.is<T>();
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// ArduinoJSON serialization support for std::set<T>
|
||||
template <typename T>
|
||||
struct Converter<std::set<T>> {
|
||||
static void toJson(const std::set<T>& src, JsonVariant dst) {
|
||||
JsonArray array = dst.to<JsonArray>();
|
||||
for (T item : src)
|
||||
array.add(item);
|
||||
}
|
||||
static std::set<T> fromJson(JsonVariantConst src) {
|
||||
std::set<T> dst;
|
||||
for (T item : src.as<JsonArrayConst>())
|
||||
dst.insert(item);
|
||||
return dst;
|
||||
}
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
JsonArrayConst array = src;
|
||||
bool result = array;
|
||||
for (JsonVariantConst item : array)
|
||||
result &= item.is<T>();
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// ArduinoJSON serialization support for std::map<std::string, T>
|
||||
template <typename T>
|
||||
struct Converter<std::map<std::string, T>> {
|
||||
static void toJson(const std::map<std::string, T>& src, JsonVariant dst) {
|
||||
JsonObject obj = dst.to<JsonObject>();
|
||||
for (const auto& item : src)
|
||||
obj[item.first] = item.second;
|
||||
}
|
||||
static std::map<std::string, T> fromJson(JsonVariantConst src) {
|
||||
std::map<std::string, T> dst;
|
||||
for (JsonPairConst item : src.as<JsonObjectConst>())
|
||||
dst[item.key().c_str()] = item.value().as<T>();
|
||||
return dst;
|
||||
}
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
JsonObjectConst obj = src;
|
||||
bool result = obj;
|
||||
for (JsonPairConst item : obj)
|
||||
result &= item.value().is<T>();
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
// ArduinoJSON serialization support for std::map<Bytes, T>
|
||||
template <typename T>
|
||||
struct Converter<std::map<RNS::Bytes, T>> {
|
||||
static void toJson(const std::map<RNS::Bytes, T>& src, JsonVariant dst) {
|
||||
JsonObject obj = dst.to<JsonObject>();
|
||||
for (const auto& item : src) {
|
||||
//obj[item.first] = item.second;
|
||||
obj[item.first.toHex()] = item.second;
|
||||
}
|
||||
}
|
||||
static std::map<RNS::Bytes, T> fromJson(JsonVariantConst src) {
|
||||
std::map<RNS::Bytes, T> dst;
|
||||
for (JsonPairConst item : src.as<JsonObjectConst>()) {
|
||||
//dst[item.key().c_str()] = item.value().as<T>();
|
||||
RNS::Bytes key;
|
||||
key.assignHex(item.key().c_str());
|
||||
//dst[key] = item.value().as<T>();
|
||||
dst.insert({key, item.value().as<T>()});
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
JsonObjectConst obj = src;
|
||||
bool result = obj;
|
||||
for (JsonPairConst item : obj) {
|
||||
result &= item.value().is<T>();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
// ArduinoJSON serialization support for RNS::Bytes
|
||||
template <>
|
||||
struct Converter<RNS::Bytes> {
|
||||
static bool toJson(const RNS::Bytes& src, JsonVariant dst) {
|
||||
//TRACE("<<< Serializing Bytes");
|
||||
//TRACE("<<< Finished serializing Bytes");
|
||||
return true;
|
||||
}
|
||||
static RNS::Bytes fromJson(JsonVariantConst src) {
|
||||
Bytes bytes;
|
||||
//TRACE(">>> Deserializing Bytes");
|
||||
//TRACE(">>> Finished deserializing Bytes");
|
||||
return bytes;
|
||||
}
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
return
|
||||
src.is<RNS::Bytes>();
|
||||
}
|
||||
};
|
||||
*/
|
||||
|
||||
/*
|
||||
// ArduinoJSON serialization support for RNS::Interface
|
||||
template <>
|
||||
struct Converter<RNS::Interface> {
|
||||
static bool toJson(const RNS::Interface& src, JsonVariant dst) {
|
||||
if (!src) {
|
||||
return dst.set(nullptr);
|
||||
}
|
||||
//TRACE("<<< Serializing interface hash " + src.get_hash().toHex());
|
||||
return dst.set(src.get_hash().toHex());
|
||||
}
|
||||
static RNS::Interface fromJson(JsonVariantConst src) {
|
||||
if (!src.isNull()) {
|
||||
RNS::Bytes hash;
|
||||
hash.assignHex(src.as<const char*>());
|
||||
//TRACE(">>> Deserialized interface hash " + hash.toHex());
|
||||
//TRACE(">>> Querying transport for interface");
|
||||
// Query transport for matching interface
|
||||
return RNS::Transport::find_interface_from_hash(hash);
|
||||
}
|
||||
else {
|
||||
return {RNS::Type::NONE};
|
||||
}
|
||||
}
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
return src.is<const char*>() && strlen(src.as<const char*>()) == 64;
|
||||
}
|
||||
};
|
||||
*/
|
||||
|
||||
/*
|
||||
// ArduinoJSON serialization support for RNS::Packet
|
||||
template <>
|
||||
struct Converter<RNS::Packet> {
|
||||
static bool toJson(const RNS::Packet& src, JsonVariant dst) {
|
||||
if (!src) {
|
||||
return dst.set(nullptr);
|
||||
}
|
||||
//TRACE("<<< Serializing packet hash " + src.get_hash().toHex());
|
||||
// Whenever a reference to a packet is serialized we must ensure that packet itself also gets serialized separately
|
||||
RNS::Transport::cache_packet(src, true);
|
||||
return dst.set(src.get_hash().toHex());
|
||||
}
|
||||
static RNS::Packet fromJson(JsonVariantConst src) {
|
||||
if (!src.isNull()) {
|
||||
RNS::Bytes hash;
|
||||
hash.assignHex(src.as<const char*>());
|
||||
//TRACE(">>> Deserialized packet hash " + hash.toHex());
|
||||
//TRACE(">>> Querying transport for cached packet");
|
||||
// Query transport for matching packet
|
||||
return RNS::Transport::get_cached_packet(hash);
|
||||
}
|
||||
else {
|
||||
return {RNS::Type::NONE};
|
||||
}
|
||||
}
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
return src.is<const char*>() && strlen(src.as<const char*>()) == 64;
|
||||
}
|
||||
};
|
||||
*/
|
||||
// ArduinoJSON serialization support for RNS::Packet
|
||||
template <>
|
||||
struct Converter<RNS::Packet> {
|
||||
static bool toJson(const RNS::Packet& src, JsonVariant dst) {
|
||||
//TRACE("<<< Serializing Packet");
|
||||
//dst["hash"] = src.get_hash();
|
||||
dst["raw"] = src.raw();
|
||||
dst["sent_at"] = src.sent_at();
|
||||
dst["destination_hash"] = src.get_hash();
|
||||
//TRACE("<<< Finished serializing Packet");
|
||||
return true;
|
||||
}
|
||||
static RNS::Packet fromJson(JsonVariantConst src) {
|
||||
//TRACE(">>> Deserializing Packet");
|
||||
RNS::Bytes raw = src["raw"];
|
||||
//RNS::Bytes raw = src["raw"].as<const RNS::Bytes&>();
|
||||
RNS::Packet packet(RNS::Destination(RNS::Type::NONE), raw);
|
||||
//packet.set_hash(src["hash"]);
|
||||
packet.sent_at(src["sent_at"]);
|
||||
RNS::Bytes destination_hash = src["destination_hash"];
|
||||
// set cached flag since pcket was read from cache
|
||||
packet.cached(true);
|
||||
//TRACE(">>> Finished deserializing Packet");
|
||||
return packet;
|
||||
}
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
return
|
||||
//src["hash"].is<RNS::Bytes>() &&
|
||||
src["raw"].is<RNS::Bytes>() &&
|
||||
src["sent_at"].is<double>() &&
|
||||
src["destination_hash"].is<RNS::Bytes>();
|
||||
}
|
||||
};
|
||||
|
||||
// ArduinoJSON serialization support for RNS::Transport::PacketEntry
|
||||
template <>
|
||||
struct Converter<RNS::Transport::PacketEntry> {
|
||||
static bool toJson(const RNS::Transport::PacketEntry& src, JsonVariant dst) {
|
||||
//TRACE("<<< Serializing Transport::PacketEntry");
|
||||
//dst["hash"] = src._hash;
|
||||
dst["raw"] = src._raw;
|
||||
dst["sent_at"] = src._sent_at;
|
||||
dst["destination_hash"] = src._destination_hash;
|
||||
//TRACE("<<< Finished serializing Transport::PacketEntry");
|
||||
return true;
|
||||
}
|
||||
static RNS::Transport::PacketEntry fromJson(JsonVariantConst src) {
|
||||
//TRACE(">>> Deserializing Transport::PacketEntry");
|
||||
RNS::Transport::PacketEntry dst;
|
||||
//dst._hash = src["hash"];
|
||||
dst._raw = src["raw"];
|
||||
dst._sent_at = src["sent_at"];
|
||||
dst._destination_hash = src["destination_hash"];
|
||||
// set cached flag since pcket was read from cache
|
||||
dst._cached = true;
|
||||
//TRACE(">>> Finished deserializing Transport::PacketEntry");
|
||||
return dst;
|
||||
}
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
return
|
||||
//src["hash"].is<RNS::Bytes>() &&
|
||||
src["raw"].is<RNS::Bytes>() &&
|
||||
src["sent_at"].is<double>() &&
|
||||
src["destination_hash"].is<RNS::Bytes>();
|
||||
}
|
||||
};
|
||||
|
||||
#if 1
|
||||
// ArduinoJSON serialization support for RNS::Transport::DestinationEntry
|
||||
template <>
|
||||
struct Converter<RNS::Transport::DestinationEntry> {
|
||||
static bool toJson(const RNS::Transport::DestinationEntry& src, JsonVariant dst) {
|
||||
//TRACE("<<< Serializing Transport::DestinationEntry");
|
||||
dst["timestamp"] = src._timestamp;
|
||||
dst["received_from"] = src._received_from;
|
||||
dst["announce_hops"] = src._hops;
|
||||
dst["expires"] = src._expires;
|
||||
dst["random_blobs"] = src._random_blobs;
|
||||
/*
|
||||
//dst["interface_hash"] = src._receiving_interface;
|
||||
if (src._receiving_interface) {
|
||||
dst["interface_hash"] = src._receiving_interface.get_hash();
|
||||
}
|
||||
else {
|
||||
dst["interface_hash"] = nullptr;
|
||||
}
|
||||
// CBA TODO Move packet serialization to *after* destination table serialization since packets are useless
|
||||
// anyway if there's no space to left to write destination table.
|
||||
// Whenever a reference to a packet is serialized we must ensure that packet itself also gets serialized separately
|
||||
//dst["packet"] = src._announce_packet;
|
||||
if (src._announce_packet) {
|
||||
dst["packet_hash"] = src._announce_packet.get_hash();
|
||||
// Only cache packet if not already cached
|
||||
if (!src._announce_packet.cached()) {
|
||||
if (RNS::Transport::cache_packet(src._announce_packet, true)) {
|
||||
const_cast<RNS::Packet&>(src._announce_packet).cached(true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
TRACE("Destination announce packet " + src._announce_packet.get_hash().toHex() + " is already cached");
|
||||
}
|
||||
}
|
||||
else {
|
||||
dst["packet_hash"] = nullptr;
|
||||
}
|
||||
*/
|
||||
dst["interface_hash"] = src._receiving_interface;
|
||||
dst["packet_hash"] = src._announce_packet;
|
||||
//TRACE("<<< Finished Serializing Transport::DestinationEntry");
|
||||
return true;
|
||||
}
|
||||
static RNS::Transport::DestinationEntry fromJson(JsonVariantConst src) {
|
||||
//TRACE(">>> Deserializing Transport::DestinationEntry");
|
||||
RNS::Transport::DestinationEntry dst;
|
||||
dst._timestamp = src["timestamp"];
|
||||
dst._received_from = src["received_from"];
|
||||
dst._hops = src["announce_hops"];
|
||||
dst._expires = src["expires"];
|
||||
dst._random_blobs = src["random_blobs"].as<std::set<RNS::Bytes>>();
|
||||
/*
|
||||
//dst._receiving_interface = src["interface_hash"];
|
||||
RNS::Bytes interface_hash = src["interface_hash"];
|
||||
if (interface_hash) {
|
||||
// Query transport for matching interface
|
||||
dst._receiving_interface = RNS::Transport::find_interface_from_hash(interface_hash);
|
||||
}
|
||||
//dst._announce_packet = src["packet"];
|
||||
RNS::Bytes packet_hash = src["packet_hash"];
|
||||
if (packet_hash) {
|
||||
// Query transport for matching packet
|
||||
dst._announce_packet = RNS::Transport::get_cached_packet(packet_hash);
|
||||
}
|
||||
*/
|
||||
dst._receiving_interface = src["interface_hash"];
|
||||
dst._announce_packet = src["packet_hash"];
|
||||
/*
|
||||
//RNS::Transport::DestinationEntry dst(src["timestamp"], src["received_from"], src["announce_hops"], src["expires"], src["random_blobs"], src["receiving_interface"], src["packet"]);
|
||||
RNS::Transport::DestinationEntry dst(
|
||||
src["timestamp"].as<double>(),
|
||||
src["received_from"].as<RNS::Bytes>(),
|
||||
src["announce_hops"].as<int>(),
|
||||
src["expires"].as<double>(),
|
||||
src["random_blobs"].as<std::set<RNS::Bytes>>(),
|
||||
src["receiving_interface"].as<RNS::Interface>(),
|
||||
src["packet"].as<RNS::Packet>()
|
||||
);
|
||||
*/
|
||||
//TRACE(">>> Finished Deserializing Transport::DestinationEntry");
|
||||
return dst;
|
||||
}
|
||||
static bool checkJson(JsonVariantConst src) {
|
||||
return
|
||||
src["timestamp"].is<double>() &&
|
||||
src["received_from"].is<RNS::Bytes>() &&
|
||||
src["announce_hops"].is<int>() &&
|
||||
src["expires"].is<double>() &&
|
||||
src["random_blobs"].is<std::set<RNS::Bytes>>() &&
|
||||
src["interface_hash"].is<RNS::Bytes>() &&
|
||||
src["packet_hash"].is<RNS::Bytes>();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#if 0
|
||||
namespace RNS {
|
||||
|
||||
inline bool convertToJson(const Transport::DestinationEntry& src, JsonVariant dst) {
|
||||
//TRACE("<<< NEW Serializing Transport::DestinationEntry");
|
||||
dst["timestamp"] = src._timestamp;
|
||||
dst["received_from"] = src._received_from;
|
||||
dst["announce_hops"] = src._hops;
|
||||
dst["expires"] = src._expires;
|
||||
dst["random_blobs"] = src._random_blobs;
|
||||
/*
|
||||
//dst["interface_hash"] = src._receiving_interface;
|
||||
if (src._receiving_interface) {
|
||||
dst["interface_hash"] = src._receiving_interface.get_hash();
|
||||
}
|
||||
else {
|
||||
dst["interface_hash"] = nullptr;
|
||||
}
|
||||
// CBA TODO Move packet serialization to *after* destination table serialization since packets are useless
|
||||
// anyway if there's no space to left to write destination table.
|
||||
// Whenever a reference to a packet is serialized we must ensure that packet itself also gets serialized separately
|
||||
//dst["packet"] = src._announce_packet;
|
||||
if (src._announce_packet) {
|
||||
dst["packet_hash"] = src._announce_packet.get_hash();
|
||||
// Only cache packet if not already cached
|
||||
if (!src._announce_packet.cached()) {
|
||||
if (RNS::Transport::cache_packet(src._announce_packet, true)) {
|
||||
const_cast<RNS::Packet&>(src._announce_packet).cached(true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
TRACE("Destination announce packet " + src._announce_packet.get_hash().toHex() + " is already cached");
|
||||
}
|
||||
}
|
||||
else {
|
||||
dst["packet_hash"] = nullptr;
|
||||
}
|
||||
*/
|
||||
dst["interface_hash"] = src._receiving_interface;
|
||||
dst["packet_hash"] = src._announce_packet;
|
||||
//TRACE("<<< Finished Serializing Transport::DestinationEntry");
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void convertFromJson(JsonVariantConst src, Transport::DestinationEntry& dst) {
|
||||
//TRACE(">>> NEW Deserializing Transport::DestinationEntry");
|
||||
dst._timestamp = src["timestamp"];
|
||||
dst._received_from = src["received_from"];
|
||||
dst._hops = src["announce_hops"];
|
||||
dst._expires = src["expires"];
|
||||
dst._random_blobs = src["random_blobs"].as<std::set<RNS::Bytes>>();
|
||||
/*
|
||||
//dst._receiving_interface = src["interface_hash"];
|
||||
RNS::Bytes interface_hash = src["interface_hash"];
|
||||
if (interface_hash) {
|
||||
// Query transport for matching interface
|
||||
dst._receiving_interface = RNS::Transport::find_interface_from_hash(interface_hash);
|
||||
}
|
||||
//dst._announce_packet = src["packet"];
|
||||
RNS::Bytes packet_hash = src["packet_hash"];
|
||||
if (packet_hash) {
|
||||
// Query transport for matching packet
|
||||
dst._announce_packet = RNS::Transport::get_cached_packet(packet_hash);
|
||||
}
|
||||
*/
|
||||
dst._receiving_interface = src["interface_hash"];
|
||||
dst._announce_packet = src["packet_hash"];
|
||||
/*
|
||||
//RNS::Transport::DestinationEntry dst(src["timestamp"], src["received_from"], src["announce_hops"], src["expires"], src["random_blobs"], src["receiving_interface"], src["packet"]);
|
||||
RNS::Transport::DestinationEntry dst(
|
||||
src["timestamp"].as<double>(),
|
||||
src["received_from"].as<RNS::Bytes>(),
|
||||
src["announce_hops"].as<int>(),
|
||||
src["expires"].as<double>(),
|
||||
src["random_blobs"].as<std::set<RNS::Bytes>>(),
|
||||
src["receiving_interface"].as<RNS::Interface>(),
|
||||
src["packet"].as<RNS::Packet>()
|
||||
);
|
||||
*/
|
||||
//TRACE(">>> Finished Deserializing Transport::DestinationEntry");
|
||||
}
|
||||
|
||||
inline bool canConvertFromJson(JsonVariantConst src, const Transport::DestinationEntry&) {
|
||||
TRACE("=== NEW Checking Transport::DestinationEntry");
|
||||
return
|
||||
src["timestamp"].is<double>() &&
|
||||
src["received_from"].is<RNS::Bytes>() &&
|
||||
src["announce_hops"].is<int>() &&
|
||||
src["expires"].is<double>() &&
|
||||
src["random_blobs"].is<std::set<RNS::Bytes>>() &&
|
||||
src["interface_hash"].is<RNS::Bytes>() &&
|
||||
src["packet_hash"].is<RNS::Bytes>();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace RNS { namespace Persistence {
|
||||
|
||||
//static DynamicJsonDocument _document(Type::Persistence::DOCUMENT_MAXSIZE);
|
||||
static JsonDocument _document;
|
||||
static Bytes _buffer(Type::Persistence::BUFFER_MAXSIZE);
|
||||
|
||||
template <typename T> size_t crc(const T& obj) {
|
||||
//TRACE("Persistence::crc<T>");
|
||||
_document.set(obj);
|
||||
size_t size = _buffer.capacity();
|
||||
#ifdef USE_MSGPACK
|
||||
size_t length = serializeMsgPack(_document, _buffer.writable(size), size);
|
||||
#else
|
||||
size_t length = serializeJson(_document, _buffer.writable(size), size);
|
||||
#endif
|
||||
if (length < size) {
|
||||
_buffer.resize(length);
|
||||
}
|
||||
TRACEF("Persistence::crc: serialized %d bytes", length);
|
||||
return Utilities::Crc::crc32(0, _buffer.data(), _buffer.size());
|
||||
}
|
||||
|
||||
template <typename T> size_t serialize(const T& obj, const char* file_path) {
|
||||
//TRACE("Persistence::serialize<T>");
|
||||
_document.set(obj);
|
||||
size_t size = _buffer.capacity();
|
||||
#ifdef USE_MSGPACK
|
||||
size_t length = serializeMsgPack(_document, _buffer.writable(size), size);
|
||||
#else
|
||||
size_t length = serializeJson(_document, _buffer.writable(size), size);
|
||||
#endif
|
||||
if (length < size) {
|
||||
_buffer.resize(length);
|
||||
}
|
||||
TRACEF("Persistence::serialize: serialized %d bytes", length);
|
||||
size_t wrote = 0;
|
||||
if (length > 0) {
|
||||
wrote = RNS::Utilities::OS::write_file(file_path, _buffer);
|
||||
if (wrote == _buffer.size()) {
|
||||
TRACEF("Persistence::serialize: wrote %d bytes", _buffer.size());
|
||||
}
|
||||
else {
|
||||
TRACE("Persistence::serialize: write failed");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
TRACE("Persistence::serialize: failed to serialize");
|
||||
return 0;
|
||||
}
|
||||
return wrote;
|
||||
}
|
||||
|
||||
template <typename T> size_t deserialize(T& obj, const char* file_path) {
|
||||
//TRACE("Persistence::deserialize<T>");
|
||||
size_t read = RNS::Utilities::OS::read_file(file_path, _buffer);
|
||||
if (read > 0) {
|
||||
TRACEF("Persistence::deserialize: read: %d bytes", _buffer.size());
|
||||
//TRACE("testDeserializeVector: data: " + _buffer.toString());
|
||||
#ifdef USE_MSGPACK
|
||||
DeserializationError error = deserializeMsgPack(_document, _buffer.data());
|
||||
#else
|
||||
DeserializationError error = deserializeJson(_document, _buffer.data());
|
||||
#endif
|
||||
if (!error) {
|
||||
TRACE("Persistence::deserialize: successfully deserialized document");
|
||||
obj = _document.as<T>();
|
||||
// CBA Following obj check doesn't work when T is a collection
|
||||
//if (obj) {
|
||||
return read;
|
||||
//}
|
||||
TRACE("Persistence::deserialize: failed to compose object");
|
||||
}
|
||||
else {
|
||||
TRACE("Persistence::deserialize: failed to deserialize");
|
||||
}
|
||||
}
|
||||
else {
|
||||
TRACE("Persistence::deserialize: read failed");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 1
|
||||
template <typename T> size_t crc(std::map<Bytes, T>& map) {
|
||||
//TRACE("Persistence::crc<map<Bytes, T>>");
|
||||
|
||||
uint32_t crc = 0;
|
||||
crc = Utilities::Crc::crc32(crc, '{');
|
||||
for (const auto& [key, value] : map) {
|
||||
crc = Utilities::Crc::crc32(crc, '"');
|
||||
std::string hex = key.toHex();
|
||||
crc = Utilities::Crc::crc32(crc, hex.c_str());
|
||||
crc = Utilities::Crc::crc32(crc, '"');
|
||||
crc = Utilities::Crc::crc32(crc, ':');
|
||||
|
||||
_document.set(value);
|
||||
size_t size = _buffer.capacity();
|
||||
#ifdef USE_MSGPACK
|
||||
size_t length = serializeMsgPack(_document, _buffer.writable(size), size);
|
||||
#else
|
||||
size_t length = serializeJson(_document, _buffer.writable(size), size);
|
||||
#endif
|
||||
if (length < size) {
|
||||
_buffer.resize(length);
|
||||
}
|
||||
TRACEF("Persistence::crc: serialized entry %d bytes", length);
|
||||
|
||||
if (length > 0) {
|
||||
crc = Utilities::Crc::crc32(crc, _buffer.data(), _buffer.size());
|
||||
}
|
||||
else {
|
||||
// if failed to serialize entry then write empty entry
|
||||
crc = Utilities::Crc::crc32(crc, "{}");
|
||||
}
|
||||
crc = Utilities::Crc::crc32(crc, ',');
|
||||
}
|
||||
return Utilities::Crc::crc32(crc, '}');
|
||||
}
|
||||
|
||||
template <typename T> size_t serialize(std::map<Bytes, T>& map, const char* file_path, uint32_t& crc) {
|
||||
//TRACE("Persistence::serialize<map<Bytes,T>>");
|
||||
|
||||
// CBA TODO: Use stream here instead to avoid having to buffer entire structure
|
||||
RNS::FileStream stream = RNS::Utilities::OS::open_file(file_path, RNS::FileStream::MODE_WRITE);
|
||||
if (!stream) {
|
||||
TRACE("Persistence::serialize: failed to open write stream");
|
||||
return 0;
|
||||
}
|
||||
|
||||
stream.write('{');
|
||||
for (const auto& [key, value] : map) {
|
||||
stream.write('"');
|
||||
std::string hex = key.toHex();
|
||||
stream.write(hex.c_str());
|
||||
stream.write('"');
|
||||
stream.write(':');
|
||||
|
||||
_document.set(value);
|
||||
#ifdef USE_MSGPACK
|
||||
size_t length = serializeMsgPack(_document, stream);
|
||||
#else
|
||||
size_t length = serializeJson(_document, stream);
|
||||
#endif
|
||||
TRACEF("Persistence::serialize: serialized entry %d bytes", length);
|
||||
|
||||
if (length == 0) {
|
||||
// if failed to serialize entry then write empty entry
|
||||
stream.write("{}");
|
||||
}
|
||||
stream.write(',');
|
||||
}
|
||||
stream.write('}');
|
||||
TRACEF("Persistence::serialize: stream size: %d bytes", stream.size());
|
||||
crc = stream.crc();
|
||||
return stream.size();
|
||||
}
|
||||
|
||||
template <typename T> size_t serialize(std::map<Bytes, T>& map, const char* file_path) {
|
||||
uint32_t crc;
|
||||
return serialize(map, file_path, crc);
|
||||
}
|
||||
|
||||
template <typename T> size_t deserialize(std::map<Bytes, T>& map, const char* file_path, uint32_t& crc) {
|
||||
//TRACE("Persistence::deserialize<map<Bytes,T>>");
|
||||
|
||||
// CBA TODO: Use stream here instead to avoid having to buffer entire structure
|
||||
RNS::FileStream stream = RNS::Utilities::OS::open_file(file_path, RNS::FileStream::MODE_READ);
|
||||
if (!stream) {
|
||||
TRACE("Persistence::deserialize: failed to open read stream");
|
||||
return 0;
|
||||
}
|
||||
TRACEF("Persistence::deserialize: size: %d bytes", stream.size());
|
||||
|
||||
map.clear();
|
||||
|
||||
if (stream.size() == 0) {
|
||||
TRACE("Persistence::deserialize: read stream is empty");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// find opening brace
|
||||
if (stream.find('{')) {
|
||||
char key_str[RNS::Type::Reticulum::DESTINATION_LENGTH*2+1] = "";
|
||||
do {
|
||||
key_str[0] = 0;
|
||||
// find map key opening quote
|
||||
if (stream.find('"')) {
|
||||
if (stream.readBytesUntil('"', key_str, sizeof(key_str)) > 0) {
|
||||
Bytes key;
|
||||
key.assignHex(key_str);
|
||||
TRACEF("Persistence::deserialize: key: %s", key.toHex().c_str());
|
||||
if (stream.find(':')) {
|
||||
#ifdef USE_MSGPACK
|
||||
//DeserializationError error = deserializeMsgPack(_document, _buffer.data());
|
||||
DeserializationError error = deserializeMsgPack(_document, stream);
|
||||
#else
|
||||
//DeserializationError error = deserializeJson(_document, _buffer.data());
|
||||
DeserializationError error = deserializeJson(_document, stream);
|
||||
#endif
|
||||
if (!error) {
|
||||
TRACE("Persistence::deserialize: successfully deserialized entry");
|
||||
T obj = _document.as<T>();
|
||||
// CBA Following obj check doesn't work when T is a collection
|
||||
//if (obj) {
|
||||
// TRACE("Persistence::deserialize: failed to compose object");
|
||||
// break;
|
||||
//}
|
||||
map.insert({key, obj});
|
||||
}
|
||||
else {
|
||||
TRACE("Persistence::deserialize: failed to deserialize entry");
|
||||
//break;
|
||||
}
|
||||
|
||||
if (!stream.find(',')) {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (key_str[0] != 0);
|
||||
}
|
||||
crc = stream.crc();
|
||||
return stream.size();
|
||||
}
|
||||
|
||||
template <typename T> size_t deserialize(std::map<Bytes, T>& map, const char* file_path) {
|
||||
uint32_t crc;
|
||||
return deserialize(map, file_path, crc);
|
||||
}
|
||||
#endif
|
||||
|
||||
} }
|
||||
354
lib/microReticulum/src/Utilities/Print.cpp
Executable file
354
lib/microReticulum/src/Utilities/Print.cpp
Executable file
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
Print.cpp - Base class that provides print() and println()
|
||||
Copyright (c) 2008 David A. Mellis. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Modified 23 November 2006 by David A. Mellis
|
||||
Modified December 2014 by Ivan Grokhotkov
|
||||
Modified May 2015 by Michael C. Miller - ESP31B progmem support
|
||||
*/
|
||||
|
||||
#ifndef ARDUINO
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "Print.h"
|
||||
extern "C" {
|
||||
#include "time.h"
|
||||
}
|
||||
|
||||
// Public Methods //////////////////////////////////////////////////////////////
|
||||
|
||||
/* default implementation: may be overridden */
|
||||
size_t Print::write(const uint8_t *buffer, size_t size)
|
||||
{
|
||||
size_t n = 0;
|
||||
while(size--) {
|
||||
n += write(*buffer++);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::printf(const char *format, ...)
|
||||
{
|
||||
char loc_buf[64];
|
||||
char * temp = loc_buf;
|
||||
va_list arg;
|
||||
va_list copy;
|
||||
va_start(arg, format);
|
||||
va_copy(copy, arg);
|
||||
int len = vsnprintf(temp, sizeof(loc_buf), format, copy);
|
||||
va_end(copy);
|
||||
if(len < 0) {
|
||||
va_end(arg);
|
||||
return 0;
|
||||
}
|
||||
if(len >= (int)sizeof(loc_buf)){ // comparation of same sign type for the compiler
|
||||
temp = (char*) malloc(len+1);
|
||||
if(temp == NULL) {
|
||||
va_end(arg);
|
||||
return 0;
|
||||
}
|
||||
len = vsnprintf(temp, len+1, format, arg);
|
||||
}
|
||||
va_end(arg);
|
||||
len = write((uint8_t*)temp, len);
|
||||
if(temp != loc_buf){
|
||||
free(temp);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t Print::print(const std::string &s)
|
||||
{
|
||||
return write(s.c_str(), s.length());
|
||||
}
|
||||
|
||||
size_t Print::print(const char str[])
|
||||
{
|
||||
return write(str);
|
||||
}
|
||||
|
||||
size_t Print::print(char c)
|
||||
{
|
||||
return write(c);
|
||||
}
|
||||
|
||||
size_t Print::print(unsigned char b, int base)
|
||||
{
|
||||
return print((unsigned long) b, base);
|
||||
}
|
||||
|
||||
size_t Print::print(int n, int base)
|
||||
{
|
||||
return print((long) n, base);
|
||||
}
|
||||
|
||||
size_t Print::print(unsigned int n, int base)
|
||||
{
|
||||
return print((unsigned long) n, base);
|
||||
}
|
||||
|
||||
size_t Print::print(long n, int base)
|
||||
{
|
||||
int t = 0;
|
||||
if (base == 10 && n < 0) {
|
||||
t = print('-');
|
||||
n = -n;
|
||||
}
|
||||
return printNumber(static_cast<unsigned long>(n), base) + t;
|
||||
}
|
||||
|
||||
size_t Print::print(unsigned long n, int base)
|
||||
{
|
||||
if(base == 0) {
|
||||
return write(n);
|
||||
} else {
|
||||
return printNumber(n, base);
|
||||
}
|
||||
}
|
||||
|
||||
size_t Print::print(long long n, int base)
|
||||
{
|
||||
int t = 0;
|
||||
if (base == 10 && n < 0) {
|
||||
t = print('-');
|
||||
n = -n;
|
||||
}
|
||||
return printNumber(static_cast<unsigned long long>(n), base) + t;
|
||||
}
|
||||
|
||||
size_t Print::print(unsigned long long n, int base)
|
||||
{
|
||||
if (base == 0) {
|
||||
return write(n);
|
||||
} else {
|
||||
return printNumber(n, base);
|
||||
}
|
||||
}
|
||||
|
||||
size_t Print::print(double n, int digits)
|
||||
{
|
||||
return printFloat(n, digits);
|
||||
}
|
||||
|
||||
size_t Print::print(struct tm * timeinfo, const char * format)
|
||||
{
|
||||
const char * f = format;
|
||||
if(!f){
|
||||
f = "%c";
|
||||
}
|
||||
char buf[64];
|
||||
size_t written = strftime(buf, 64, f, timeinfo);
|
||||
if(written == 0){
|
||||
return written;
|
||||
}
|
||||
return print(buf);
|
||||
}
|
||||
|
||||
size_t Print::println(void)
|
||||
{
|
||||
return print("\r\n");
|
||||
}
|
||||
|
||||
size_t Print::println(const std::string &s)
|
||||
{
|
||||
size_t n = print(s);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(const char c[])
|
||||
{
|
||||
size_t n = print(c);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(char c)
|
||||
{
|
||||
size_t n = print(c);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(unsigned char b, int base)
|
||||
{
|
||||
size_t n = print(b, base);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(int num, int base)
|
||||
{
|
||||
size_t n = print(num, base);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(unsigned int num, int base)
|
||||
{
|
||||
size_t n = print(num, base);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(long num, int base)
|
||||
{
|
||||
size_t n = print(num, base);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(unsigned long num, int base)
|
||||
{
|
||||
size_t n = print(num, base);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(long long num, int base)
|
||||
{
|
||||
size_t n = print(num, base);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(unsigned long long num, int base)
|
||||
{
|
||||
size_t n = print(num, base);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(double num, int digits)
|
||||
{
|
||||
size_t n = print(num, digits);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t Print::println(struct tm * timeinfo, const char * format)
|
||||
{
|
||||
size_t n = print(timeinfo, format);
|
||||
n += println();
|
||||
return n;
|
||||
}
|
||||
|
||||
// Private Methods /////////////////////////////////////////////////////////////
|
||||
|
||||
size_t Print::printNumber(unsigned long n, uint8_t base)
|
||||
{
|
||||
char buf[8 * sizeof(n) + 1]; // Assumes 8-bit chars plus zero byte.
|
||||
char *str = &buf[sizeof(buf) - 1];
|
||||
|
||||
*str = '\0';
|
||||
|
||||
// prevent crash if called with base == 1
|
||||
if(base < 2) {
|
||||
base = 10;
|
||||
}
|
||||
|
||||
do {
|
||||
char c = n % base;
|
||||
n /= base;
|
||||
|
||||
*--str = c < 10 ? c + '0' : c + 'A' - 10;
|
||||
} while (n);
|
||||
|
||||
return write(str);
|
||||
}
|
||||
|
||||
size_t Print::printNumber(unsigned long long n, uint8_t base)
|
||||
{
|
||||
char buf[8 * sizeof(n) + 1]; // Assumes 8-bit chars plus zero byte.
|
||||
char* str = &buf[sizeof(buf) - 1];
|
||||
|
||||
*str = '\0';
|
||||
|
||||
// prevent crash if called with base == 1
|
||||
if (base < 2) {
|
||||
base = 10;
|
||||
}
|
||||
|
||||
do {
|
||||
auto m = n;
|
||||
n /= base;
|
||||
char c = m - base * n;
|
||||
|
||||
*--str = c < 10 ? c + '0' : c + 'A' - 10;
|
||||
} while (n);
|
||||
|
||||
return write(str);
|
||||
}
|
||||
|
||||
size_t Print::printFloat(double number, uint8_t digits)
|
||||
{
|
||||
size_t n = 0;
|
||||
|
||||
if(isnan(number)) {
|
||||
return print("nan");
|
||||
}
|
||||
if(isinf(number)) {
|
||||
return print("inf");
|
||||
}
|
||||
if(number > 4294967040.0) {
|
||||
return print("ovf"); // constant determined empirically
|
||||
}
|
||||
if(number < -4294967040.0) {
|
||||
return print("ovf"); // constant determined empirically
|
||||
}
|
||||
|
||||
// Handle negative numbers
|
||||
if(number < 0.0) {
|
||||
n += print('-');
|
||||
number = -number;
|
||||
}
|
||||
|
||||
// Round correctly so that print(1.999, 2) prints as "2.00"
|
||||
double rounding = 0.5;
|
||||
for(uint8_t i = 0; i < digits; ++i) {
|
||||
rounding /= 10.0;
|
||||
}
|
||||
|
||||
number += rounding;
|
||||
|
||||
// Extract the integer part of the number and print it
|
||||
unsigned long int_part = (unsigned long) number;
|
||||
double remainder = number - (double) int_part;
|
||||
n += print(int_part);
|
||||
|
||||
// Print the decimal point, but only if there are digits beyond
|
||||
if(digits > 0) {
|
||||
n += print(".");
|
||||
}
|
||||
|
||||
// Extract digits from the remainder one at a time
|
||||
while(digits-- > 0) {
|
||||
remainder *= 10.0;
|
||||
int toPrint = int(remainder);
|
||||
n += print(toPrint);
|
||||
remainder -= toPrint;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
#endif
|
||||
121
lib/microReticulum/src/Utilities/Print.h
Executable file
121
lib/microReticulum/src/Utilities/Print.h
Executable file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
Print.h - Base class that provides print() and println()
|
||||
Copyright (c) 2008 David A. Mellis. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef ARDUINO
|
||||
|
||||
#ifndef Print_h
|
||||
#define Print_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#include <string>
|
||||
|
||||
//#define DEC 10
|
||||
//#define HEX 16
|
||||
//#define OCT 8
|
||||
//#define BIN 2
|
||||
static constexpr uint8_t DEC {10};
|
||||
static constexpr uint8_t HEX {16};
|
||||
static constexpr uint8_t OCT {8};
|
||||
static constexpr uint8_t BIN {2};
|
||||
|
||||
class Print
|
||||
{
|
||||
private:
|
||||
int write_error;
|
||||
size_t printNumber(unsigned long, uint8_t);
|
||||
size_t printNumber(unsigned long long, uint8_t);
|
||||
size_t printFloat(double, uint8_t);
|
||||
protected:
|
||||
void setWriteError(int err = 1)
|
||||
{
|
||||
write_error = err;
|
||||
}
|
||||
public:
|
||||
Print() :
|
||||
write_error(0)
|
||||
{
|
||||
}
|
||||
virtual ~Print() {}
|
||||
int getWriteError()
|
||||
{
|
||||
return write_error;
|
||||
}
|
||||
void clearWriteError()
|
||||
{
|
||||
setWriteError(0);
|
||||
}
|
||||
|
||||
virtual size_t write(uint8_t) = 0;
|
||||
size_t write(const char *str)
|
||||
{
|
||||
if(str == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return write((const uint8_t *) str, strlen(str));
|
||||
}
|
||||
virtual size_t write(const uint8_t *buffer, size_t size);
|
||||
size_t write(const char *buffer, size_t size)
|
||||
{
|
||||
return write((const uint8_t *) buffer, size);
|
||||
}
|
||||
|
||||
size_t printf(const char * format, ...) __attribute__ ((format (printf, 2, 3)));
|
||||
|
||||
// add availableForWrite to make compatible with Arduino Print.h
|
||||
// default to zero, meaning "a single write may block"
|
||||
// should be overriden by subclasses with buffering
|
||||
virtual int availableForWrite() { return 0; }
|
||||
size_t print(const std::string &);
|
||||
size_t print(const char[]);
|
||||
size_t print(char);
|
||||
size_t print(unsigned char, int = DEC);
|
||||
size_t print(int, int = DEC);
|
||||
size_t print(unsigned int, int = DEC);
|
||||
size_t print(long, int = DEC);
|
||||
size_t print(unsigned long, int = DEC);
|
||||
size_t print(long long, int = DEC);
|
||||
size_t print(unsigned long long, int = DEC);
|
||||
size_t print(double, int = 2);
|
||||
size_t print(struct tm * timeinfo, const char * format = NULL);
|
||||
|
||||
size_t println(const std::string &s);
|
||||
size_t println(const char[]);
|
||||
size_t println(char);
|
||||
size_t println(unsigned char, int = DEC);
|
||||
size_t println(int, int = DEC);
|
||||
size_t println(unsigned int, int = DEC);
|
||||
size_t println(long, int = DEC);
|
||||
size_t println(unsigned long, int = DEC);
|
||||
size_t println(long long, int = DEC);
|
||||
size_t println(unsigned long long, int = DEC);
|
||||
size_t println(double, int = 2);
|
||||
size_t println(struct tm * timeinfo, const char * format = NULL);
|
||||
size_t println(void);
|
||||
|
||||
virtual void flush() { /* Empty implementation for backward compatibility */ }
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
339
lib/microReticulum/src/Utilities/Stream.cpp
Executable file
339
lib/microReticulum/src/Utilities/Stream.cpp
Executable file
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
Stream.cpp - adds parsing methods to Stream class
|
||||
Copyright (c) 2008 David A. Mellis. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Created July 2011
|
||||
parsing functions based on TextFinder library by Michael Margolis
|
||||
*/
|
||||
|
||||
#ifndef ARDUINO
|
||||
|
||||
#include "Stream.h"
|
||||
#include "OS.h"
|
||||
|
||||
#define PARSE_TIMEOUT 1000 // default number of milli-seconds to wait
|
||||
#define NO_SKIP_CHAR 1 // a magic char not found in a valid ASCII numeric field
|
||||
|
||||
// private method to read stream with timeout
|
||||
int Stream::timedRead()
|
||||
{
|
||||
int c;
|
||||
_startMillis = RNS::Utilities::OS::ltime();
|
||||
do {
|
||||
c = read();
|
||||
if(c >= 0) {
|
||||
return c;
|
||||
}
|
||||
} while(RNS::Utilities::OS::ltime() - _startMillis < _timeout);
|
||||
return -1; // -1 indicates timeout
|
||||
}
|
||||
|
||||
// private method to peek stream with timeout
|
||||
int Stream::timedPeek()
|
||||
{
|
||||
int c;
|
||||
_startMillis = RNS::Utilities::OS::ltime();
|
||||
do {
|
||||
c = peek();
|
||||
if(c >= 0) {
|
||||
return c;
|
||||
}
|
||||
} while(RNS::Utilities::OS::ltime() - _startMillis < _timeout);
|
||||
return -1; // -1 indicates timeout
|
||||
}
|
||||
|
||||
// returns peek of the next digit in the stream or -1 if timeout
|
||||
// discards non-numeric characters
|
||||
int Stream::peekNextDigit()
|
||||
{
|
||||
int c;
|
||||
while(1) {
|
||||
c = timedPeek();
|
||||
if(c < 0) {
|
||||
return c; // timeout
|
||||
}
|
||||
if(c == '-') {
|
||||
return c;
|
||||
}
|
||||
if(c >= '0' && c <= '9') {
|
||||
return c;
|
||||
}
|
||||
read(); // discard non-numeric
|
||||
}
|
||||
}
|
||||
|
||||
// Public Methods
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
void Stream::setTimeout(unsigned long timeout) // sets the maximum number of milliseconds to wait
|
||||
{
|
||||
_timeout = timeout;
|
||||
}
|
||||
unsigned long Stream::getTimeout(void) {
|
||||
return _timeout;
|
||||
}
|
||||
|
||||
// find returns true if the target string is found
|
||||
bool Stream::find(const char *target)
|
||||
{
|
||||
return findUntil(target, strlen(target), NULL, 0);
|
||||
}
|
||||
|
||||
// reads data from the stream until the target string of given length is found
|
||||
// returns true if target string is found, false if timed out
|
||||
bool Stream::find(const char *target, size_t length)
|
||||
{
|
||||
return findUntil(target, length, NULL, 0);
|
||||
}
|
||||
|
||||
// as find but search ends if the terminator string is found
|
||||
bool Stream::findUntil(const char *target, const char *terminator)
|
||||
{
|
||||
return findUntil(target, strlen(target), terminator, strlen(terminator));
|
||||
}
|
||||
|
||||
// reads data from the stream until the target string of the given length is found
|
||||
// search terminated if the terminator string is found
|
||||
// returns true if target string is found, false if terminated or timed out
|
||||
bool Stream::findUntil(const char *target, size_t targetLen, const char *terminator, size_t termLen)
|
||||
{
|
||||
if (terminator == NULL) {
|
||||
MultiTarget t[1] = {{target, targetLen, 0}};
|
||||
return findMulti(t, 1) == 0 ? true : false;
|
||||
} else {
|
||||
MultiTarget t[2] = {{target, targetLen, 0}, {terminator, termLen, 0}};
|
||||
return findMulti(t, 2) == 0 ? true : false;
|
||||
}
|
||||
}
|
||||
|
||||
int Stream::findMulti( struct Stream::MultiTarget *targets, int tCount) {
|
||||
// any zero length target string automatically matches and would make
|
||||
// a mess of the rest of the algorithm.
|
||||
for (struct MultiTarget *t = targets; t < targets+tCount; ++t) {
|
||||
if (t->len <= 0)
|
||||
return t - targets;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
int c = timedRead();
|
||||
if (c < 0)
|
||||
return -1;
|
||||
|
||||
for (struct MultiTarget *t = targets; t < targets+tCount; ++t) {
|
||||
// the simple case is if we match, deal with that first.
|
||||
if (c == t->str[t->index]) {
|
||||
if (++t->index == t->len)
|
||||
return t - targets;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
// if not we need to walk back and see if we could have matched further
|
||||
// down the stream (ie '1112' doesn't match the first position in '11112'
|
||||
// but it will match the second position so we can't just reset the current
|
||||
// index to 0 when we find a mismatch.
|
||||
if (t->index == 0)
|
||||
continue;
|
||||
|
||||
int origIndex = t->index;
|
||||
do {
|
||||
--t->index;
|
||||
// first check if current char works against the new current index
|
||||
if (c != t->str[t->index])
|
||||
continue;
|
||||
|
||||
// if it's the only char then we're good, nothing more to check
|
||||
if (t->index == 0) {
|
||||
t->index++;
|
||||
break;
|
||||
}
|
||||
|
||||
// otherwise we need to check the rest of the found string
|
||||
int diff = origIndex - t->index;
|
||||
size_t i;
|
||||
for (i = 0; i < t->index; ++i) {
|
||||
if (t->str[i] != t->str[i + diff])
|
||||
break;
|
||||
}
|
||||
|
||||
// if we successfully got through the previous loop then our current
|
||||
// index is good.
|
||||
if (i == t->index) {
|
||||
t->index++;
|
||||
break;
|
||||
}
|
||||
|
||||
// otherwise we just try the next index
|
||||
} while (t->index);
|
||||
}
|
||||
}
|
||||
// unreachable
|
||||
return -1;
|
||||
}
|
||||
|
||||
// returns the first valid (long) integer value from the current position.
|
||||
// initial characters that are not digits (or the minus sign) are skipped
|
||||
// function is terminated by the first character that is not a digit.
|
||||
long Stream::parseInt()
|
||||
{
|
||||
return parseInt(NO_SKIP_CHAR); // terminate on first non-digit character (or timeout)
|
||||
}
|
||||
|
||||
// as above but a given skipChar is ignored
|
||||
// this allows format characters (typically commas) in values to be ignored
|
||||
long Stream::parseInt(char skipChar)
|
||||
{
|
||||
bool isNegative = false;
|
||||
long value = 0;
|
||||
int c;
|
||||
|
||||
c = peekNextDigit();
|
||||
// ignore non numeric leading characters
|
||||
if(c < 0) {
|
||||
return 0; // zero returned if timeout
|
||||
}
|
||||
|
||||
do {
|
||||
if(c == skipChar) {
|
||||
} // ignore this charactor
|
||||
else if(c == '-') {
|
||||
isNegative = true;
|
||||
} else if(c >= '0' && c <= '9') { // is c a digit?
|
||||
value = value * 10 + c - '0';
|
||||
}
|
||||
read(); // consume the character we got with peek
|
||||
c = timedPeek();
|
||||
} while((c >= '0' && c <= '9') || c == skipChar);
|
||||
|
||||
if(isNegative) {
|
||||
value = -value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// as parseInt but returns a floating point value
|
||||
float Stream::parseFloat()
|
||||
{
|
||||
return parseFloat(NO_SKIP_CHAR);
|
||||
}
|
||||
|
||||
// as above but the given skipChar is ignored
|
||||
// this allows format characters (typically commas) in values to be ignored
|
||||
float Stream::parseFloat(char skipChar)
|
||||
{
|
||||
bool isNegative = false;
|
||||
bool isFraction = false;
|
||||
long value = 0;
|
||||
int c;
|
||||
float fraction = 1.0;
|
||||
|
||||
c = peekNextDigit();
|
||||
// ignore non numeric leading characters
|
||||
if(c < 0) {
|
||||
return 0; // zero returned if timeout
|
||||
}
|
||||
|
||||
do {
|
||||
if(c == skipChar) {
|
||||
} // ignore
|
||||
else if(c == '-') {
|
||||
isNegative = true;
|
||||
} else if(c == '.') {
|
||||
isFraction = true;
|
||||
} else if(c >= '0' && c <= '9') { // is c a digit?
|
||||
value = value * 10 + c - '0';
|
||||
if(isFraction) {
|
||||
fraction *= 0.1f;
|
||||
}
|
||||
}
|
||||
read(); // consume the character we got with peek
|
||||
c = timedPeek();
|
||||
} while((c >= '0' && c <= '9') || c == '.' || c == skipChar);
|
||||
|
||||
if(isNegative) {
|
||||
value = -value;
|
||||
}
|
||||
if(isFraction) {
|
||||
return value * fraction;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
// read characters from stream into buffer
|
||||
// terminates if length characters have been read, or timeout (see setTimeout)
|
||||
// returns the number of characters placed in the buffer
|
||||
// the buffer is NOT null terminated.
|
||||
//
|
||||
size_t Stream::readBytes(char *buffer, size_t length)
|
||||
{
|
||||
size_t count = 0;
|
||||
while(count < length) {
|
||||
int c = timedRead();
|
||||
if(c < 0) {
|
||||
break;
|
||||
}
|
||||
*buffer++ = (char) c;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// as readBytes with terminator character
|
||||
// terminates if length characters have been read, timeout, or if the terminator character detected
|
||||
// returns the number of characters placed in the buffer (0 means no valid data found)
|
||||
|
||||
size_t Stream::readBytesUntil(char terminator, char *buffer, size_t length)
|
||||
{
|
||||
if(length < 1) {
|
||||
return 0;
|
||||
}
|
||||
size_t index = 0;
|
||||
while(index < length) {
|
||||
int c = timedRead();
|
||||
if(c < 0 || c == terminator) {
|
||||
break;
|
||||
}
|
||||
*buffer++ = (char) c;
|
||||
index++;
|
||||
}
|
||||
return index; // return number of characters, not including null terminator
|
||||
}
|
||||
|
||||
std::string Stream::readString()
|
||||
{
|
||||
std::string ret;
|
||||
int c = timedRead();
|
||||
while(c >= 0) {
|
||||
ret += (char) c;
|
||||
c = timedRead();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string Stream::readStringUntil(char terminator)
|
||||
{
|
||||
std::string ret;
|
||||
int c = timedRead();
|
||||
while(c >= 0 && c != terminator) {
|
||||
ret += (char) c;
|
||||
c = timedRead();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
143
lib/microReticulum/src/Utilities/Stream.h
Executable file
143
lib/microReticulum/src/Utilities/Stream.h
Executable file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
Stream.h - base class for character-based streams.
|
||||
Copyright (c) 2010 David A. Mellis. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
parsing functions based on TextFinder library by Michael Margolis
|
||||
*/
|
||||
|
||||
#ifndef ARDUINO
|
||||
|
||||
#ifndef Stream_h
|
||||
#define Stream_h
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "Print.h"
|
||||
|
||||
// compatability macros for testing
|
||||
/*
|
||||
#define getInt() parseInt()
|
||||
#define getInt(skipChar) parseInt(skipchar)
|
||||
#define getFloat() parseFloat()
|
||||
#define getFloat(skipChar) parseFloat(skipChar)
|
||||
#define getString( pre_string, post_string, buffer, length)
|
||||
readBytesBetween( pre_string, terminator, buffer, length)
|
||||
*/
|
||||
|
||||
class Stream: public Print
|
||||
{
|
||||
protected:
|
||||
unsigned long _timeout; // number of milliseconds to wait for the next char before aborting timed read
|
||||
unsigned long _startMillis; // used for timeout measurement
|
||||
int timedRead(); // private method to read stream with timeout
|
||||
int timedPeek(); // private method to peek stream with timeout
|
||||
int peekNextDigit(); // returns the next numeric digit in the stream or -1 if timeout
|
||||
|
||||
public:
|
||||
virtual int available() = 0;
|
||||
virtual int read() = 0;
|
||||
virtual int peek() = 0;
|
||||
|
||||
Stream():_startMillis(0)
|
||||
{
|
||||
_timeout = 1000;
|
||||
}
|
||||
virtual ~Stream() {}
|
||||
|
||||
// parsing methods
|
||||
|
||||
void setTimeout(unsigned long timeout); // sets maximum milliseconds to wait for stream data, default is 1 second
|
||||
unsigned long getTimeout(void);
|
||||
|
||||
bool find(const char *target); // reads data from the stream until the target string is found
|
||||
bool find(uint8_t *target)
|
||||
{
|
||||
return find((char *) target);
|
||||
}
|
||||
// returns true if target string is found, false if timed out (see setTimeout)
|
||||
|
||||
bool find(const char *target, size_t length); // reads data from the stream until the target string of given length is found
|
||||
bool find(const uint8_t *target, size_t length)
|
||||
{
|
||||
return find((char *) target, length);
|
||||
}
|
||||
// returns true if target string is found, false if timed out
|
||||
|
||||
bool find(char target)
|
||||
{
|
||||
return find (&target, 1);
|
||||
}
|
||||
|
||||
bool findUntil(const char *target, const char *terminator); // as find but search ends if the terminator string is found
|
||||
bool findUntil(const uint8_t *target, const char *terminator)
|
||||
{
|
||||
return findUntil((char *) target, terminator);
|
||||
}
|
||||
|
||||
bool findUntil(const char *target, size_t targetLen, const char *terminate, size_t termLen); // as above but search ends if the terminate string is found
|
||||
bool findUntil(const uint8_t *target, size_t targetLen, const char *terminate, size_t termLen)
|
||||
{
|
||||
return findUntil((char *) target, targetLen, terminate, termLen);
|
||||
}
|
||||
|
||||
long parseInt(); // returns the first valid (long) integer value from the current position.
|
||||
// initial characters that are not digits (or the minus sign) are skipped
|
||||
// integer is terminated by the first character that is not a digit.
|
||||
|
||||
float parseFloat(); // float version of parseInt
|
||||
|
||||
virtual size_t readBytes(char *buffer, size_t length); // read chars from stream into buffer
|
||||
virtual size_t readBytes(uint8_t *buffer, size_t length)
|
||||
{
|
||||
return readBytes((char *) buffer, length);
|
||||
}
|
||||
// terminates if length characters have been read or timeout (see setTimeout)
|
||||
// returns the number of characters placed in the buffer (0 means no valid data found)
|
||||
|
||||
size_t readBytesUntil(char terminator, char *buffer, size_t length); // as readBytes with terminator character
|
||||
size_t readBytesUntil(char terminator, uint8_t *buffer, size_t length)
|
||||
{
|
||||
return readBytesUntil(terminator, (char *) buffer, length);
|
||||
}
|
||||
// terminates if length characters have been read, timeout, or if the terminator character detected
|
||||
// returns the number of characters placed in the buffer (0 means no valid data found)
|
||||
|
||||
// Arduino String functions to be added here
|
||||
virtual std::string readString();
|
||||
std::string readStringUntil(char terminator);
|
||||
|
||||
protected:
|
||||
long parseInt(char skipChar); // as above but the given skipChar is ignored
|
||||
// as above but the given skipChar is ignored
|
||||
// this allows format characters (typically commas) in values to be ignored
|
||||
|
||||
float parseFloat(char skipChar); // as above but the given skipChar is ignored
|
||||
|
||||
struct MultiTarget {
|
||||
const char *str; // string you're searching for
|
||||
size_t len; // length of string you're searching for
|
||||
size_t index; // index used by the search routine.
|
||||
};
|
||||
|
||||
// This allows you to search for an arbitrary number of strings.
|
||||
// Returns index of the target that is found first or -1 if timeout occurs.
|
||||
int findMulti(struct MultiTarget *targets, int tCount);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
1268
lib/microReticulum/src/Utilities/tlsf.c
Executable file
1268
lib/microReticulum/src/Utilities/tlsf.c
Executable file
File diff suppressed because it is too large
Load Diff
90
lib/microReticulum/src/Utilities/tlsf.h
Executable file
90
lib/microReticulum/src/Utilities/tlsf.h
Executable file
@@ -0,0 +1,90 @@
|
||||
#ifndef INCLUDED_tlsf
|
||||
#define INCLUDED_tlsf
|
||||
|
||||
/*
|
||||
** Two Level Segregated Fit memory allocator, version 3.1.
|
||||
** Written by Matthew Conte
|
||||
** http://tlsf.baisoku.org
|
||||
**
|
||||
** Based on the original documentation by Miguel Masmano:
|
||||
** http://www.gii.upv.es/tlsf/main/docs
|
||||
**
|
||||
** This implementation was written to the specification
|
||||
** of the document, therefore no GPL restrictions apply.
|
||||
**
|
||||
** Copyright (c) 2006-2016, Matthew Conte
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** * Neither the name of the copyright holder nor the
|
||||
** names of its contributors may be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
** DISCLAIMED. IN NO EVENT SHALL MATTHEW CONTE BE LIABLE FOR ANY
|
||||
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* tlsf_t: a TLSF structure. Can contain 1 to N pools. */
|
||||
/* pool_t: a block of memory that TLSF can manage. */
|
||||
typedef void* tlsf_t;
|
||||
typedef void* pool_t;
|
||||
|
||||
/* Create/destroy a memory pool. */
|
||||
tlsf_t tlsf_create(void* mem);
|
||||
tlsf_t tlsf_create_with_pool(void* mem, size_t bytes);
|
||||
void tlsf_destroy(tlsf_t tlsf);
|
||||
pool_t tlsf_get_pool(tlsf_t tlsf);
|
||||
|
||||
/* Add/remove memory pools. */
|
||||
pool_t tlsf_add_pool(tlsf_t tlsf, void* mem, size_t bytes);
|
||||
void tlsf_remove_pool(tlsf_t tlsf, pool_t pool);
|
||||
|
||||
/* malloc/memalign/realloc/free replacements. */
|
||||
void* tlsf_malloc(tlsf_t tlsf, size_t bytes);
|
||||
void* tlsf_memalign(tlsf_t tlsf, size_t align, size_t bytes);
|
||||
void* tlsf_realloc(tlsf_t tlsf, void* ptr, size_t size);
|
||||
void tlsf_free(tlsf_t tlsf, void* ptr);
|
||||
|
||||
/* Returns internal block size, not original request size */
|
||||
size_t tlsf_block_size(void* ptr);
|
||||
|
||||
/* Overheads/limits of internal structures. */
|
||||
size_t tlsf_size(void);
|
||||
size_t tlsf_align_size(void);
|
||||
size_t tlsf_block_size_min(void);
|
||||
size_t tlsf_block_size_max(void);
|
||||
size_t tlsf_pool_overhead(void);
|
||||
size_t tlsf_alloc_overhead(void);
|
||||
|
||||
/* Debugging. */
|
||||
typedef void (*tlsf_walker)(void* ptr, size_t size, int used, void* user);
|
||||
void tlsf_walk_pool(pool_t pool, tlsf_walker walker, void* user);
|
||||
/* Returns nonzero if any internal consistency check fails. */
|
||||
int tlsf_check(tlsf_t tlsf);
|
||||
int tlsf_check_pool(pool_t pool);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user