update espMqttClient

This commit is contained in:
technyon
2024-07-25 13:26:01 +02:00
parent f1f1612cd4
commit d64031d710
22 changed files with 589 additions and 812 deletions

View File

@@ -1,34 +0,0 @@
---
name: Bug report
about: Create a report to help us improve espMqttClient
title: "[BUG]"
labels: ''
assignees: ''
---
#### Do not use to discuss topics!
**Describe the bug**
A clear and concise description of what the bug is.
Which platform, esp8266 or esp32?
Do you use TLS or not?
Do you use an IDE (Arduino, Platformio...)?
Which version of the Arduino framework?
Please include any debug output and/or decoded stack trace if applicable.
**Expected behaviour**
A clear and concise description of what you expected to happen.
**To Reproduce**
Steps to reproduce the behaviour:
**Example code**
```cpp
// Put code here to reproduce the bug, if possible
```
**Additional context**
Add any other context about the problem here.

View File

@@ -1,2 +0,0 @@
listener 1883
allow_anonymous true

View File

@@ -1,9 +0,0 @@
name: Arduino Lint
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: arduino/arduino-lint-action@v1

View File

@@ -1,62 +0,0 @@
name: Build with Arduino IDE
on:
- push
- pull_request
jobs:
build-for-esp8266:
runs-on: ubuntu-latest
strategy:
matrix:
fqbn:
- esp8266:esp8266:generic
steps:
- uses: actions/checkout@v3
- uses: arduino/compile-sketches@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
fqbn: ${{ matrix.fqbn }}
enable-deltas-report: true
platforms: |
- name: esp8266:esp8266
source-url: https://arduino.esp8266.com/stable/package_esp8266com_index.json
sketch-paths: |
- examples/largepayload-esp8266
- examples/simple-esp8266
- examples/simpleAsync-esp8266
- examples/tls-esp8266
libraries: |
- name: espMqttClient
source-path: ./
- name: ESPAsyncTCP
source-url: https://github.com/me-no-dev/ESPAsyncTCP.git
build-for-esp32:
runs-on: ubuntu-latest
strategy:
matrix:
fqbn:
- esp32:esp32:esp32
steps:
- uses: actions/checkout@v3
- uses: arduino/compile-sketches@v1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
fqbn: ${{ matrix.fqbn }}
enable-deltas-report: true
platforms: |
- name: esp32:esp32
source-url: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
sketch-paths: |
- examples/simple-esp32
- examples/simpleAsync-esp32
- examples/tls-esp32
libraries: |
- name: espMqttClient
source-path: ./
- name: AsyncTCP
source-url: https://github.com/me-no-dev/AsyncTCP.git

View File

@@ -1,90 +0,0 @@
name: Build with Platformio
on: [push, pull_request]
jobs:
build-for-esp8266:
runs-on: ubuntu-latest
strategy:
matrix:
example: [
examples/largepayload-esp8266/largepayload-esp8266.ino,
examples/ota-esp8266/ota-esp8266.ino,
examples/simple-esp8266/simple-esp8266.ino,
examples/simpleAsync-esp8266/simpleAsync-esp8266.ino,
examples/tls-esp8266/tls-esp8266.ino
]
steps:
- uses: actions/checkout@v3
- uses: actions/cache@v3
with:
path: |
~/.cache/pip
~/.platformio/.cache
key: ${{ runner.os }}-pio
- uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install PlatformIO Core
run: pip install --upgrade platformio
- name: Download external libraries
run: pio pkg install --global --library me-no-dev/ESPAsyncTCP
- name: Build PlatformIO examples
run: pio ci --lib="." --board=d1_mini
env:
PLATFORMIO_CI_SRC: ${{ matrix.example }}
build-for-esp32:
runs-on: ubuntu-latest
strategy:
matrix:
example: [
examples/notask-esp32/notask-esp32.ino,
examples/simple-esp32/simple-esp32.ino,
examples/simpleAsync-esp32/simpleAsync-esp32.ino,
examples/tls-esp32/tls-esp32.ino
]
steps:
- uses: actions/checkout@v3
- uses: actions/cache@v3
with:
path: |
~/.cache/pip
~/.platformio/.cache
key: ${{ runner.os }}-pio
- uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install PlatformIO Core
run: pip install --upgrade platformio
- name: Download external libraries
run: pio pkg install --global --library me-no-dev/AsyncTCP
- name: Build PlatformIO examples
run: pio ci --lib="." --board=lolin32
env:
PLATFORMIO_CI_SRC: ${{ matrix.example }}
build-for-linux:
runs-on: ubuntu-latest
strategy:
matrix:
example: [
examples/simple-linux/main.cpp
]
steps:
- uses: actions/checkout@v3
- uses: actions/cache@v3
with:
path: |
~/.cache/pip
~/.platformio/.cache
key: ${{ runner.os }}-pio
- uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install PlatformIO Core
run: pip install --upgrade platformio
- name: Build PlatformIO examples
run: pio ci --lib="." --project-conf="./examples/simple-linux/platformio.ini"
env:
PLATFORMIO_CI_SRC: ${{ matrix.example }}

View File

@@ -1,22 +0,0 @@
name: Cppcheck
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install platformio
- name: Cppcheck
run: |
pio check --fail-on-defect=medium --fail-on-defect=high --flags "--inline-suppr --enable=warning --enable=style --enable=performance --suppress=unusedFunction --suppress=preprocessorErrorDirective" --skip-packages

View File

@@ -1,22 +0,0 @@
name: cpplint
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install cpplint
- name: Linting
run: |
cpplint --repository=. --recursive --filter=-whitespace/line_length,-build/include ./src

View File

@@ -1,29 +0,0 @@
name: Test with Platformio
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: |
sudo apt update && sudo apt install -y valgrind
python -m pip install --upgrade pip
pip install platformio
- name: Start Mosquitto
uses: namoshek/mosquitto-github-action@v1
with:
version: '2.0'
ports: '1883:1883'
config: ${{ github.workspace }}/.github/etc/mosquitto.conf
- name: Test
run: |
pio test -e native -v

View File

@@ -1,5 +0,0 @@
.pio
.vscode
cov
*cov.info
.idea

View File

@@ -1,105 +1,105 @@
# Memory Pool
EARLY VERSION. USE AT OWN RISK.
### Description
This is a simple memory pool that doesn't solve the fragmentation problem but contains it. Inside the pool you will still suffer memory fragmentation. The upside is that you're not restricted on memory size. As long as it fits in the pool, you can request any size!
For applications where the (maximum) size to allocate is known, a simple fixed block size memory pool is available. There is no memory fragmentation happening in this case. The downside is wastage of memory if you need less then the specified blocksize.
#### Features
- pool memory is statically allocated
- pool size adjusts on architecture
- no size calculation required: input number of blocks and size of block
- header-only library
- Variable size pool: no restriction on allocated size
- Variable size pool: malloc and free are O(n); The number of allocated blocks affects lookup.
- Fixed size pool: malloc and free are O(1).
[![Test with Platformio](https://github.com/bertmelis/MemoryPool/actions/workflows/test-platformio.yml/badge.svg)](https://github.com/bertmelis/MemoryPool/actions/workflows/test-platformio.yml)
[![cpplint](https://github.com/bertmelis/MemoryPool/actions/workflows/cpplint.yml/badge.svg)](https://github.com/bertmelis/MemoryPool/actions/workflows/cpplint.yml)
<!---[![cppcheck](https://github.com/bertmelis/MemoryPool/actions/workflows/cppcheck.yml/badge.svg)](https://github.com/bertmelis/MemoryPool/actions/workflows/cppcheck.yml)--->
### Usage
#### Variable size pool
```cpp
#include <MemoryPool.h>
Struct MyStruct {
unsigned int id;
std::size_t size;
unsigned char data[256];
};
// pool will be able to hold 10 blocks the size of MyStruct
MemoryPool::Variable<10, sizeof(MyStruct)> pool;
// you can allocate the specified blocksize
// allocation is done in number of 'unsigned char'
MyStruct* s = reinterpret_cast<MyStruct*>(pool.malloc(sizeof(MyStruct)));
// you can allocate less than the specified blocksize
int* i = reinterpret_cast<int*>(pool.malloc(sizeof(int)));
// you can allocate more than the specified blocksize
unsigned char* m = reinterpret_cast<unsigned char*>(pool.malloc(400));
pool.free(s);
pool.free(i);
pool.free(m);
```
#### Fixed size pool
```cpp
#include <MemoryPool.h>
Struct MyStruct {
unsigned int id;
std::size_t size;
unsigned char data[256];
};
// pool will be able to hold 10 blocks the size of MyStruct
MemoryPool::Fixed<10, sizeof(MyStruct)> pool;
// there is no size argument in the malloc function!
MyStruct* s = reinterpret_cast<MyStruct*>(pool.malloc());
// you can allocate less than the specified blocksize
int* i = reinterpret_cast<int*>(pool.malloc());
pool.free(s);
pool.free(i);
```
#### How it works
##### Variable size pool
Free blocks are organized as a linked list with their header (contains pointer to next and size). An allocated block also has this header with it's pointer set to `nullptr`. Therefore, each allocation wastes memory the size of the header (`sizeof(void*) + sizeof(std::size_t)`). On creation, the pool calculations the needed space to store the number of blocks wich each their header.
However, memory allocation isn't restricted the the specified blocksize. So in reality, you can allocate more if you allocate larger chunks because less memory blocks means less headers. After all, memory needs to be contiguous.
If you inspect the pool you'll see that a free pool only has one big block.
Allocation is linear: the pool is iterated until a suitable spot is found.
Freeing is also linear as the pool is traversed to insert the chunk in the linked list of free blocks
When freeing, free blocks which are adjacent are combined into one.
##### Fixed size pool
The fixed size pool is implemented as an array. Free blocks are saved as a linked list in this array.
### Bugs and feature requests
Please use Github's facilities to get in touch.
### License
This library is released under the MIT Licence. A copy is included in the repo.
# Memory Pool
EARLY VERSION. USE AT OWN RISK.
### Description
This is a simple memory pool that doesn't solve the fragmentation problem but contains it. Inside the pool you will still suffer memory fragmentation. The upside is that you're not restricted on memory size. As long as it fits in the pool, you can request any size!
For applications where the (maximum) size to allocate is known, a simple fixed block size memory pool is available. There is no memory fragmentation happening in this case. The downside is wastage of memory if you need less then the specified blocksize.
#### Features
- pool memory is statically allocated
- pool size adjusts on architecture
- no size calculation required: input number of blocks and size of block
- header-only library
- Variable size pool: no restriction on allocated size
- Variable size pool: malloc and free are O(n); The number of allocated blocks affects lookup.
- Fixed size pool: malloc and free are O(1).
[![Test with Platformio](https://github.com/bertmelis/MemoryPool/actions/workflows/test-platformio.yml/badge.svg)](https://github.com/bertmelis/MemoryPool/actions/workflows/test-platformio.yml)
[![cpplint](https://github.com/bertmelis/MemoryPool/actions/workflows/cpplint.yml/badge.svg)](https://github.com/bertmelis/MemoryPool/actions/workflows/cpplint.yml)
<!---[![cppcheck](https://github.com/bertmelis/MemoryPool/actions/workflows/cppcheck.yml/badge.svg)](https://github.com/bertmelis/MemoryPool/actions/workflows/cppcheck.yml)--->
### Usage
#### Variable size pool
```cpp
#include <MemoryPool.h>
Struct MyStruct {
unsigned int id;
std::size_t size;
unsigned char data[256];
};
// pool will be able to hold 10 blocks the size of MyStruct
MemoryPool::Variable<10, sizeof(MyStruct)> pool;
// you can allocate the specified blocksize
// allocation is done in number of 'unsigned char'
MyStruct* s = reinterpret_cast<MyStruct*>(pool.malloc(sizeof(MyStruct)));
// you can allocate less than the specified blocksize
int* i = reinterpret_cast<int*>(pool.malloc(sizeof(int)));
// you can allocate more than the specified blocksize
unsigned char* m = reinterpret_cast<unsigned char*>(pool.malloc(400));
pool.free(s);
pool.free(i);
pool.free(m);
```
#### Fixed size pool
```cpp
#include <MemoryPool.h>
Struct MyStruct {
unsigned int id;
std::size_t size;
unsigned char data[256];
};
// pool will be able to hold 10 blocks the size of MyStruct
MemoryPool::Fixed<10, sizeof(MyStruct)> pool;
// there is no size argument in the malloc function!
MyStruct* s = reinterpret_cast<MyStruct*>(pool.malloc());
// you can allocate less than the specified blocksize
int* i = reinterpret_cast<int*>(pool.malloc());
pool.free(s);
pool.free(i);
```
#### How it works
##### Variable size pool
Free blocks are organized as a linked list with their header (contains pointer to next and size). An allocated block also has this header with it's pointer set to `nullptr`. Therefore, each allocation wastes memory the size of the header (`sizeof(void*) + sizeof(std::size_t)`). On creation, the pool calculations the needed space to store the number of blocks wich each their header.
However, memory allocation isn't restricted the the specified blocksize. So in reality, you can allocate more if you allocate larger chunks because less memory blocks means less headers. After all, memory needs to be contiguous.
If you inspect the pool you'll see that a free pool only has one big block.
Allocation is linear: the pool is iterated until a suitable spot is found.
Freeing is also linear as the pool is traversed to insert the chunk in the linked list of free blocks
When freeing, free blocks which are adjacent are combined into one.
##### Fixed size pool
The fixed size pool is implemented as an array. Free blocks are saved as a linked list in this array.
### Bugs and feature requests
Please use Github's facilities to get in touch.
### License
This library is released under the MIT Licence. A copy is included in the repo.

View File

@@ -1,16 +1,16 @@
# Datatypes (KEYWORD1)
Fixed KEYWORD1
Variable KEYWORD1
# Methods and Functions (KEYWORD2)
malloc KEYWORD2
free KEYWORD2
freeMemory KEYWORD2
maxBlockSize KEYWORD2
print KEYWORD2
# Structures (KEYWORD3)
# structure KEYWORD3
# Constants (LITERAL1)
MemoryPool LITERAL1
# Datatypes (KEYWORD1)
Fixed KEYWORD1
Variable KEYWORD1
# Methods and Functions (KEYWORD2)
malloc KEYWORD2
free KEYWORD2
freeMemory KEYWORD2
maxBlockSize KEYWORD2
print KEYWORD2
# Structures (KEYWORD3)
# structure KEYWORD3
# Constants (LITERAL1)
MemoryPool LITERAL1

View File

@@ -1,21 +1,21 @@
{
"name": "MemoryPool",
"keywords": "memory",
"description": "A simple memory pool for fixed and variable sizes",
"authors":
{
"name": "Bert Melis",
"url": "https://github.com/bertmelis"
},
"license": "MIT",
"homepage": "https://github.com/bertmelis/MemoryPool",
"repository":
{
"type": "git",
"url": "https://github.com/bertmelis/MemoryPool.git"
},
"version": "0.1.0",
"frameworks": "*",
"platforms": "*",
"headers": ["MemoryPool.h"]
{
"name": "MemoryPool",
"keywords": "memory",
"description": "A simple memory pool for fixed and variable sizes",
"authors":
{
"name": "Bert Melis",
"url": "https://github.com/bertmelis"
},
"license": "MIT",
"homepage": "https://github.com/bertmelis/MemoryPool",
"repository":
{
"type": "git",
"url": "https://github.com/bertmelis/MemoryPool.git"
},
"version": "0.1.0",
"frameworks": "*",
"platforms": "*",
"headers": ["MemoryPool.h"]
}

View File

@@ -1,10 +1,10 @@
name=MemoryPool
version=0.1.0
author=Bert Melis
maintainer=Bert Melis
sentence=A simple memory pool for fixed and variable sizes
paragraph=
category=Other
url=https://github.com/bertmelis/MemoryPool
architectures=*
name=MemoryPool
version=0.1.0
author=Bert Melis
maintainer=Bert Melis
sentence=A simple memory pool for fixed and variable sizes
paragraph=
category=Other
url=https://github.com/bertmelis/MemoryPool
architectures=*
includes=MemoryPool.h

View File

@@ -1,119 +1,119 @@
/*
Copyright (c) 2024 Bert Melis. All rights reserved.
This work is licensed under the terms of the MIT license.
For a copy, see <https://opensource.org/licenses/MIT> or
the LICENSE file.
*/
#pragma once
#include <cstddef> // std::size_t
#include <cassert> // assert
#if _GLIBCXX_HAS_GTHREADS
#include <mutex> // NOLINT [build/c++11] std::mutex, std::lock_guard
#else
#warning "The memory pool is not thread safe"
#endif
#ifdef MEMPOL_DEBUG
#include <iostream>
#endif
namespace MemoryPool {
template <std::size_t nrBlocks, std::size_t blocksize>
class Fixed {
public:
Fixed() // cppcheck-suppress uninitMemberVar
: _buffer{0}
, _head(_buffer) {
unsigned char* b = _head;
std::size_t adjustedBlocksize = sizeof(std::size_t) > blocksize ? sizeof(std::size_t) : blocksize;
for (std::size_t i = 0; i < nrBlocks - 1; ++i) {
*reinterpret_cast<unsigned char**>(b) = b + adjustedBlocksize;
b += adjustedBlocksize;
}
*reinterpret_cast<unsigned char**>(b) = nullptr;
}
// no copy nor move
Fixed (const Fixed&) = delete;
Fixed& operator= (const Fixed&) = delete;
void* malloc() {
#if _GLIBCXX_HAS_GTHREADS
const std::lock_guard<std::mutex> lockGuard(_mutex);
#endif
if (_head) {
void* retVal = _head;
_head = *reinterpret_cast<unsigned char**>(_head);
return retVal;
}
return nullptr;
}
void free(void* ptr) {
if (!ptr) return;
#if _GLIBCXX_HAS_GTHREADS
const std::lock_guard<std::mutex> lockGuard(_mutex);
#endif
*reinterpret_cast<unsigned char**>(ptr) = _head;
_head = reinterpret_cast<unsigned char*>(ptr);
}
std::size_t freeMemory() {
#if _GLIBCXX_HAS_GTHREADS
const std::lock_guard<std::mutex> lockGuard(_mutex);
#endif
unsigned char* i = _head;
std::size_t retVal = 0;
while (i) {
retVal += blocksize;
i = reinterpret_cast<unsigned char**>(i)[0];
}
return retVal;
}
#ifdef MEMPOL_DEBUG
void print() {
std::size_t adjustedBlocksize = sizeof(std::size_t) > blocksize ? sizeof(std::size_t) : blocksize;
std::cout << "+--------------------" << std::endl;
std::cout << "|start:" << reinterpret_cast<void*>(_buffer) << std::endl;
std::cout << "|blocks:" << nrBlocks << std::endl;
std::cout << "|blocksize:" << adjustedBlocksize << std::endl;
std::cout << "|head: " << reinterpret_cast<void*>(_head) << std::endl;
unsigned char* currentBlock = _buffer;
for (std::size_t i = 0; i < nrBlocks; ++i) {
std::cout << "|" << i + 1 << ": " << reinterpret_cast<void*>(currentBlock) << std::endl;
if (_isFree(currentBlock)) {
std::cout << "| free" << std::endl;
std::cout << "| next: " << reinterpret_cast<void*>(*reinterpret_cast<unsigned char**>(currentBlock)) << std::endl;
} else {
std::cout << "| allocated" << std::endl;
}
currentBlock += adjustedBlocksize;
}
std::cout << "+--------------------" << std::endl;
}
bool _isFree(const unsigned char* ptr) {
unsigned char* b = _head;
while (b) {
if (b == ptr) return true;
b = *reinterpret_cast<unsigned char**>(b);
}
return false;
}
#endif
private:
unsigned char _buffer[nrBlocks * (sizeof(std::size_t) > blocksize ? sizeof(std::size_t) : blocksize)];
unsigned char* _head;
#if _GLIBCXX_HAS_GTHREADS
std::mutex _mutex;
#endif
};
} // end namespace MemoryPool
/*
Copyright (c) 2024 Bert Melis. All rights reserved.
This work is licensed under the terms of the MIT license.
For a copy, see <https://opensource.org/licenses/MIT> or
the LICENSE file.
*/
#pragma once
#include <cstddef> // std::size_t
#include <cassert> // assert
#if _GLIBCXX_HAS_GTHREADS
#include <mutex> // NOLINT [build/c++11] std::mutex, std::lock_guard
#else
#warning "The memory pool is not thread safe"
#endif
#ifdef MEMPOL_DEBUG
#include <iostream>
#endif
namespace MemoryPool {
template <std::size_t nrBlocks, std::size_t blocksize>
class Fixed {
public:
Fixed() // cppcheck-suppress uninitMemberVar
: _buffer{0}
, _head(_buffer) {
unsigned char* b = _head;
std::size_t adjustedBlocksize = sizeof(std::size_t) > blocksize ? sizeof(std::size_t) : blocksize;
for (std::size_t i = 0; i < nrBlocks - 1; ++i) {
*reinterpret_cast<unsigned char**>(b) = b + adjustedBlocksize;
b += adjustedBlocksize;
}
*reinterpret_cast<unsigned char**>(b) = nullptr;
}
// no copy nor move
Fixed (const Fixed&) = delete;
Fixed& operator= (const Fixed&) = delete;
void* malloc() {
#if _GLIBCXX_HAS_GTHREADS
const std::lock_guard<std::mutex> lockGuard(_mutex);
#endif
if (_head) {
void* retVal = _head;
_head = *reinterpret_cast<unsigned char**>(_head);
return retVal;
}
return nullptr;
}
void free(void* ptr) {
if (!ptr) return;
#if _GLIBCXX_HAS_GTHREADS
const std::lock_guard<std::mutex> lockGuard(_mutex);
#endif
*reinterpret_cast<unsigned char**>(ptr) = _head;
_head = reinterpret_cast<unsigned char*>(ptr);
}
std::size_t freeMemory() {
#if _GLIBCXX_HAS_GTHREADS
const std::lock_guard<std::mutex> lockGuard(_mutex);
#endif
unsigned char* i = _head;
std::size_t retVal = 0;
while (i) {
retVal += blocksize;
i = reinterpret_cast<unsigned char**>(i)[0];
}
return retVal;
}
#ifdef MEMPOL_DEBUG
void print() {
std::size_t adjustedBlocksize = sizeof(std::size_t) > blocksize ? sizeof(std::size_t) : blocksize;
std::cout << "+--------------------" << std::endl;
std::cout << "|start:" << reinterpret_cast<void*>(_buffer) << std::endl;
std::cout << "|blocks:" << nrBlocks << std::endl;
std::cout << "|blocksize:" << adjustedBlocksize << std::endl;
std::cout << "|head: " << reinterpret_cast<void*>(_head) << std::endl;
unsigned char* currentBlock = _buffer;
for (std::size_t i = 0; i < nrBlocks; ++i) {
std::cout << "|" << i + 1 << ": " << reinterpret_cast<void*>(currentBlock) << std::endl;
if (_isFree(currentBlock)) {
std::cout << "| free" << std::endl;
std::cout << "| next: " << reinterpret_cast<void*>(*reinterpret_cast<unsigned char**>(currentBlock)) << std::endl;
} else {
std::cout << "| allocated" << std::endl;
}
currentBlock += adjustedBlocksize;
}
std::cout << "+--------------------" << std::endl;
}
bool _isFree(const unsigned char* ptr) {
unsigned char* b = _head;
while (b) {
if (b == ptr) return true;
b = *reinterpret_cast<unsigned char**>(b);
}
return false;
}
#endif
private:
unsigned char _buffer[nrBlocks * (sizeof(std::size_t) > blocksize ? sizeof(std::size_t) : blocksize)];
unsigned char* _head;
#if _GLIBCXX_HAS_GTHREADS
std::mutex _mutex;
#endif
};
} // end namespace MemoryPool

View File

@@ -1,12 +1,12 @@
/*
Copyright (c) 2024 Bert Melis. All rights reserved.
This work is licensed under the terms of the MIT license.
For a copy, see <https://opensource.org/licenses/MIT> or
the LICENSE file.
*/
#pragma once
#include "Variable.h"
#include "Fixed.h"
/*
Copyright (c) 2024 Bert Melis. All rights reserved.
This work is licensed under the terms of the MIT license.
For a copy, see <https://opensource.org/licenses/MIT> or
the LICENSE file.
*/
#pragma once
#include "Variable.h"
#include "Fixed.h"

View File

@@ -1,242 +1,242 @@
/*
Copyright (c) 2024 Bert Melis. All rights reserved.
This work is licensed under the terms of the MIT license.
For a copy, see <https://opensource.org/licenses/MIT> or
the LICENSE file.
*/
#pragma once
#include <cstddef> // std::size_t
#include <cassert> // assert
#if _GLIBCXX_HAS_GTHREADS
#include <mutex> // NOLINT [build/c++11] std::mutex, std::lock_guard
#else
#warning "The memory pool is not thread safe"
#endif
#ifdef MEMPOL_DEBUG
#include <iostream>
#endif
namespace MemoryPool {
template <std::size_t nrBlocks, std::size_t blocksize>
class Variable {
public:
Variable()
: _buffer{0}
, _head(nullptr)
#ifdef MEMPOL_DEBUG
, _bufferSize(0)
#endif
{
std::size_t _normBlocksize = blocksize / sizeof(BlockHeader) + ((blocksize % sizeof(BlockHeader)) ? 1 : 0);
size_t nrBlocksToAlloc = nrBlocks * (_normBlocksize + 1);
BlockHeader* h = reinterpret_cast<BlockHeader*>(_buffer);
h->next = nullptr;
h->size = nrBlocksToAlloc;
_head = h;
#ifdef MEMPOL_DEBUG
_bufferSize = nrBlocksToAlloc;
#endif
}
// no copy nor move
Variable (const Variable&) = delete;
Variable& operator= (const Variable&) = delete;
void* malloc(size_t size) {
#if _GLIBCXX_HAS_GTHREADS
const std::lock_guard<std::mutex> lockGuard(_mutex);
#endif
if (size == 0) return nullptr;
size = (size / sizeof(BlockHeader) + (size % sizeof(BlockHeader) != 0)) + 1; // count by BlockHeader size, add 1 for header
#ifdef MEMPOL_DEBUG
std::cout << "malloc (raw) " << size << std::endl;
std::cout << "malloc (adj) " << size << " - ";
#endif
BlockHeader* currentBlock = _head;
BlockHeader* previousBlock = nullptr;
void* retVal = nullptr;
// iterate through linked free blocks
while (currentBlock) {
// consume whole block is size equals required size
if (currentBlock->size == size) {
if (previousBlock) previousBlock->next = currentBlock->next;
break;
// split block if size is larger and add second part to list of free blocks
} else if (currentBlock->size > size) {
BlockHeader* newBlock = currentBlock + size;
if (previousBlock) previousBlock->next = newBlock;
newBlock->next = currentBlock->next;
newBlock->size = currentBlock->size - size;
currentBlock->next = newBlock;
break;
}
previousBlock = currentBlock;
currentBlock = currentBlock->next;
}
if (currentBlock) {
if (currentBlock == _head) {
_head = currentBlock->next;
}
currentBlock->size = size;
currentBlock->next = nullptr; // used when freeing memory
retVal = currentBlock + 1;
#ifdef MEMPOL_DEBUG
std::cout << "ok" << std::endl;
#endif
} else {
#ifdef MEMPOL_DEBUG
std::cout << "nok" << std::endl;
#endif
(void)0;
}
return retVal;
}
void free(void* ptr) {
if (!ptr) return;
// check if ptr points to region in _buffer
#ifdef MEMPOL_DEBUG
std::cout << "free " << static_cast<void*>(reinterpret_cast<BlockHeader*>(ptr) - 1) << std::endl;
#endif
#if _GLIBCXX_HAS_GTHREADS
const std::lock_guard<std::mutex> lockGuard(_mutex);
#endif
BlockHeader* toFree = reinterpret_cast<BlockHeader*>(ptr) - 1;
BlockHeader* previous = reinterpret_cast<BlockHeader*>(_buffer);
BlockHeader* next = _head;
// toFree is the only free block
if (!next) {
_head = toFree;
return;
}
while (previous) {
if (!next || toFree < next) {
// 1. add block to linked list of free blocks
if (toFree < _head) {
toFree->next = _head;
_head = toFree;
} else {
previous->next = toFree;
toFree->next = next;
}
// 2. merge with previous if adjacent
if (toFree > _head && toFree == previous + previous->size) {
previous->size += toFree->size;
previous->next = toFree->next;
toFree = previous; // used in next check
}
// 3. merge with next if adjacent
if (toFree + toFree->size == next) {
toFree->size += next->size;
toFree->next = next->next;
}
// 4. done
return;
}
previous = next;
next = next->next;
}
}
std::size_t freeMemory() {
#if _GLIBCXX_HAS_GTHREADS
const std::lock_guard<std::mutex> lockGuard(_mutex);
#endif
size_t retVal = 0;
BlockHeader* currentBlock = reinterpret_cast<BlockHeader*>(_head);
while (currentBlock) {
retVal += currentBlock->size - 1;
currentBlock = currentBlock->next;
}
return retVal * sizeof(BlockHeader);
}
std::size_t maxBlockSize() {
#if _GLIBCXX_HAS_GTHREADS
const std::lock_guard<std::mutex> lockGuard(_mutex);
#endif
size_t retVal = 0;
BlockHeader* currentBlock = reinterpret_cast<BlockHeader*>(_head);
while (currentBlock) {
retVal = (currentBlock->size - 1 > retVal) ? currentBlock->size - 1 : retVal;
currentBlock = currentBlock->next;
}
return retVal * sizeof(BlockHeader);
}
#ifdef MEMPOL_DEBUG
void print() {
std::cout << "+--------------------" << std::endl;
std::cout << "|start:" << static_cast<void*>(_buffer) << std::endl;
std::cout << "|size:" << _bufferSize << std::endl;
std::cout << "|headersize:" << sizeof(BlockHeader) << std::endl;
std::cout << "|head: " << static_cast<void*>(_head) << std::endl;
BlockHeader* nextFreeBlock = _head;
BlockHeader* currentBlock = reinterpret_cast<BlockHeader*>(_buffer);
size_t blockNumber = 1;
while (currentBlock < reinterpret_cast<BlockHeader*>(_buffer) + _bufferSize) {
std::cout << "|" << blockNumber << ": " << static_cast<void*>(currentBlock) << std::endl;
std::cout << "| " << static_cast<void*>(currentBlock->next) << std::endl;
std::cout << "| " << currentBlock->size << std::endl;
if (currentBlock == nextFreeBlock) {
std::cout << "| free" << std::endl;
nextFreeBlock = nextFreeBlock->next;
} else {
std::cout << "| allocated" << std::endl;
}
++blockNumber;
currentBlock += currentBlock->size;
}
std::cout << "+--------------------" << std::endl;
}
#endif
private:
struct BlockHeader {
BlockHeader* next;
std::size_t size;
};
/*
pool size is aligned to sizeof(BlockHeader).
requested blocksize is therefore multiple of blockheader (rounded up)
total size = nr requested blocks * multiplier * blockheadersize
see constructor for calculation
*/
unsigned char _buffer[(nrBlocks * ((blocksize / sizeof(BlockHeader) + ((blocksize % sizeof(BlockHeader)) ? 1 : 0)) + 1)) * sizeof(BlockHeader)];
BlockHeader* _head;
#if _GLIBCXX_HAS_GTHREADS
std::mutex _mutex;
#endif
#ifdef MEMPOL_DEBUG
std::size_t _bufferSize;
#endif
};
} // end namespace MemoryPool
/*
Copyright (c) 2024 Bert Melis. All rights reserved.
This work is licensed under the terms of the MIT license.
For a copy, see <https://opensource.org/licenses/MIT> or
the LICENSE file.
*/
#pragma once
#include <cstddef> // std::size_t
#include <cassert> // assert
#if _GLIBCXX_HAS_GTHREADS
#include <mutex> // NOLINT [build/c++11] std::mutex, std::lock_guard
#else
#warning "The memory pool is not thread safe"
#endif
#ifdef MEMPOL_DEBUG
#include <iostream>
#endif
namespace MemoryPool {
template <std::size_t nrBlocks, std::size_t blocksize>
class Variable {
public:
Variable()
: _buffer{0}
, _head(nullptr)
#ifdef MEMPOL_DEBUG
, _bufferSize(0)
#endif
{
std::size_t _normBlocksize = blocksize / sizeof(BlockHeader) + ((blocksize % sizeof(BlockHeader)) ? 1 : 0);
size_t nrBlocksToAlloc = nrBlocks * (_normBlocksize + 1);
BlockHeader* h = reinterpret_cast<BlockHeader*>(_buffer);
h->next = nullptr;
h->size = nrBlocksToAlloc;
_head = h;
#ifdef MEMPOL_DEBUG
_bufferSize = nrBlocksToAlloc;
#endif
}
// no copy nor move
Variable (const Variable&) = delete;
Variable& operator= (const Variable&) = delete;
void* malloc(size_t size) {
#if _GLIBCXX_HAS_GTHREADS
const std::lock_guard<std::mutex> lockGuard(_mutex);
#endif
if (size == 0) return nullptr;
size = (size / sizeof(BlockHeader) + (size % sizeof(BlockHeader) != 0)) + 1; // count by BlockHeader size, add 1 for header
#ifdef MEMPOL_DEBUG
std::cout << "malloc (raw) " << size << std::endl;
std::cout << "malloc (adj) " << size << " - ";
#endif
BlockHeader* currentBlock = _head;
BlockHeader* previousBlock = nullptr;
void* retVal = nullptr;
// iterate through linked free blocks
while (currentBlock) {
// consume whole block is size equals required size
if (currentBlock->size == size) {
if (previousBlock) previousBlock->next = currentBlock->next;
break;
// split block if size is larger and add second part to list of free blocks
} else if (currentBlock->size > size) {
BlockHeader* newBlock = currentBlock + size;
if (previousBlock) previousBlock->next = newBlock;
newBlock->next = currentBlock->next;
newBlock->size = currentBlock->size - size;
currentBlock->next = newBlock;
break;
}
previousBlock = currentBlock;
currentBlock = currentBlock->next;
}
if (currentBlock) {
if (currentBlock == _head) {
_head = currentBlock->next;
}
currentBlock->size = size;
currentBlock->next = nullptr; // used when freeing memory
retVal = currentBlock + 1;
#ifdef MEMPOL_DEBUG
std::cout << "ok" << std::endl;
#endif
} else {
#ifdef MEMPOL_DEBUG
std::cout << "nok" << std::endl;
#endif
(void)0;
}
return retVal;
}
void free(void* ptr) {
if (!ptr) return;
// check if ptr points to region in _buffer
#ifdef MEMPOL_DEBUG
std::cout << "free " << static_cast<void*>(reinterpret_cast<BlockHeader*>(ptr) - 1) << std::endl;
#endif
#if _GLIBCXX_HAS_GTHREADS
const std::lock_guard<std::mutex> lockGuard(_mutex);
#endif
BlockHeader* toFree = reinterpret_cast<BlockHeader*>(ptr) - 1;
BlockHeader* previous = reinterpret_cast<BlockHeader*>(_buffer);
BlockHeader* next = _head;
// toFree is the only free block
if (!next) {
_head = toFree;
return;
}
while (previous) {
if (!next || toFree < next) {
// 1. add block to linked list of free blocks
if (toFree < _head) {
toFree->next = _head;
_head = toFree;
} else {
previous->next = toFree;
toFree->next = next;
}
// 2. merge with previous if adjacent
if (toFree > _head && toFree == previous + previous->size) {
previous->size += toFree->size;
previous->next = toFree->next;
toFree = previous; // used in next check
}
// 3. merge with next if adjacent
if (toFree + toFree->size == next) {
toFree->size += next->size;
toFree->next = next->next;
}
// 4. done
return;
}
previous = next;
next = next->next;
}
}
std::size_t freeMemory() {
#if _GLIBCXX_HAS_GTHREADS
const std::lock_guard<std::mutex> lockGuard(_mutex);
#endif
size_t retVal = 0;
BlockHeader* currentBlock = reinterpret_cast<BlockHeader*>(_head);
while (currentBlock) {
retVal += currentBlock->size - 1;
currentBlock = currentBlock->next;
}
return retVal * sizeof(BlockHeader);
}
std::size_t maxBlockSize() {
#if _GLIBCXX_HAS_GTHREADS
const std::lock_guard<std::mutex> lockGuard(_mutex);
#endif
size_t retVal = 0;
BlockHeader* currentBlock = reinterpret_cast<BlockHeader*>(_head);
while (currentBlock) {
retVal = (currentBlock->size - 1 > retVal) ? currentBlock->size - 1 : retVal;
currentBlock = currentBlock->next;
}
return retVal * sizeof(BlockHeader);
}
#ifdef MEMPOL_DEBUG
void print() {
std::cout << "+--------------------" << std::endl;
std::cout << "|start:" << static_cast<void*>(_buffer) << std::endl;
std::cout << "|size:" << _bufferSize << std::endl;
std::cout << "|headersize:" << sizeof(BlockHeader) << std::endl;
std::cout << "|head: " << static_cast<void*>(_head) << std::endl;
BlockHeader* nextFreeBlock = _head;
BlockHeader* currentBlock = reinterpret_cast<BlockHeader*>(_buffer);
size_t blockNumber = 1;
while (currentBlock < reinterpret_cast<BlockHeader*>(_buffer) + _bufferSize) {
std::cout << "|" << blockNumber << ": " << static_cast<void*>(currentBlock) << std::endl;
std::cout << "| " << static_cast<void*>(currentBlock->next) << std::endl;
std::cout << "| " << currentBlock->size << std::endl;
if (currentBlock == nextFreeBlock) {
std::cout << "| free" << std::endl;
nextFreeBlock = nextFreeBlock->next;
} else {
std::cout << "| allocated" << std::endl;
}
++blockNumber;
currentBlock += currentBlock->size;
}
std::cout << "+--------------------" << std::endl;
}
#endif
private:
struct BlockHeader {
BlockHeader* next;
std::size_t size;
};
/*
pool size is aligned to sizeof(BlockHeader).
requested blocksize is therefore multiple of blockheader (rounded up)
total size = nr requested blocks * multiplier * blockheadersize
see constructor for calculation
*/
unsigned char _buffer[(nrBlocks * ((blocksize / sizeof(BlockHeader) + ((blocksize % sizeof(BlockHeader)) ? 1 : 0)) + 1)) * sizeof(BlockHeader)];
BlockHeader* _head;
#if _GLIBCXX_HAS_GTHREADS
std::mutex _mutex;
#endif
#ifdef MEMPOL_DEBUG
std::size_t _bufferSize;
#endif
};
} // end namespace MemoryPool

View File

@@ -11,13 +11,13 @@ the LICENSE file.
#pragma once
#include "MqttClient.h"
#if EMC_MULTIPLE_CALLBACKS
#include <list>
#include <utility>
#endif
#include "MqttClient.h"
template <typename T>
class MqttClientSetup : public MqttClient {
public:

View File

@@ -27,12 +27,12 @@ bool ClientPosix::connect(IPAddress ip, uint16_t port) {
_sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
if (_sockfd < 0) {
emc_log_e("Error %d opening socket", errno);
emc_log_e("Error %d: \"%s\" opening socket", errno, strerror(errno));
}
int flag = 1;
if (setsockopt(_sockfd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(int)) < 0) {
emc_log_e("Error %d disabling nagle", errno);
emc_log_e("Error %d: \"%s\" disabling nagle", errno, strerror(errno));
}
memset(&_host, 0, sizeof(_host));
@@ -47,15 +47,17 @@ bool ClientPosix::connect(IPAddress ip, uint16_t port) {
return false;
}
emc_log_i("Connected");
emc_log_i("Socket connected");
return true;
}
bool ClientPosix::connect(const char* host, uint16_t port) {
// tbi
(void) host;
(void) port;
return false;
bool ClientPosix::connect(const char* hostname, uint16_t port) {
IPAddress ipAddress = _hostToIP(hostname);
if (ipAddress == IPAddress(0)) {
emc_log_e("No such host '%s'", hostname);
return false;
}
return connect(ipAddress, port);
}
size_t ClientPosix::write(const uint8_t* buf, size_t size) {
@@ -87,6 +89,42 @@ bool ClientPosix::disconnected() {
return _sockfd < 0;
}
IPAddress ClientPosix::_hostToIP(const char* hostname) {
IPAddress returnIP(0);
struct addrinfo hints, *servinfo, *p;
struct sockaddr_in *h;
int rv;
// Set up request addrinfo struct
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
emc_log_i("Looking for '%s'", hostname);
// ask for host data
if ((rv = getaddrinfo(hostname, NULL, &hints, &servinfo)) != 0) {
emc_log_e("getaddrinfo: %s", gai_strerror(rv));
return returnIP;
}
// loop through all the results and connect to the first we can
for (p = servinfo; p != NULL; p = p->ai_next) {
h = (struct sockaddr_in *)p->ai_addr;
returnIP = ::htonl(h->sin_addr.s_addr);
if (returnIP != IPAddress(0)) break;
}
// Release allocated memory
freeaddrinfo(servinfo);
if (returnIP != IPAddress(0)) {
emc_log_i("Host '%s' = %u", hostname, (uint32_t)returnIP);
} else {
emc_log_e("No IP for '%s' found", hostname);
}
return returnIP;
}
} // namespace espMqttClientInternals
#endif

View File

@@ -19,6 +19,7 @@ the LICENSE file.
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <arpa/inet.h>
#include "Transport.h" // includes IPAddress
#include "../Logging.h"
@@ -34,7 +35,7 @@ class ClientPosix : public Transport {
ClientPosix();
~ClientPosix();
bool connect(IPAddress ip, uint16_t port) override;
bool connect(const char* host, uint16_t port) override;
bool connect(const char* hostname, uint16_t port) override;
size_t write(const uint8_t* buf, size_t size) override;
int read(uint8_t* buf, size_t size) override;
void stop() override;
@@ -44,6 +45,8 @@ class ClientPosix : public Transport {
protected:
int _sockfd;
sockaddr_in _host;
IPAddress _hostToIP(const char* hostname);
};
} // namespace espMqttClientInternals

View File

@@ -29,4 +29,12 @@ IPAddress::operator uint32_t() {
return _address;
}
bool IPAddress::operator==(IPAddress other) {
return _address == other._address;
}
bool IPAddress::operator!=(IPAddress other) {
return _address != other._address;
}
#endif

View File

@@ -18,8 +18,10 @@ class IPAddress {
public:
IPAddress();
IPAddress(uint8_t p0, uint8_t p1, uint8_t p2, uint8_t p3);
explicit IPAddress(uint32_t address);
IPAddress(uint32_t address); // NOLINT(runtime/explicit)
operator uint32_t();
bool operator==(IPAddress other);
bool operator!=(IPAddress other);
protected:
uint32_t _address;

View File

@@ -16,8 +16,9 @@ uint32_t onPublishCbId = 6;
std::atomic_bool exitProgram(false);
std::thread t;
const IPAddress broker(127,0,0,1);
//const char* broker = "localhost";
//const IPAddress broker(127,0,0,1);
const char* broker = "mqtt";
//const char* broker = "test.mosquitto.org";
const uint16_t broker_port = 1883;
/*