-
-
Notifications
You must be signed in to change notification settings - Fork 570
/
dynamic_binary_buffer.hpp
182 lines (146 loc) · 5.76 KB
/
dynamic_binary_buffer.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#pragma once
#include <iostream>
class dynamic_binary_buffer_t {
public:
dynamic_binary_buffer_t() : byte_storage(nullptr), maximum_internal_storage_size(0) {
// std::cout << "Default constructor called" << std::endl;
}
// Explicitly removed it as we need to implement it properly when needed
dynamic_binary_buffer_t(dynamic_binary_buffer_t&& that) = delete;
// We should set maximum buffer size here.
// TODO: add ability to relocate memory of we need more memory
bool set_maximum_buffer_size_in_bytes(ssize_t size) {
// Already allocated
if (byte_storage) {
return false;
}
// With nothrow we are using new without exceptions
byte_storage = new (std::nothrow) uint8_t[size];
if (byte_storage) {
maximum_internal_storage_size = size;
return true;
} else {
return false;
}
}
~dynamic_binary_buffer_t() {
// std::cout << "Destructor called" << std::endl;
if (byte_storage) {
delete[] byte_storage;
byte_storage = nullptr;
maximum_internal_storage_size = 0;
}
}
// So this implementation will be useful only for real object copies
// For returning local variable from function compiler will do this job
// perfectly:
// https://en.wikipedia.org/wiki/Return_value_optimization
dynamic_binary_buffer_t(const dynamic_binary_buffer_t& that) {
this->maximum_internal_storage_size = that.maximum_internal_storage_size;
// Copy internal pointer too! It's very important!
this->internal_data_shift = that.internal_data_shift;
// std::cout << "Copy constructor called" << std::endl;
// std::cout << "Copy constructor will copy " << this->internal_size << "
// bytes" <<
// std::endl;
// We are copying all memory (unused too)
if (this->maximum_internal_storage_size > 0) {
// Allocate memory for new instance
this->set_maximum_buffer_size_in_bytes(this->maximum_internal_storage_size);
memcpy(this->byte_storage, that.byte_storage, that.maximum_internal_storage_size);
}
}
// All this functions just append some data with certain length to buffer and
// increase total
// size
// They are very similar to std::stringstream but for binary data only
bool append_byte(uint8_t byte_value) {
// Do bounds check
if (internal_data_shift > maximum_internal_storage_size - 1) {
errors_occured = true;
return false;
}
byte_storage[internal_data_shift] = byte_value;
internal_data_shift += sizeof(uint8_t);
return true;
}
// Use reference as argument
bool append_dynamic_buffer(dynamic_binary_buffer_t& dynamic_binary_buffer) {
// In this case we are copying only used memory
// TODO: Why +1?
if (internal_data_shift + dynamic_binary_buffer.get_used_size() > maximum_internal_storage_size + 1) {
errors_occured = true;
return false;
}
return this->append_data_as_pointer(dynamic_binary_buffer.get_pointer(), dynamic_binary_buffer.get_used_size());
}
bool append_data_as_pointer(const void* ptr, size_t length) {
if (internal_data_shift + length > maximum_internal_storage_size + 1) {
errors_occured = true;
return false;
}
memcpy(byte_storage + internal_data_shift, ptr, length);
internal_data_shift += length;
return true;
}
template <typename src_type> bool append_data_as_object_ptr(src_type* ptr) {
if (internal_data_shift + sizeof(src_type) > maximum_internal_storage_size + 1) {
errors_occured = true;
return false;
}
memcpy(byte_storage + internal_data_shift, ptr, sizeof(src_type));
internal_data_shift += sizeof(src_type);
return true;
}
// All functions below DO NOT CHANGE internal buffer position! They are very
// low level and
// should be avoided!
// We could set arbitrary byte with this function
bool set_byte(uint32_t byte_number, uint8_t byte_value) {
// Do bounds check
if (byte_number > maximum_internal_storage_size - 1) {
errors_occured = true;
return false;
}
byte_storage[byte_number] = byte_value;
return true;
}
bool memcpy_from_ptr(uint32_t shift, const void* ptr, uint32_t length) {
if (shift + length > maximum_internal_storage_size + 1) {
errors_occured = true;
return false;
}
memcpy(byte_storage + shift, ptr, length);
return true;
}
// More user friendly version of previous function
template <typename src_type> bool memcpy_from_object_ptr(uint32_t shift, src_type* ptr) {
if (shift + sizeof(src_type) > maximum_internal_storage_size + 1) {
errors_occured = true;
return false;
}
memcpy(byte_storage + shift, ptr, sizeof(src_type));
return true;
}
// Return full size (with non initialized data region too)
uint32_t get_full_size() {
return maximum_internal_storage_size;
}
// Return only used memory region
size_t get_used_size() {
return internal_data_shift;
}
const uint8_t* get_pointer() {
return byte_storage;
}
// If we have any issues with it
bool is_failed() {
return errors_occured;
}
private:
size_t internal_data_shift = 0;
uint8_t* byte_storage = nullptr;
ssize_t maximum_internal_storage_size = 0;
// If any errors occurred in any time when we used this buffer
bool errors_occured = false;
};