Fast NBT Parser (loading a chunk is now about 10 times faster)
git-svn-id: http://mc-server.googlecode.com/svn/trunk@481 0a769ca7-a7f5-676a-18bf-c427514a06d6
This commit is contained in:
320
source/FastNBT.cpp
Normal file
320
source/FastNBT.cpp
Normal file
@@ -0,0 +1,320 @@
|
||||
|
||||
// FastNBT.cpp
|
||||
|
||||
// Implements the fast NBT parser and writer
|
||||
|
||||
#include "Globals.h"
|
||||
#include "FastNBT.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define RETURN_FALSE_IF_FALSE(X) do { if (!X) return false; } while (0)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cFastNBTParser:
|
||||
|
||||
#define NEEDBYTES(N) \
|
||||
if (m_Length - m_Pos < N) \
|
||||
{ \
|
||||
return false; \
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cParsedNBT::cParsedNBT(const char * a_Data, int a_Length) :
|
||||
m_Data(a_Data),
|
||||
m_Length(a_Length),
|
||||
m_Pos(0)
|
||||
{
|
||||
m_IsValid = Parse();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cParsedNBT::Parse(void)
|
||||
{
|
||||
if (m_Length < 3)
|
||||
{
|
||||
// Data too short
|
||||
return false;
|
||||
}
|
||||
if (m_Data[0] != TAG_Compound)
|
||||
{
|
||||
// The top-level tag must be a Compound
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Tags.reserve(200);
|
||||
|
||||
m_Tags.push_back(cFastNBTTag(TAG_Compound, -1));
|
||||
|
||||
m_Pos = 1;
|
||||
|
||||
RETURN_FALSE_IF_FALSE(ReadString(m_Tags.back().m_NameStart, m_Tags.back().m_NameLength));
|
||||
RETURN_FALSE_IF_FALSE(ReadCompound());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cParsedNBT::ReadString(int & a_StringStart, int & a_StringLen)
|
||||
{
|
||||
NEEDBYTES(2);
|
||||
a_StringStart = m_Pos + 2;
|
||||
a_StringLen = ntohs(*((short *)(m_Data + m_Pos)));
|
||||
if (a_StringLen < 0)
|
||||
{
|
||||
// Invalid string length
|
||||
return false;
|
||||
}
|
||||
m_Pos += 2 + a_StringLen;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cParsedNBT::ReadCompound(void)
|
||||
{
|
||||
// Reads the latest tag as a compound
|
||||
int ParentIdx = m_Tags.size() - 1;
|
||||
int PrevSibling = -1;
|
||||
while (true)
|
||||
{
|
||||
NEEDBYTES(1);
|
||||
eTagType TagType = (eTagType)(m_Data[m_Pos]);
|
||||
m_Pos++;
|
||||
if (TagType == TAG_End)
|
||||
{
|
||||
break;
|
||||
}
|
||||
m_Tags.push_back(cFastNBTTag(TagType, ParentIdx, PrevSibling));
|
||||
if (PrevSibling >= 0)
|
||||
{
|
||||
m_Tags[PrevSibling].m_NextSibling = m_Tags.size() - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Tags[ParentIdx].m_FirstChild = m_Tags.size() - 1;
|
||||
}
|
||||
PrevSibling = m_Tags.size() - 1;
|
||||
RETURN_FALSE_IF_FALSE(ReadString(m_Tags.back().m_NameStart, m_Tags.back().m_NameLength));
|
||||
RETURN_FALSE_IF_FALSE(ReadTag());
|
||||
} // while (true)
|
||||
m_Tags[ParentIdx].m_LastChild = PrevSibling;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cParsedNBT::ReadList(eTagType a_ChildrenType)
|
||||
{
|
||||
// Reads the latest tag as a list of items of type a_ChildrenType
|
||||
|
||||
// Read the count:
|
||||
NEEDBYTES(4);
|
||||
int Count = ntohl(*((int *)(m_Data + m_Pos)));
|
||||
m_Pos += 4;
|
||||
if (Count < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read items:
|
||||
int ParentIdx = m_Tags.size() - 1;
|
||||
int PrevSibling = -1;
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
m_Tags.push_back(cFastNBTTag(a_ChildrenType, ParentIdx, PrevSibling));
|
||||
if (PrevSibling >= 0)
|
||||
{
|
||||
m_Tags[PrevSibling].m_NextSibling = m_Tags.size() - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Tags[ParentIdx].m_FirstChild = m_Tags.size() - 1;
|
||||
}
|
||||
PrevSibling = m_Tags.size() - 1;
|
||||
RETURN_FALSE_IF_FALSE(ReadTag());
|
||||
} // for (i)
|
||||
m_Tags[ParentIdx].m_LastChild = PrevSibling;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#define CASE_SIMPLE_TAG(TAGTYPE, LEN) \
|
||||
case TAG_##TAGTYPE: \
|
||||
{ \
|
||||
NEEDBYTES(LEN); \
|
||||
Tag.m_DataStart = m_Pos; \
|
||||
Tag.m_DataLength = LEN; \
|
||||
m_Pos += LEN; \
|
||||
return true; \
|
||||
}
|
||||
|
||||
bool cParsedNBT::ReadTag(void)
|
||||
{
|
||||
cFastNBTTag & Tag = m_Tags.back();
|
||||
switch (Tag.m_Type)
|
||||
{
|
||||
CASE_SIMPLE_TAG(Byte, 1)
|
||||
CASE_SIMPLE_TAG(Short, 2)
|
||||
CASE_SIMPLE_TAG(Int, 4)
|
||||
CASE_SIMPLE_TAG(Long, 8)
|
||||
CASE_SIMPLE_TAG(Float, 4)
|
||||
CASE_SIMPLE_TAG(Double, 8)
|
||||
|
||||
case TAG_String:
|
||||
{
|
||||
return ReadString(Tag.m_DataStart, Tag.m_DataLength);
|
||||
}
|
||||
|
||||
case TAG_ByteArray:
|
||||
{
|
||||
NEEDBYTES(4);
|
||||
int len = ntohl(*((int *)(m_Data + m_Pos)));
|
||||
m_Pos += 4;
|
||||
if (len < 0)
|
||||
{
|
||||
// Invalid length
|
||||
return false;
|
||||
}
|
||||
NEEDBYTES(len);
|
||||
Tag.m_DataLength = len;
|
||||
Tag.m_DataStart = m_Pos;
|
||||
m_Pos += len;
|
||||
return true;
|
||||
}
|
||||
|
||||
case TAG_List:
|
||||
{
|
||||
NEEDBYTES(1);
|
||||
eTagType ItemType = (eTagType)m_Data[m_Pos];
|
||||
m_Pos++;
|
||||
RETURN_FALSE_IF_FALSE(ReadList(ItemType));
|
||||
return true;
|
||||
}
|
||||
|
||||
case TAG_Compound:
|
||||
{
|
||||
RETURN_FALSE_IF_FALSE(ReadCompound());
|
||||
return true;
|
||||
}
|
||||
|
||||
case TAG_IntArray:
|
||||
{
|
||||
NEEDBYTES(4);
|
||||
int len = ntohl(*((int *)(m_Data + m_Pos)));
|
||||
m_Pos += 4;
|
||||
if (len < 0)
|
||||
{
|
||||
// Invalid length
|
||||
return false;
|
||||
}
|
||||
len *= 4;
|
||||
NEEDBYTES(len);
|
||||
Tag.m_DataLength = len;
|
||||
Tag.m_DataStart = m_Pos;
|
||||
m_Pos += len;
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
ASSERT(!"Unhandled NBT tag type");
|
||||
return false;
|
||||
}
|
||||
} // switch (iType)
|
||||
}
|
||||
|
||||
#undef CASE_SIMPLE_TAG
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cParsedNBT::FindChildByName(int a_Tag, const char * a_Name, size_t a_NameLength) const
|
||||
{
|
||||
if (a_Tag < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (m_Tags[a_Tag].m_Type != TAG_Compound)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (a_NameLength == 0)
|
||||
{
|
||||
a_NameLength = strlen(a_Name);
|
||||
}
|
||||
for (int Child = m_Tags[a_Tag].m_FirstChild; Child != -1; Child = m_Tags[Child].m_NextSibling)
|
||||
{
|
||||
if (
|
||||
(m_Tags[Child].m_NameLength == a_NameLength) &&
|
||||
(memcmp(m_Data + m_Tags[Child].m_NameStart, a_Name, a_NameLength) == 0)
|
||||
)
|
||||
{
|
||||
return Child;
|
||||
}
|
||||
} // for Child - children of a_Tag
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cParsedNBT::FindTagByPath(int a_Tag, const AString & a_Path) const
|
||||
{
|
||||
if (a_Tag < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
size_t Begin = 0;
|
||||
size_t Length = a_Path.length();
|
||||
int Tag = a_Tag;
|
||||
for (size_t i = 0; i < Length; i++)
|
||||
{
|
||||
if (a_Path[i] != '\\')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Tag = FindChildByName(Tag, a_Path.c_str() + Begin, i - Begin - 1);
|
||||
if (Tag < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
Begin = i + 1;
|
||||
} // for i - a_Path[]
|
||||
|
||||
if (Begin < Length)
|
||||
{
|
||||
Tag = FindChildByName(Tag, a_Path.c_str() + Begin, Length - Begin);
|
||||
}
|
||||
return Tag;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user