Cuberite
A lightweight, fast and extensible game server for Minecraft
FastNBT.h
Go to the documentation of this file.
1 
2 // FastNBT.h
3 
4 // Interfaces to the fast NBT parser and writer
5 
6 /*
7 The fast parser parses the data into a vector of cFastNBTTag structures. These structures describe the NBT tree,
8 but themselves are allocated in a vector, thus minimizing reallocation.
9 The structures have a minimal constructor, setting all member "pointers" to "invalid".
10 
11 The fast writer doesn't need a NBT tree structure built beforehand, it is commanded to open, append and close tags
12 (just like XML); it keeps the internal tag stack and reports errors in usage.
13 It directly outputs a string containing the serialized NBT data.
14 */
15 
16 
17 
18 
19 
20 #pragma once
21 
22 #include <system_error>
23 #include "../Endianness.h"
24 
25 
26 
27 
28 
30 {
31  TAG_Min = 0, // The minimum value for a tag type
32  TAG_End = 0,
33  TAG_Byte = 1,
34  TAG_Short = 2,
35  TAG_Int = 3,
36  TAG_Long = 4,
37  TAG_Float = 5,
41  TAG_List = 9,
44  TAG_Max = 11, // The maximum value for a tag type
45 } ;
46 
47 
48 
49 
50 
58 {
59 public:
60 
62 
63  // The following members are indices into the data stream. m_DataLength == 0 if no data available
64  // They must not be pointers, because the datastream may be copied into another AString object in the meantime.
65  size_t m_NameStart;
66  size_t m_NameLength;
67  size_t m_DataStart;
68  size_t m_DataLength;
69 
70  // The following members are indices into the array returned; -1 if not valid
71  // They must not be pointers, because pointers would not survive std::vector reallocation
72  int m_Parent;
77 
78  cFastNBTTag(eTagType a_Type, int a_Parent) :
79  m_Type(a_Type),
80  m_NameStart(0),
81  m_NameLength(0),
82  m_DataStart(0),
83  m_DataLength(0),
84  m_Parent(a_Parent),
85  m_PrevSibling(-1),
86  m_NextSibling(-1),
87  m_FirstChild(-1),
88  m_LastChild(-1)
89  {
90  }
91 
92  cFastNBTTag(eTagType a_Type, int a_Parent, int a_PrevSibling) :
93  m_Type(a_Type),
94  m_NameStart(0),
95  m_NameLength(0),
96  m_DataStart(0),
97  m_DataLength(0),
98  m_Parent(a_Parent),
99  m_PrevSibling(a_PrevSibling),
100  m_NextSibling(-1),
101  m_FirstChild(-1),
102  m_LastChild(-1)
103  {
104  }
105 } ;
106 
107 
108 
109 
110 
111 enum class eNBTParseError
112 {
113  npSuccess = 0,
114  npNeedBytes,
125  npUnknownTag,
126 };
127 
128 // The following is required to make an error_code constructible from an eNBTParseError
129 std::error_code make_error_code(eNBTParseError a_Err) NOEXCEPT;
130 
131 namespace std
132 {
133  template <>
134  struct is_error_code_enum<eNBTParseError>:
135  public std::true_type
136  {
137  };
138 }
139 
140 
141 
142 
143 
153 {
154 public:
155  cParsedNBT(const char * a_Data, size_t a_Length);
156 
157  bool IsValid(void) const { return (m_Error == eNBTParseError::npSuccess); }
158 
160  std::error_code GetErrorCode() const { return m_Error; }
161 
163  size_t GetErrorPos() const { return m_Pos; }
164 
166  int GetRoot(void) const { return 0; }
167 
169  int GetFirstChild (int a_Tag) const { return m_Tags[static_cast<size_t>(a_Tag)].m_FirstChild; }
170 
172  int GetLastChild (int a_Tag) const { return m_Tags[static_cast<size_t>(a_Tag)].m_LastChild; }
173 
175  int GetNextSibling(int a_Tag) const { return m_Tags[static_cast<size_t>(a_Tag)].m_NextSibling; }
176 
178  int GetPrevSibling(int a_Tag) const { return m_Tags[static_cast<size_t>(a_Tag)].m_PrevSibling; }
179 
182  size_t GetDataLength (int a_Tag) const
183  {
184  ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type != TAG_List);
185  ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type != TAG_Compound);
186  return m_Tags[static_cast<size_t>(a_Tag)].m_DataLength;
187  }
188 
191  const char * GetData(int a_Tag) const
192  {
193  ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type != TAG_List);
194  ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type != TAG_Compound);
195  return m_Data + m_Tags[static_cast<size_t>(a_Tag)].m_DataStart;
196  }
197 
199  int FindChildByName(int a_Tag, const AString & a_Name) const
200  {
201  return FindChildByName(a_Tag, a_Name.c_str(), a_Name.length());
202  }
203 
205  int FindChildByName(int a_Tag, const char * a_Name, size_t a_NameLength = 0) const;
206 
208  int FindTagByPath(int a_Tag, const AString & a_Path) const;
209 
210  eTagType GetType(int a_Tag) const { return m_Tags[static_cast<size_t>(a_Tag)].m_Type; }
211 
213  eTagType GetChildrenType(int a_Tag) const
214  {
215  ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type == TAG_List);
216  return (m_Tags[static_cast<size_t>(a_Tag)].m_FirstChild < 0) ? TAG_End : m_Tags[static_cast<size_t>(m_Tags[static_cast<size_t>(a_Tag)].m_FirstChild)].m_Type;
217  }
218 
220  inline unsigned char GetByte(int a_Tag) const
221  {
222  ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type == TAG_Byte);
223  return static_cast<unsigned char>(m_Data[static_cast<size_t>(m_Tags[static_cast<size_t>(a_Tag)].m_DataStart)]);
224  }
225 
227  inline Int16 GetShort(int a_Tag) const
228  {
229  ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type == TAG_Short);
230  return GetBEShort(m_Data + m_Tags[static_cast<size_t>(a_Tag)].m_DataStart);
231  }
232 
234  inline Int32 GetInt(int a_Tag) const
235  {
236  ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type == TAG_Int);
237  return GetBEInt(m_Data + m_Tags[static_cast<size_t>(a_Tag)].m_DataStart);
238  }
239 
241  inline Int64 GetLong(int a_Tag) const
242  {
243  ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type == TAG_Long);
244  return NetworkToHostLong8(m_Data + m_Tags[static_cast<size_t>(a_Tag)].m_DataStart);
245  }
246 
248  inline float GetFloat(int a_Tag) const
249  {
250  ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type == TAG_Float);
251 
252  // Cause a compile-time error if sizeof(float) != 4
253  // If your platform produces a compiler error here, you'll need to add code that manually decodes 32-bit floats
254  char Check1[5 - sizeof(float)]; // Fails if sizeof(float) > 4
255  char Check2[sizeof(float) - 3]; // Fails if sizeof(float) < 4
256  UNUSED_VAR(Check1);
257  UNUSED_VAR(Check2);
258 
259  Int32 i = GetBEInt(m_Data + m_Tags[static_cast<size_t>(a_Tag)].m_DataStart);
260  float f;
261  memcpy(&f, &i, sizeof(f));
262  return f;
263  }
264 
266  inline double GetDouble(int a_Tag) const
267  {
268  // Cause a compile-time error if sizeof(double) != 8
269  // If your platform produces a compiler error here, you'll need to add code that manually decodes 64-bit doubles
270  char Check1[9 - sizeof(double)]; // Fails if sizeof(double) > 8
271  char Check2[sizeof(double) - 7]; // Fails if sizeof(double) < 8
272  UNUSED_VAR(Check1);
273  UNUSED_VAR(Check2);
274 
275  ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type == TAG_Double);
276  return NetworkToHostDouble8(m_Data + m_Tags[static_cast<size_t>(a_Tag)].m_DataStart);
277  }
278 
280  inline AString GetString(int a_Tag) const
281  {
282  ASSERT(m_Tags[static_cast<size_t>(a_Tag)].m_Type == TAG_String);
283  AString res;
284  res.assign(m_Data + m_Tags[static_cast<size_t>(a_Tag)].m_DataStart, static_cast<size_t>(m_Tags[static_cast<size_t>(a_Tag)].m_DataLength));
285  return res;
286  }
287 
289  inline AString GetName(int a_Tag) const
290  {
291  AString res;
292  res.assign(m_Data + m_Tags[static_cast<size_t>(a_Tag)].m_NameStart, static_cast<size_t>(m_Tags[static_cast<size_t>(a_Tag)].m_NameLength));
293  return res;
294  }
295 
296 protected:
297  const char * m_Data;
298  size_t m_Length;
299  std::vector<cFastNBTTag> m_Tags;
300  eNBTParseError m_Error; // npSuccess if parsing succeeded
301 
302  // Used while parsing:
303  size_t m_Pos;
304 
305  eNBTParseError Parse(void);
306  eNBTParseError ReadString(size_t & a_StringStart, size_t & a_StringLen); // Reads a simple string (2 bytes length + data), sets the string descriptors
307  eNBTParseError ReadCompound(void); // Reads the latest tag as a compound
308  eNBTParseError ReadList(eTagType a_ChildrenType); // Reads the latest tag as a list of items of type a_ChildrenType
309  eNBTParseError ReadTag(void); // Reads the latest tag, depending on its m_Type setting
310 } ;
311 
312 
313 
314 
315 
317 {
318 public:
319  cFastNBTWriter(const AString & a_RootTagName = "");
320 
321  void BeginCompound(const AString & a_Name);
322  void EndCompound(void);
323 
324  void BeginList(const AString & a_Name, eTagType a_ChildrenType);
325  void EndList(void);
326 
327  void AddByte (const AString & a_Name, unsigned char a_Value);
328  void AddShort (const AString & a_Name, Int16 a_Value);
329  void AddInt (const AString & a_Name, Int32 a_Value);
330  void AddLong (const AString & a_Name, Int64 a_Value);
331  void AddFloat (const AString & a_Name, float a_Value);
332  void AddDouble (const AString & a_Name, double a_Value);
333  void AddString (const AString & a_Name, const AString & a_Value);
334  void AddByteArray(const AString & a_Name, const char * a_Value, size_t a_NumElements);
335  void AddIntArray (const AString & a_Name, const int * a_Value, size_t a_NumElements);
336 
337  void AddByteArray(const AString & a_Name, const AString & a_Value)
338  {
339  AddByteArray(a_Name, a_Value.data(), a_Value.size());
340  }
341 
342  const AString & GetResult(void) const {return m_Result; }
343 
344  void Finish(void);
345 
346 protected:
347 
348  struct sParent
349  {
350  int m_Type; // TAG_Compound or TAG_List
351  int m_Pos; // for TAG_List, the position of the list count
352  int m_Count; // for TAG_List, the element count
353  eTagType m_ItemType; // for TAG_List, the element type
354  } ;
355 
356  static const int MAX_STACK = 50; // Highly doubtful that an NBT would be constructed this many levels deep
357 
358  // These two fields emulate a stack. A raw array is used due to speed issues - no reallocations are allowed.
359  sParent m_Stack[MAX_STACK];
361 
363 
364  bool IsStackTopCompound(void) const { return (m_Stack[m_CurrentStack].m_Type == TAG_Compound); }
365 
366  void WriteString(const char * a_Data, UInt16 a_Length);
367 
368  inline void TagCommon(const AString & a_Name, eTagType a_Type)
369  {
370  // If we're directly inside a list, check that the list is of the correct type:
371  ASSERT((m_Stack[m_CurrentStack].m_Type != TAG_List) || (m_Stack[m_CurrentStack].m_ItemType == a_Type));
372 
373  if (IsStackTopCompound())
374  {
375  // Compound: add the type and name:
376  m_Result.push_back(static_cast<char>(a_Type));
377  WriteString(a_Name.c_str(), static_cast<UInt16>(a_Name.length()));
378  }
379  else
380  {
381  // List: add to the counter
382  m_Stack[m_CurrentStack].m_Count++;
383  }
384  }
385 } ;
386 
387 
388 
389 
Int64 NetworkToHostLong8(const void *a_Value)
Definition: Endianness.h:50
int m_Parent
Definition: FastNBT.h:72
void TagCommon(const AString &a_Name, eTagType a_Type)
Definition: FastNBT.h:368
Definition: FastNBT.h:131
float GetFloat(int a_Tag) const
Returns the value stored in a Float tag.
Definition: FastNBT.h:248
Int64 GetLong(int a_Tag) const
Returns the value stored in a Long tag.
Definition: FastNBT.h:241
This structure is used for all NBT tags.
Definition: FastNBT.h:57
const char * GetData(int a_Tag) const
Returns the data stored in this tag.
Definition: FastNBT.h:191
std::error_code make_error_code(eNBTParseError a_Err) NOEXCEPT
Definition: FastNBT.cpp:133
size_t m_Length
Definition: FastNBT.h:298
signed short Int16
Definition: Globals.h:109
int m_CurrentStack
Definition: FastNBT.h:360
size_t GetErrorPos() const
Returns the position where an error occurred while parsing.
Definition: FastNBT.h:163
bool IsValid(void) const
Definition: FastNBT.h:157
int m_LastChild
Definition: FastNBT.h:76
Parses and contains the parsed data Also implements data accessor functions for tree traversal and va...
Definition: FastNBT.h:152
int GetRoot(void) const
Returns the root tag of the hierarchy.
Definition: FastNBT.h:166
AString m_Result
Definition: FastNBT.h:362
eTagType GetType(int a_Tag) const
Definition: FastNBT.h:210
size_t m_Pos
Definition: FastNBT.h:303
double NetworkToHostDouble8(const void *a_Value)
Definition: Endianness.h:36
size_t m_NameStart
Definition: FastNBT.h:65
size_t m_DataStart
Definition: FastNBT.h:67
std::vector< cFastNBTTag > m_Tags
Definition: FastNBT.h:299
eTagType m_Type
Definition: FastNBT.h:61
Int32 GetInt(int a_Tag) const
Returns the value stored in an Int tag.
Definition: FastNBT.h:234
double GetDouble(int a_Tag) const
Returns the value stored in a Double tag.
Definition: FastNBT.h:266
std::error_code GetErrorCode() const
Returns the error code for the parsing of the NBT data.
Definition: FastNBT.h:160
short GetBEShort(const char *a_Mem)
Reads two bytes from the specified memory location and interprets them as BigEndian short...
eNBTParseError
Definition: FastNBT.h:111
eTagType
Definition: FastNBT.h:29
Int16 GetShort(int a_Tag) const
Returns the value stored in a Short tag.
Definition: FastNBT.h:227
cFastNBTTag(eTagType a_Type, int a_Parent)
Definition: FastNBT.h:78
int m_NextSibling
Definition: FastNBT.h:74
#define ASSERT(x)
Definition: Globals.h:335
int GetNextSibling(int a_Tag) const
Returns the next sibling of the specified tag, or -1 if none.
Definition: FastNBT.h:175
eTagType GetChildrenType(int a_Tag) const
Returns the children type for a List tag; undefined on other tags.
Definition: FastNBT.h:213
int m_FirstChild
Definition: FastNBT.h:75
int GetPrevSibling(int a_Tag) const
Returns the previous sibling of the specified tag, or -1 if none.
Definition: FastNBT.h:178
const char * m_Data
Definition: FastNBT.h:297
bool IsStackTopCompound(void) const
Definition: FastNBT.h:364
AString GetName(int a_Tag) const
Returns the tag&#39;s name.
Definition: FastNBT.h:289
unsigned short UInt16
Definition: Globals.h:114
const AString & GetResult(void) const
Definition: FastNBT.h:342
int m_PrevSibling
Definition: FastNBT.h:73
std::string AString
Definition: StringUtils.h:13
int FindChildByName(int a_Tag, const AString &a_Name) const
Returns the direct child tag of the specified name, or -1 if no such tag.
Definition: FastNBT.h:199
#define UNUSED_VAR(X)
Definition: Globals.h:145
size_t GetDataLength(int a_Tag) const
Returns the length of the tag&#39;s data, in bytes.
Definition: FastNBT.h:182
size_t m_NameLength
Definition: FastNBT.h:66
signed int Int32
Definition: Globals.h:108
size_t m_DataLength
Definition: FastNBT.h:68
int GetLastChild(int a_Tag) const
Returns the last child of the specified tag, or -1 if none / not applicable.
Definition: FastNBT.h:172
unsigned char GetByte(int a_Tag) const
Returns the value stored in a Byte tag.
Definition: FastNBT.h:220
AString GetString(int a_Tag) const
Returns the value stored in a String tag.
Definition: FastNBT.h:280
eNBTParseError m_Error
Definition: FastNBT.h:300
int GetBEInt(const char *a_Mem)
Reads four bytes from the specified memory location and interprets them as BigEndian int...
void AddByteArray(const AString &a_Name, const AString &a_Value)
Definition: FastNBT.h:337
cFastNBTTag(eTagType a_Type, int a_Parent, int a_PrevSibling)
Definition: FastNBT.h:92
signed long long Int64
Definition: Globals.h:107
int GetFirstChild(int a_Tag) const
Returns the first child of the specified tag, or -1 if none / not applicable.
Definition: FastNBT.h:169