Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Common/Data/Collections/CharQueue.h
3189 views
1
#pragma once
2
3
#include "Common/Log.h"
4
#include "Common/Data/Collections/Slice.h"
5
6
#include <cstdlib>
7
#include <cstring>
8
#include <string_view>
9
10
// Queue with a dynamic size, optimized for bulk inserts and retrievals - and optimized
11
// to be fast in debug builds, hence it's pretty much C internally.
12
class CharQueue {
13
public:
14
explicit CharQueue(size_t blockSize = 16384) : blockSize_(blockSize) {
15
head_ = new Block{};
16
tail_ = head_;
17
head_->data = (char *)malloc(blockSize_);
18
head_->size = (int)blockSize_;
19
head_->next = nullptr;
20
}
21
22
// Remove copy constructors.
23
CharQueue(const CharQueue &) = delete;
24
void operator=(const CharQueue &) = delete;
25
26
// But let's have a move constructor.
27
CharQueue(CharQueue &&src) noexcept {
28
// Steal the data from the other queue.
29
blockSize_ = src.blockSize_;
30
head_ = src.head_;
31
tail_ = src.tail_;
32
// Give the old queue a new block. Could probably also leave it in an invalid state and get rid of it.
33
src.head_ = new Block{};
34
src.tail_ = src.head_;
35
src.head_->data = (char *)malloc(src.blockSize_);
36
src.head_->size = (int)src.blockSize_;
37
}
38
39
~CharQueue() {
40
clear();
41
_dbg_assert_(head_ == tail_);
42
_dbg_assert_(head_->size == blockSize_);
43
_dbg_assert_(size() == 0);
44
// delete the final block
45
delete head_;
46
}
47
48
char *push_back_write(size_t size) {
49
int remain = tail_->size - tail_->tail;
50
_dbg_assert_(remain >= 0);
51
if (remain >= (int)size) {
52
char *retval = tail_->data + tail_->tail;
53
tail_->tail += (int)size;
54
return retval;
55
} else {
56
// Can't fit? Just allocate a new block and fill it up with the new data.
57
int bsize = (int)blockSize_;
58
if ((int)size > bsize) {
59
bsize = (int)size;
60
}
61
Block *b = new Block{};
62
b->head = 0;
63
b->tail = (int)size;
64
b->size = bsize;
65
b->data = (char *)malloc(bsize);
66
tail_->next = b;
67
tail_ = b;
68
return tail_->data;
69
}
70
}
71
72
void push_back(const char *data, size_t size) {
73
memcpy(push_back_write(size), data, size);
74
}
75
76
void push_back(std::string_view chars) {
77
memcpy(push_back_write(chars.size()), chars.data(), chars.size());
78
}
79
80
// For debugging, mainly.
81
size_t block_count() const {
82
int count = 0;
83
Block *b = head_;
84
do {
85
count++;
86
b = b->next;
87
} while (b);
88
return count;
89
}
90
91
size_t size() const {
92
size_t s = 0;
93
Block *b = head_;
94
do {
95
s += b->tail - b->head;
96
b = b->next;
97
} while (b);
98
return s;
99
}
100
101
char peek(size_t peekOff) {
102
Block *b = head_;
103
do {
104
int remain = b->tail - b->head;
105
if (remain > (int)peekOff) {
106
return b->data[b->head + peekOff];
107
} else {
108
peekOff -= remain;
109
}
110
b = b->next;
111
} while (b);
112
// Ran out of data.
113
_dbg_assert_(false);
114
return 0;
115
}
116
117
bool empty() const {
118
return size() == 0;
119
}
120
121
// Pass in a lambda that takes each partial buffer as char*, size_t.
122
template<typename Func>
123
bool iterate_blocks(Func callback) const {
124
Block *b = head_;
125
do {
126
if (b->tail > b->head) {
127
if (!callback(b->data + b->head, b->tail - b->head)) {
128
return false;
129
}
130
}
131
b = b->next;
132
} while (b);
133
return true;
134
}
135
136
size_t pop_front_bulk(char *dest, size_t size) {
137
int popSize = (int)size;
138
int writeOff = 0;
139
while (popSize > 0) {
140
int remain = head_->tail - head_->head;
141
int readSize = popSize;
142
if (readSize > remain) {
143
readSize = remain;
144
}
145
if (dest) {
146
memcpy(dest + writeOff, head_->data + head_->head, readSize);
147
}
148
writeOff += readSize;
149
head_->head += readSize;
150
popSize -= readSize;
151
if (head_->head == head_->tail) {
152
// Ran out of data in this block. Let's hope there's more...
153
if (head_ == tail_) {
154
// Can't read any more, bail.
155
break;
156
}
157
Block *next = head_->next;
158
delete head_;
159
head_ = next;
160
}
161
}
162
return (int)size - popSize;
163
}
164
165
size_t skip(size_t size) {
166
return pop_front_bulk(nullptr, size);
167
}
168
169
void clear() {
170
Block *b = head_;
171
// Delete all blocks except the last.
172
while (b != tail_) {
173
Block *next = b->next;
174
delete b;
175
b = next;
176
}
177
if (b->size != blockSize_) {
178
// Restore the remaining block to default size.
179
free(b->data);
180
b->data = (char *)malloc(blockSize_);
181
b->size = (int)blockSize_;
182
}
183
b->head = 0;
184
b->tail = 0;
185
// head and tail are now equal.
186
head_ = b;
187
}
188
189
// If return value is negative, one wasn't found.
190
int next_crlf_offset() {
191
int offset = 0;
192
Block *b = head_;
193
do {
194
int remain = b->tail - b->head;
195
for (int i = 0; i < remain; i++) {
196
if (b->data[b->head + i] == '\r') {
197
// Use peek to avoid handling edge cases.
198
if (peek(offset + i + 1) == '\n') {
199
return offset + i;
200
}
201
}
202
}
203
offset += remain;
204
b = b->next;
205
} while (b);
206
// Ran out of data.
207
return -1;
208
}
209
210
private:
211
struct Block {
212
~Block() {
213
if (data) {
214
free(data);
215
data = 0;
216
}
217
size = 0;
218
}
219
Block *next;
220
char *data;
221
int size; // Can be bigger than the default block size if a push is very large.
222
// Internal head and tail inside the block.
223
int head;
224
int tail;
225
};
226
227
// There's always at least one block, initialized in the constructor.
228
Block *head_;
229
Block *tail_;
230
// Default min block size for new blocks.
231
size_t blockSize_;
232
};
233
234