Uniot Core
0.9.0
Loading...
Searching...
No Matches
Bytes.h
Go to the documentation of this file.
1/*
2 * This is a part of the Uniot project.
3 * Copyright (C) 2016-2023 Uniot <contact@uniot.io>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#pragma once
20
21#include <Common.h>
22#include <Logger.h>
23#include <WString.h>
24#include <stdio.h>
25
26#include <functional>
27#include <type_traits>
28
38class Bytes {
39 public:
45 using Filler = std::function<size_t(uint8_t *buf, size_t size)>;
46
52 Bytes() : Bytes(nullptr, 0) {}
53
62 Bytes(const uint8_t *data, size_t size) {
63 _init();
64 if (data) {
65 _copy(data, size);
66 } else if (size) {
67 _reserve(size);
68 }
69 }
70
78 Bytes(const char *str) : Bytes((uint8_t *)str, strlen(str) + 1) // + 1 null terminator
79 {}
80
89 template <typename T>
90 Bytes(T value) : Bytes(((uint8_t *)&value), sizeof(T)) {
91 static_assert(std::is_integral<T>::value, "only integral types are allowed");
92 }
93
101 Bytes(const Bytes &value) {
102 _init();
103 *this = value;
104 }
105
114 Bytes(Bytes &&value) noexcept {
115 mBuffer = value.mBuffer;
116 mSize = value.mSize;
117 value._init();
118 }
119
127 Bytes(const String &value) {
128 _init();
129 *this = value;
130 }
131
137 virtual ~Bytes() {
138 _invalidate();
139 }
140
149 Bytes &operator=(const Bytes &rhs) {
150 if (this != &rhs) {
151 if (rhs.mBuffer) {
152 _copy(rhs.mBuffer, rhs.mSize);
153 } else {
154 _invalidate();
155 }
156 }
157 return *this;
158 }
159
169 Bytes &operator=(Bytes &&rhs) noexcept {
170 if (this != &rhs) {
171 _invalidate();
172 mBuffer = rhs.mBuffer;
173 mSize = rhs.mSize;
174 rhs._init();
175 }
176 return *this;
177 }
178
187 Bytes &operator=(const String &rhs) {
188 if (rhs.length()) {
189 _copy((const uint8_t *)rhs.c_str(), rhs.length());
190 } else {
191 _invalidate();
192 }
193 return this->terminate();
194 }
195
196 // template <typename T>
197 // operator T() {
198 // return *((T *)mBuffer);
199 // }
200
211 static Bytes fromHexString(const String &hexStr) {
212 size_t len = hexStr.length();
213 if (len % 2 != 0) {
214 UNIOT_LOG_ERROR("invalid hex string length");
215 return Bytes();
216 }
217
218 Bytes bytes;
219 if (!bytes._reserve(len / 2)) {
220 return Bytes();
221 }
222 for (size_t i = 0; i < len; i += 2) {
223 char buf[3] = {hexStr.charAt(i), hexStr.charAt(i + 1), '\0'};
224 uint8_t b = strtol(buf, nullptr, 16);
225 bytes.mBuffer[i / 2] = b;
226 }
227 return bytes;
228 }
229
240 size_t fill(Filler filler) {
241 if (filler) {
242 return filler(mBuffer, mSize);
243 }
244 return 0;
245 }
246
256 Bytes &prune(size_t newSize) {
257 if (newSize < mSize) {
258 _reserve(newSize);
259 }
260 return *this;
261 }
262
270 const uint8_t *raw() const {
271 return mBuffer;
272 }
273
282 if (!mSize) {
283 _reserve(1);
284 return *this;
285 }
286
287 if (mBuffer[mSize - 1] != '\0') {
288 if (_reserve(mSize + 1)) {
289 mBuffer[mSize - 1] = '\0';
290 } else {
291 // An unterminated buffer would be read past its end by anything using c_str().
292 _invalidate();
293 }
294 }
295 return *this;
296 }
297
306 const char *c_str() const {
307 return mBuffer ? (const char *)mBuffer : "";
308 }
309
317 String toString() const {
318 return String(this->c_str());
319 }
320
328 String toHexString() const {
329 String result = "";
330 for (size_t i = 0; i < mSize; i++) {
331 char buf[3];
332 snprintf(buf, sizeof(buf), "%02X", mBuffer[i]);
333 result += buf;
334 }
335 return result;
336 }
337
343 size_t size() const {
344 return mSize;
345 }
346
352 void clean() {
353 _invalidate();
354 }
355
363 uint32_t checksum() const {
364 return CRC32(mBuffer, mSize);
365 }
366
367 private:
373 inline void _init(void) {
374 mBuffer = nullptr;
375 mSize = 0;
376 }
377
383 void _invalidate(void) {
384 if (mBuffer) {
385 free(mBuffer);
386 }
387 _init();
388 }
389
400 bool _reserve(size_t newSize) {
401 // realloc(ptr, 0) may free and return null, which would read as a failure below.
402 if (!newSize) {
403 _invalidate();
404 return true;
405 }
406
407 // On failure realloc leaves the original block intact, so the object is left exactly
408 // as it was: never a size the buffer does not have, and nothing leaked.
409 auto buffer = (uint8_t *)realloc(mBuffer, newSize);
410 if (!buffer) {
411 return false;
412 }
413
414 if (newSize > mSize) {
415 memset(buffer + mSize, 0, newSize - mSize);
416 }
417 mBuffer = buffer;
418 mSize = newSize;
419 return true;
420 }
421
431 Bytes &_copy(const uint8_t *data, size_t size) {
432 if (_reserve(size)) {
433 if (size) {
434 memcpy(mBuffer, data, size);
435 }
436 } else {
437 _invalidate();
438 }
439 return *this;
440 }
441
442 uint8_t *mBuffer;
443 size_t mSize;
444};
445
uint32_t checksum() const
Calculates a checksum of the byte array.
Definition Bytes.h:363
void clean()
Deallocates the internal buffer.
Definition Bytes.h:352
String toString() const
Converts the byte array to an Arduino String.
Definition Bytes.h:317
Bytes & terminate()
Ensures the byte array is null-terminated.
Definition Bytes.h:281
Bytes & operator=(const Bytes &rhs)
Assignment operator.
Definition Bytes.h:149
const uint8_t * raw() const
Gets a const pointer to the raw byte array.
Definition Bytes.h:270
Bytes(const String &value)
Constructor from Arduino String.
Definition Bytes.h:127
const char * c_str() const
Gets the byte array as a C string.
Definition Bytes.h:306
Bytes(const uint8_t *data, size_t size)
Constructor from raw byte array.
Definition Bytes.h:62
Bytes & prune(size_t newSize)
Reduces the size of the buffer.
Definition Bytes.h:256
Bytes & operator=(Bytes &&rhs) noexcept
Move assignment operator.
Definition Bytes.h:169
Bytes(const Bytes &value)
Copy constructor.
Definition Bytes.h:101
Bytes & operator=(const String &rhs)
Assignment from Arduino String.
Definition Bytes.h:187
size_t fill(Filler filler)
Fills the buffer using a provided filler function.
Definition Bytes.h:240
String toHexString() const
Converts the byte array to a hexadecimal string.
Definition Bytes.h:328
std::function< size_t(uint8_t *buf, size_t size)> Filler
Function type for filling byte arrays.
Definition Bytes.h:45
virtual ~Bytes()
Destructor.
Definition Bytes.h:137
static Bytes fromHexString(const String &hexStr)
Creates a Bytes object from a hexadecimal string.
Definition Bytes.h:211
Bytes(Bytes &&value) noexcept
Move constructor.
Definition Bytes.h:114
size_t size() const
Gets the size of the byte array.
Definition Bytes.h:343
Bytes()
Default constructor.
Definition Bytes.h:52
Bytes(T value)
Constructor from integral type.
Definition Bytes.h:90
Bytes(const char *str)
Constructor from C-string.
Definition Bytes.h:78
uint32_t CRC32(const void *data, size_t length, uint32_t crc=0)
Calculates CRC32-C (Castagnoli) checksum for data integrity verification.
Definition Common.h:225
#define UNIOT_LOG_ERROR(...)
Log an ERROR level message Used for critical errors that may prevent normal operation....
Definition Logger.h:273