EmBencode
Implementation of the Bencode serialisation format for embedded microcontrollers.
 All Classes Files Functions Pages
EmBencode.h
Go to the documentation of this file.
1 /// @file
2 /// Embedded bencode support, header definitions.
3 // 2012-09-29 <jc@wippler.nl> http://opensource.org/licenses/mit-license.php
4 
5 #include <stdint.h>
6 #include <stdlib.h>
7 #include <string.h>
8 
9 /// Encoder class to generate Bencode on the fly (no buffer storage needed).
10 class EmBencode {
11 public:
12  EmBencode () {}
13 
14  /// Push a string out in Bencode format.
15  /// @param str The zero-terminated string to send out (without trailing \0).
16  static void push (const char* str) {
17  push(str, strlen(str));
18  }
19 
20  /// Push arbitrary bytes in Bencode format.
21  /// @param ptr Pointer to the data to send out.
22  /// @param len Number of data bytes to send out.
23  static void push (const void* ptr, uint8_t len) {
24  PushCount(len);
25  PushChar(':');
26  PushData(ptr, len);
27  }
28 
29  /// Push a signed integer in Bencode format.
30  /// @param val The integer to send (this implementation supports 32 bits).
31  static void push (long val) {
32  PushChar('i');
33  if (val < 0) {
34  PushChar('-');
35  val = -val;
36  }
37  PushCount(val);
38  PushEnd();
39  }
40 
41  /// Start a new new list. Must be matched with a call to endList().
42  /// Entries can be nested with more calls to startList(), startDict(), etc.
43  static void startList () {
44  PushChar('l');
45  }
46 
47  /// Terminate a list, started earlier with a call to startList().
48  static void endList () {
49  PushEnd();
50  }
51 
52  /// Start a new new dictionary. Must be matched with a call to endDict().
53  /// Dictionary entries must consist of a string key plus an arbitrary value.
54  /// Entries can be nested with more calls to startList(), startDict(), etc.
55  static void startDict () {
56  PushChar('d');
57  }
58 
59  /// Terminate a dictionary, started earlier with a call to startDict().
60  static void endDict () {
61  PushEnd();
62  }
63 
64 protected:
65  static void PushCount (uint32_t num) {
66  char buf[11];
67  PushData(ultoa(num, buf, 10), strlen(buf));
68  }
69 
70  static void PushEnd () {
71  PushChar('e');
72  }
73 
74  static void PushData (const void* ptr, uint8_t len) {
75  for (const char* p = (const char*) ptr; len-- > 0; ++p)
76  PushChar(*p);
77  }
78 
79  /// This function is not implemented in the library. It must be supplied by
80  /// the caller to implement the actual writing of caharacters.
81  static void PushChar (char ch);
82 };
83 
84 /// Decoder class, needs an external buffer to collect the incoming data.
85 class EmBdecode {
86 public:
87  /// Types of tokens, as returned by nextToken().
88  enum { T_STRING = 0, T_NUMBER = 251, T_DICT, T_LIST, T_POP, T_END };
89 
90  /// Initialize a decoder instance with the specified buffer space.
91  /// @param buf Pointer to the buffer which will be used by the decoder.
92  /// @param len Size of the buffer, must be in the range 50 to 255.
93  EmBdecode (char* buf, uint8_t len)
94  : bufPtr (buf), bufLen (len) { reset(); }
95 
96  /// Reset the decoder - can be called to prepare for a new round of decoding.
97  uint8_t reset ();
98 
99  /// Process a single incoming caharacter.
100  /// @return Returns a count > 0 when the buffer contains a complete packet.
101  uint8_t process (char ch);
102 
103  /// Call this after process() is done, to extract each of the data tokens.
104  /// @returns Returns one of the T_STRING .. T_END enumeration codes.
105  uint8_t nextToken ();
106  /// Extract the last token as string (works for T_STRING and T_NUMBER).
107  /// @param plen This variable will receive the size, if present.
108  /// @return Returns pointer to a zero-terminated string in the decode buffer.
109  const char* asString (uint8_t* plen =0);
110  /// Extract the last token as number (also works for strings if numeric).
111  /// @return Returns the decoded integer, max 32-bit signed in this version.
112  long asNumber ();
113 
114 protected:
115  void AddToBuf (char ch);
116 
117  char level, *bufPtr;
118  uint8_t bufLen, count, next, last, state;
119 };
120 
121 // Encoder class for a tightly-packed one-to-one transformation of Bencode.
122 class EmBenPack {
123  enum { PT_EXT, PT_POS, PT_NEG, PT_STR };
124  enum { PE_END, PE_LIST, PE_DICT };
125 public:
126  EmBenPack (char* ptr, uint8_t len) : buf (ptr), limit (len) {
127  reset ();
128  }
129 
130  uint8_t fill () const {
131  return next;
132  }
133 
134  void reset () {
135  next = 0;
136  }
137 
138  void push (const char* str) {
139  push(str, strlen(str));
140  }
141 
142  void push (const void* ptr, uint8_t len) {
143  PushCount((len << 2) | PT_STR);
144  for (const char* p = (const char*) ptr; len-- > 0; ++p)
145  PushChar(*p);
146  }
147 
148  /// @bug this currently only works for longs up to 30 bits
149  void push (long val) {
150  PushCount(val < 0 ? (~val << 2) | PT_NEG
151  : (val << 2) | PT_POS);
152  }
153 
154  void startList () {
155  PushChar((PE_LIST << 2) | PT_EXT);
156  }
157 
158  void endList () {
159  PushEnd();
160  }
161 
162  void startDict () {
163  PushChar((PE_DICT << 2) | PT_EXT);
164  }
165 
166  void endDict () {
167  PushEnd();
168  }
169 
170  void pushMarker () {
171  PushChar(0); // special value, doesn't match any other valid item
172  }
173 
174 protected:
175  void PushCount (uint32_t num, uint8_t flag =0x80) {
176  if (num > 0x7F)
177  PushCount(num >> 7, 0);
178  PushCount(flag | (num & 0x7F));
179  }
180 
181  void PushEnd () {
182  PushChar((PE_END << 2) | PT_EXT);
183  }
184 
185  void PushChar (char ch) {
186  if (next < limit)
187  buf[next++] = ch;
188  }
189 
190  char* buf;
191  uint8_t limit, next;
192 };