Codechange: Use std::variant instead of type and union to evaluate Action 14. (#13328)

AllowedSubtags are now passed by span, so no terminator is required, and are now static constexpr.
This commit is contained in:
Peter Nelson 2025-01-17 20:53:19 +00:00 committed by GitHub
parent 2f0b52d5b3
commit 6c88169d7b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -8375,79 +8375,15 @@ typedef bool (*BranchHandler)(ByteReader &); ///< Type of callback functi
* 3. Text leaf nodes (identified by 'T'). * 3. Text leaf nodes (identified by 'T').
*/ */
struct AllowedSubtags { struct AllowedSubtags {
/** Create empty subtags object used to identify the end of a list. */ /** Custom 'span' of subtags. Required because std::span with an incomplete type is UB. */
AllowedSubtags() : using Span = std::pair<const AllowedSubtags *, const AllowedSubtags *>;
id(0),
type(0)
{}
/** uint32_t id; ///< The identifier for this node.
* Create a binary leaf node. std::variant<DataHandler, TextHandler, BranchHandler, Span> handler; ///< The handler for this node.
* @param id The id for this node.
* @param handler The callback function to call.
*/
AllowedSubtags(uint32_t id, DataHandler handler) :
id(id),
type('B')
{
this->handler.data = handler;
}
/**
* Create a text leaf node.
* @param id The id for this node.
* @param handler The callback function to call.
*/
AllowedSubtags(uint32_t id, TextHandler handler) :
id(id),
type('T')
{
this->handler.text = handler;
}
/**
* Create a branch node with a callback handler
* @param id The id for this node.
* @param handler The callback function to call.
*/
AllowedSubtags(uint32_t id, BranchHandler handler) :
id(id),
type('C')
{
this->handler.call_handler = true;
this->handler.u.branch = handler;
}
/**
* Create a branch node with a list of sub-nodes.
* @param id The id for this node.
* @param subtags Array with all valid subtags.
*/
AllowedSubtags(uint32_t id, AllowedSubtags *subtags) :
id(id),
type('C')
{
this->handler.call_handler = false;
this->handler.u.subtags = subtags;
}
uint32_t id; ///< The identifier for this node
uint8_t type; ///< The type of the node, must be one of 'C', 'B' or 'T'.
union {
DataHandler data; ///< Callback function for a binary node, only valid if type == 'B'.
TextHandler text; ///< Callback function for a text node, only valid if type == 'T'.
struct {
union {
BranchHandler branch; ///< Callback function for a branch node, only valid if type == 'C' && call_handler.
AllowedSubtags *subtags; ///< Pointer to a list of subtags, only valid if type == 'C' && !call_handler.
} u;
bool call_handler; ///< True if there is a callback function for this node, false if there is a list of subnodes.
};
} handler;
}; };
static bool SkipUnknownInfo(ByteReader &buf, uint8_t type); static bool SkipUnknownInfo(ByteReader &buf, uint8_t type);
static bool HandleNodes(ByteReader &buf, AllowedSubtags *tags); static bool HandleNodes(ByteReader &buf, std::span<const AllowedSubtags> tags);
/** /**
* Callback function for 'INFO'->'PARA'->param_num->'VALU' to set the names * Callback function for 'INFO'->'PARA'->param_num->'VALU' to set the names
@ -8482,15 +8418,14 @@ static bool ChangeGRFParamValueNames(ByteReader &buf)
} }
/** Action14 parameter tags */ /** Action14 parameter tags */
AllowedSubtags _tags_parameters[] = { static constexpr AllowedSubtags _tags_parameters[] = {
AllowedSubtags('NAME', ChangeGRFParamName), AllowedSubtags{'NAME', ChangeGRFParamName},
AllowedSubtags('DESC', ChangeGRFParamDescription), AllowedSubtags{'DESC', ChangeGRFParamDescription},
AllowedSubtags('TYPE', ChangeGRFParamType), AllowedSubtags{'TYPE', ChangeGRFParamType},
AllowedSubtags('LIMI', ChangeGRFParamLimits), AllowedSubtags{'LIMI', ChangeGRFParamLimits},
AllowedSubtags('MASK', ChangeGRFParamMask), AllowedSubtags{'MASK', ChangeGRFParamMask},
AllowedSubtags('VALU', ChangeGRFParamValueNames), AllowedSubtags{'VALU', ChangeGRFParamValueNames},
AllowedSubtags('DFLT', ChangeGRFParamDefault), AllowedSubtags{'DFLT', ChangeGRFParamDefault},
AllowedSubtags()
}; };
/** /**
@ -8526,23 +8461,21 @@ static bool HandleParameterInfo(ByteReader &buf)
} }
/** Action14 tags for the INFO node */ /** Action14 tags for the INFO node */
AllowedSubtags _tags_info[] = { static constexpr AllowedSubtags _tags_info[] = {
AllowedSubtags('NAME', ChangeGRFName), AllowedSubtags{'NAME', ChangeGRFName},
AllowedSubtags('DESC', ChangeGRFDescription), AllowedSubtags{'DESC', ChangeGRFDescription},
AllowedSubtags('URL_', ChangeGRFURL), AllowedSubtags{'URL_', ChangeGRFURL},
AllowedSubtags('NPAR', ChangeGRFNumUsedParams), AllowedSubtags{'NPAR', ChangeGRFNumUsedParams},
AllowedSubtags('PALS', ChangeGRFPalette), AllowedSubtags{'PALS', ChangeGRFPalette},
AllowedSubtags('BLTR', ChangeGRFBlitter), AllowedSubtags{'BLTR', ChangeGRFBlitter},
AllowedSubtags('VRSN', ChangeGRFVersion), AllowedSubtags{'VRSN', ChangeGRFVersion},
AllowedSubtags('MINV', ChangeGRFMinVersion), AllowedSubtags{'MINV', ChangeGRFMinVersion},
AllowedSubtags('PARA', HandleParameterInfo), AllowedSubtags{'PARA', HandleParameterInfo},
AllowedSubtags()
}; };
/** Action14 root tags */ /** Action14 root tags */
AllowedSubtags _tags_root[] = { static constexpr AllowedSubtags _tags_root[] = {
AllowedSubtags('INFO', _tags_info), AllowedSubtags{'INFO', std::make_pair(std::begin(_tags_info), std::end(_tags_info))},
AllowedSubtags()
}; };
@ -8592,34 +8525,49 @@ static bool SkipUnknownInfo(ByteReader &buf, uint8_t type)
* @param subtags Allowed subtags. * @param subtags Allowed subtags.
* @return Whether all tags could be handled. * @return Whether all tags could be handled.
*/ */
static bool HandleNode(uint8_t type, uint32_t id, ByteReader &buf, AllowedSubtags subtags[]) static bool HandleNode(uint8_t type, uint32_t id, ByteReader &buf, std::span<const AllowedSubtags> subtags)
{ {
uint i = 0; /* Visitor to get a subtag handler's type. */
AllowedSubtags *tag; struct type_visitor {
while ((tag = &subtags[i++])->type != 0) { char operator()(const DataHandler &) { return 'B'; }
if (tag->id != BSWAP32(id) || tag->type != type) continue; char operator()(const TextHandler &) { return 'T'; }
switch (type) { char operator()(const BranchHandler &) { return 'C'; }
default: NOT_REACHED(); char operator()(const AllowedSubtags::Span &) { return 'C'; }
};
case 'T': { /* Visitor to evaluate a subtag handler. */
uint8_t langid = buf.ReadByte(); struct evaluate_visitor {
return tag->handler.text(langid, buf.ReadString()); ByteReader &buf;
}
case 'B': { bool operator()(const DataHandler &handler)
{
size_t len = buf.ReadWord(); size_t len = buf.ReadWord();
if (buf.Remaining() < len) return false; if (buf.Remaining() < len) return false;
return tag->handler.data(len, buf); return handler(len, buf);
} }
case 'C': { bool operator()(const TextHandler &handler)
if (tag->handler.call_handler) { {
return tag->handler.u.branch(buf); uint8_t langid = buf.ReadByte();
return handler(langid, buf.ReadString());
} }
return HandleNodes(buf, tag->handler.u.subtags);
bool operator()(const BranchHandler &handler)
{
return handler(buf);
} }
bool operator()(const AllowedSubtags::Span &subtags)
{
return HandleNodes(buf, {subtags.first, subtags.second});
} }
};
for (const auto &tag : subtags) {
if (tag.id != BSWAP32(id) || std::visit(type_visitor{}, tag.handler) != type) continue;
return std::visit(evaluate_visitor{buf}, tag.handler);
} }
GrfMsg(2, "StaticGRFInfo: unknown type/id combination found, type={:c}, id={:x}", type, id); GrfMsg(2, "StaticGRFInfo: unknown type/id combination found, type={:c}, id={:x}", type, id);
return SkipUnknownInfo(buf, type); return SkipUnknownInfo(buf, type);
} }
@ -8630,7 +8578,7 @@ static bool HandleNode(uint8_t type, uint32_t id, ByteReader &buf, AllowedSubtag
* @param subtags List of subtags. * @param subtags List of subtags.
* @return Whether the nodes could all be handled. * @return Whether the nodes could all be handled.
*/ */
static bool HandleNodes(ByteReader &buf, AllowedSubtags subtags[]) static bool HandleNodes(ByteReader &buf, std::span<const AllowedSubtags> subtags)
{ {
uint8_t type = buf.ReadByte(); uint8_t type = buf.ReadByte();
while (type != 0) { while (type != 0) {