1
0

BlockTypePalette: Improved loading speed in MSVC Debug builds.

This commit is contained in:
Mattes D 2020-01-13 22:38:33 +01:00
parent 3d699c0dc4
commit a0d2e934eb
2 changed files with 134 additions and 49 deletions

View File

@ -7,6 +7,30 @@
/** Returns the index into aString >= aStartIdx at which the next separator occurs.
Separator is one of \t, \n or \r.
Returns AString::npos if no such separator. */
static size_t findNextSeparator(const AString & aString, size_t aStartIdx = 0)
{
for (size_t i = aStartIdx, len = aString.length(); i < len; ++i)
{
switch (aString[i])
{
case '\t':
case '\n':
case '\r':
{
return i;
}
}
}
return AString::npos;
}
BlockTypePalette::BlockTypePalette(): BlockTypePalette::BlockTypePalette():
mMaxIndex(0) mMaxIndex(0)
{ {
@ -196,89 +220,150 @@ void BlockTypePalette::loadFromJsonString(const AString & aJsonPalette)
void BlockTypePalette::loadFromTsv(const AString & aTsvPalette, bool aIsUpgrade) void BlockTypePalette::loadFromTsv(const AString & aTsvPalette, bool aIsUpgrade)
{ {
auto lines = StringSplitAndTrim(aTsvPalette, "\n"); static const AString hdrTsvRegular = "BlockTypePalette";
static const AString hdrTsvUpgrade = "UpgradeBlockTypePalette";
// Check the file signature:
auto idx = findNextSeparator(aTsvPalette);
if ((idx == AString::npos) || (aTsvPalette[idx] == '\t'))
{
throw LoadFailedException("Invalid signature");
}
auto signature = aTsvPalette.substr(0, idx);
bool isUpgrade = (signature == hdrTsvUpgrade);
if (!isUpgrade && (signature != hdrTsvRegular))
{
throw LoadFailedException("Unknown signature");
}
if (aTsvPalette[idx] == '\r') // CR of the CRLF pair, skip the LF:
{
idx += 1;
}
// Parse the header: // Parse the header:
int fileVersion = 0; bool hasHadVersion = false;
AString commonPrefix; AString commonPrefix;
auto numLines = lines.size(); int line = 2;
for (size_t idx = 1; idx < numLines; ++idx) auto len = aTsvPalette.length();
while (true)
{ {
const auto & line = lines[idx]; auto keyStart = idx + 1;
if (line.empty()) auto keyEnd = findNextSeparator(aTsvPalette, idx + 1);
if (keyEnd == AString::npos)
{ {
// End of headers, erase them from lines[] and go parse the data throw LoadFailedException(Printf("Invalid header key format on line %u", line));
lines.erase(lines.begin(), lines.begin() + static_cast<AStringVector::difference_type>(idx) + 1); }
if (keyEnd == idx + 1) // Empty line, end of headers
{
if (aTsvPalette[keyEnd] == '\r') // CR of the CRLF pair, skip the LF:
{
++keyEnd;
}
idx = keyEnd;
++line;
break; break;
} }
auto s = StringSplit(line, "\t"); auto valueEnd = findNextSeparator(aTsvPalette, keyEnd + 1);
if (s.size() != 2) if ((valueEnd == AString::npos) || (aTsvPalette[valueEnd] == '\t'))
{ {
throw LoadFailedException(Printf("Invalid header format on line %u", idx + 1)); throw LoadFailedException(Printf("Invalid header value format on line %u", line));
} }
if (s[0] == "FileVersion") auto key = aTsvPalette.substr(keyStart, keyEnd - keyStart);
if (key == "FileVersion")
{ {
try unsigned version = 0;
auto value = aTsvPalette.substr(keyEnd + 1, valueEnd - keyEnd - 1);
if (!StringToInteger(value, version))
{ {
fileVersion = std::stoi(s[1]); throw LoadFailedException("Invalid FileVersion value");
} }
catch (const std::exception & exc) else if (version != 1)
{ {
throw LoadFailedException(Printf("Invalid file version: \"%d\" (%s)", s[1], exc.what())); throw LoadFailedException(Printf("Unknown FileVersion: %u. Only version 1 is supported.", version));
} }
hasHadVersion = true;
} }
else if (s[0] == "CommonPrefix") else if (key == "CommonPrefix")
{ {
commonPrefix = s[1]; commonPrefix = aTsvPalette.substr(keyEnd + 1, valueEnd - keyEnd - 1);
} }
idx = valueEnd;
if (aTsvPalette[idx] == '\r') // CR of the CRLF pair, skip the LF:
{
++idx;
}
++line;
} }
if (fileVersion != 1) if (!hasHadVersion)
{ {
throw LoadFailedException(Printf("Unknown file version (%d), only version 1 is supported", fileVersion)); throw LoadFailedException("No FileVersion value");
} }
// Parse the data: // Parse the data:
size_t minSplit = aIsUpgrade ? 3 : 2; while (idx + 1 < len)
for (const auto & line: lines)
{ {
auto s = StringSplit(line, "\t"); auto lineStart = idx + 1;
auto numSplit = s.size(); auto idEnd = findNextSeparator(aTsvPalette, lineStart);
if (numSplit < minSplit) if ((idEnd == AString::npos) || (aTsvPalette[idEnd] != '\t'))
{ {
throw LoadFailedException(Printf("Not enough values on data line: \"%s\"", line)); throw LoadFailedException(Printf("Incomplete data on line %u (id)", line));
} }
UInt32 id; UInt32 id;
try if (!StringToInteger(aTsvPalette.substr(lineStart, idEnd - lineStart), id))
{ {
id = static_cast<UInt32>(std::stoi(s[0])); throw LoadFailedException(Printf("Failed to parse id on line %u", line));
} }
catch (const std::exception & exc) size_t metaEnd = idEnd;
if (isUpgrade)
{ {
throw LoadFailedException(Printf("Invalid block ID: \"%s\" (%s)", s[0], exc.what())); metaEnd = findNextSeparator(aTsvPalette, idEnd + 1);
} if ((metaEnd == AString::npos) || (aTsvPalette[metaEnd] != '\t'))
size_t idx = 1;
if (aIsUpgrade)
{
id = id * 16;
try
{ {
id = id + static_cast<UInt32>(Clamp(std::stoi(s[1]), 0, 15)); throw LoadFailedException(Printf("Incomplete data on line %u (meta)", line));
} }
catch (const std::exception & exc) UInt32 meta = 0;
if (!StringToInteger(aTsvPalette.substr(idEnd + 1, metaEnd - idEnd - 1), meta))
{ {
throw LoadFailedException(Printf("Invalid block meta: \"%s\" (%s)", s[1], exc.what())); throw LoadFailedException(Printf("Failed to parse meta on line %u", line));
} }
idx = 2; if (meta > 15)
{
throw LoadFailedException(Printf("Invalid meta value on line %u: %u", line, meta));
}
id = (id * 16) | meta;
} }
const auto & blockTypeName = s[idx]; auto blockTypeEnd = findNextSeparator(aTsvPalette, metaEnd + 1);
idx += 1; if (blockTypeEnd == AString::npos)
std::map<AString, AString> state;
while (idx + 1 < numSplit)
{ {
state[s[idx]] = s[idx + 1]; throw LoadFailedException(Printf("Incomplete data on line %u (blockTypeName)", line));
idx += 2;
} }
addMapping(id, commonPrefix + blockTypeName, state); auto blockTypeName = aTsvPalette.substr(metaEnd + 1, blockTypeEnd - metaEnd - 1);
auto blockStateEnd = blockTypeEnd;
AStringMap blockState;
while (aTsvPalette[blockStateEnd] == '\t')
{
auto keyEnd = findNextSeparator(aTsvPalette, blockStateEnd + 1);
if ((keyEnd == AString::npos) || (aTsvPalette[keyEnd] != '\t'))
{
throw LoadFailedException(Printf("Incomplete data on line %u (blockState key)", line));
}
auto valueEnd = findNextSeparator(aTsvPalette, keyEnd + 1);
if (valueEnd == AString::npos)
{
throw LoadFailedException(Printf("Incomplete data on line %u (blockState value)", line));
}
auto key = aTsvPalette.substr(blockStateEnd + 1, keyEnd - blockStateEnd - 1);
auto value = aTsvPalette.substr(keyEnd + 1, valueEnd - keyEnd - 1);
blockState[key] = value;
blockStateEnd = valueEnd;
}
addMapping(id, commonPrefix + blockTypeName, std::move(blockState));
++line;
if (aTsvPalette[blockStateEnd] == '\r') // CR of the CRLF pair, skip the LF:
{
++blockStateEnd;
}
idx = blockStateEnd;
} }
} }

View File

@ -229,8 +229,8 @@ static void testLoadTsvRegular(void)
auto str = "\ auto str = "\
BlockTypePalette\r\n\ BlockTypePalette\r\n\
FileVersion\t1\n\ FileVersion\t1\n\
CommonPrefix\tminecraft:\r\n\ CommonPrefix\tminecraft:\n\
\n\ \r\n\
0\tair\r\n\ 0\tair\r\n\
1\tstone\n\ 1\tstone\n\
2\tgrass\tsnow_covered\t0\n\ 2\tgrass\tsnow_covered\t0\n\