forked from sysrun/embencode
-
Notifications
You must be signed in to change notification settings - Fork 0
/
EmBencode.h
253 lines (225 loc) · 5.78 KB
/
EmBencode.h
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
/// @file
/// Embedded bencode support, header definitions.
// 2012-09-29 <jc@wippler.nl> http://opensource.org/licenses/mit-license.php
#pragma once
#ifndef _EMBENCODE_h
#define _EMBENCODE_h
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
/// Encoder class to generate Bencode on the fly (no buffer storage needed).
template <int bufLen>
class EmBencode {
public:
uint8_t buffer[bufLen];
uint8_t buffIdx = 0;
EmBencode () {}
/// Push a string out in Bencode format.
/// @param str The zero-terminated string to send out (without trailing \0).
void push (const char* str) {
push(str, strlen(str));
}
/// Push arbitrary bytes in Bencode format.
/// @param ptr Pointer to the data to send out.
/// @param len Number of data bytes to send out.
void push (const void* ptr, uint8_t len) {
PushCount(len);
PushChar(':');
PushData(ptr, len);
}
/// Push a signed integer in Bencode format.
/// @param val The integer to send (this implementation supports 32 bits).
void push (long val) {
PushChar('i');
if (val < 0) {
PushChar('-');
val = -val;
}
PushCount(val);
PushEnd();
}
/// Push a zero interger in Bencode format.
void pushZero() {
PushChar('i');
PushChar('0');
PushEnd();
}
/// Start a new new list. Must be matched with a call to endList().
/// Entries can be nested with more calls to startList(), startDict(), etc.
void startList () {
PushChar('l');
}
/// Terminate a list, started earlier with a call to startList().
void endList () {
PushEnd();
}
/// Start a new new dictionary. Must be matched with a call to endDict().
/// Dictionary entries must consist of a string key plus an arbitrary value.
/// Entries can be nested with more calls to startList(), startDict(), etc.
void startDict () {
PushChar('d');
}
/// Terminate a dictionary, started earlier with a call to startDict().
void endDict () {
PushEnd();
}
void reset()
{
memset(&buffer, 0, sizeof(buffer));
buffIdx = 0;
}
protected:
void PushCount (uint32_t num) {
char buf[11];
ultoa(num, buf, 10);
PushData(buf, strlen(buf));
}
void PushEnd () {
PushChar('e');
}
void PushData (const void* ptr, uint8_t len) {
for (const char* p = (const char*) ptr; len > 0; ++p, --len)
PushChar(*p);
}
/// This function is not implemented in the library. It must be supplied by
/// the caller to implement the actual writing of caharacters.
void PushChar(char ch)
{
buffer[buffIdx] = ch;
buffIdx++;
}
};
/// Decoder enum
enum { EMB_ANY, EMB_LEN, EMB_INT, EMB_STR };
enum { T_STRING = 0, T_NUMBER = 251, T_DICT, T_LIST, T_POP, T_END };
/// Decoder class, templated internal buffer to collect the incoming data.
template <int bufLen>
class EmBdecode {
protected:
char level, buffer[bufLen];
uint8_t count, next, last, state;
void AddToBuf(char ch)
{
if (next >= bufLen)
buffer[0] = T_END; // mark entire buffer as empty
else
buffer[next++] = ch;
};
public:
/// Types of tokens, as returned by nextToken().
enum { T_STRING = 0, T_NUMBER = 251, T_DICT, T_LIST, T_POP, T_END };
/// Initialize a decoder instance with the specified buffer space.
/// @param buf Pointer to the buffer which will be used by the decoder.
/// @param len Size of the buffer, must be in the range 50 to 255.
EmBdecode()
{
reset();
}
/// Reset the decoder - can be called to prepare for a new round of decoding.
uint8_t reset()
{
count = next;
level = next = 0;
state = EMB_ANY;
return count;
}
/// Process a single incoming caharacter.
/// @return Returns a count > 0 when the buffer contains a complete packet.
uint8_t process(char ch){
switch (state) {
case EMB_ANY:
if (ch < '0' || ch > '9') {
if (ch == 'i') {
AddToBuf(T_NUMBER);
state = EMB_INT;
}
else if (ch == 'd' || ch == 'l') {
AddToBuf(ch == 'd' ? T_DICT : T_LIST);
++level;
}
else if (ch == 'e') {
AddToBuf(T_POP);
--level;
break; // end of dict or list
}
return 0;
}
state = EMB_LEN;
count = 0;
// fall through
case EMB_LEN:
if (ch == ':') {
AddToBuf(T_STRING + count);
if (count == 0) {
AddToBuf(0);
break; // empty string
}
state = EMB_STR;
}
else
count = 10 * count + (ch - '0');
return 0;
case EMB_STR:
AddToBuf(ch);
if (--count == 0) {
AddToBuf(0);
break; // end of string
}
return 0;
case EMB_INT:
if (ch == 'e') {
AddToBuf(0);
break; // end of int
}
AddToBuf(ch);
return 0;
}
// end of an item reached
if (level > 0) {
state = EMB_ANY;
return 0;
}
AddToBuf(T_END);
return reset(); // not in dict or list, data is complete
};
/// Call this after process() is done, to extract each of the data tokens.
/// @returns Returns one of the T_STRING .. T_END enumeration codes.
uint8_t nextToken ()
{
uint8_t ch = buffer[next++];
last = next;
switch (ch) {
default: // string
next += ch + 1;
return T_STRING;
case T_NUMBER:
while (buffer[next++] != 0)
;
break;
case T_END:
--next; // don't advance past end token
// fall through
case T_DICT:
case T_LIST:
case T_POP:
break;
}
return ch;
};
/// Extract the last token as string (works for T_STRING and T_NUMBER).
/// @param plen This variable will receive the size, if present.
/// @return Returns pointer to a zero-terminated string in the decode buffer.
const char* asString (uint8_t* plen =0)
{
if (plen != 0)
*plen = next - last - 1;
return buffer + last;
};
/// Extract the last token as number (also works for strings if numeric).
/// @return Returns the decoded integer, max 32-bit signed in this version.
long asNumber ()
{
return atol(buffer + last);
};
};
#endif