xdrgen: Implement big-endian enums

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
This commit is contained in:
Chuck Lever 2024-09-29 20:50:16 -04:00
parent 6e853dcd2d
commit b376d519bd
11 changed files with 117 additions and 14 deletions

View File

@ -680,6 +680,27 @@ xdr_stream_decode_u32(struct xdr_stream *xdr, __u32 *ptr)
return 0;
}
/**
* xdr_stream_decode_be32 - Decode a big-endian 32-bit integer
* @xdr: pointer to xdr_stream
* @ptr: location to store integer
*
* Return values:
* %0 on success
* %-EBADMSG on XDR buffer overflow
*/
static inline ssize_t
xdr_stream_decode_be32(struct xdr_stream *xdr, __be32 *ptr)
{
const size_t count = sizeof(*ptr);
__be32 *p = xdr_inline_decode(xdr, count);
if (unlikely(!p))
return -EBADMSG;
*ptr = *p;
return 0;
}
/**
* xdr_stream_decode_u64 - Decode a 64-bit integer
* @xdr: pointer to xdr_stream

View File

@ -150,6 +150,23 @@ Pragma directives specify exceptions to the normal generation of
encoding and decoding functions. Currently one directive is
implemented: "public".
Pragma big_endian
------ ----------
pragma big_endian <enum> ;
For variables that might contain only a small number values, it
is more efficient to avoid the byte-swap when encoding or decoding
on little-endian machines. Such is often the case with error status
codes. For example:
pragma big_endian nfsstat3;
In this case, when generating an XDR struct or union containing a
field of type "nfsstat3", xdrgen will make the type of that field
"__be32" instead of "enum nfsstat3". XDR unions then switch on the
non-byte-swapped value of that field.
Pragma exclude
------ -------

View File

@ -4,7 +4,7 @@
"""Generate code to handle XDR enum types"""
from generators import SourceGenerator, create_jinja2_environment
from xdr_ast import _XdrEnum, public_apis
from xdr_ast import _XdrEnum, public_apis, big_endian
class XdrEnumGenerator(SourceGenerator):
@ -30,15 +30,24 @@ class XdrEnumGenerator(SourceGenerator):
for enumerator in node.enumerators:
print(template.render(name=enumerator.name, value=enumerator.value))
template = self.environment.get_template("definition/close.j2")
if node.name in big_endian:
template = self.environment.get_template("definition/close_be.j2")
else:
template = self.environment.get_template("definition/close.j2")
print(template.render(name=node.name))
def emit_decoder(self, node: _XdrEnum) -> None:
"""Emit one decoder function for an XDR enum type"""
template = self.environment.get_template("decoder/enum.j2")
if node.name in big_endian:
template = self.environment.get_template("decoder/enum_be.j2")
else:
template = self.environment.get_template("decoder/enum.j2")
print(template.render(name=node.name))
def emit_encoder(self, node: _XdrEnum) -> None:
"""Emit one encoder function for an XDR enum type"""
template = self.environment.get_template("encoder/enum.j2")
if node.name in big_endian:
template = self.environment.get_template("encoder/enum_be.j2")
else:
template = self.environment.get_template("encoder/enum.j2")
print(template.render(name=node.name))

View File

@ -8,7 +8,7 @@ from jinja2 import Environment
from generators import SourceGenerator
from generators import create_jinja2_environment, get_jinja2_template
from xdr_ast import _XdrBasic, _XdrUnion, _XdrVoid
from xdr_ast import _XdrBasic, _XdrUnion, _XdrVoid, big_endian
from xdr_ast import _XdrDeclaration, _XdrCaseSpec, public_apis
@ -77,13 +77,18 @@ def emit_union_switch_spec_decoder(
print(template.render(name=node.name, type=node.spec.type_name))
def emit_union_case_spec_decoder(environment: Environment, node: _XdrCaseSpec) -> None:
def emit_union_case_spec_decoder(
environment: Environment, node: _XdrCaseSpec, big_endian_discriminant: bool
) -> None:
"""Emit decoder functions for an XDR union's case arm"""
if isinstance(node.arm, _XdrVoid):
return
template = get_jinja2_template(environment, "decoder", "case_spec")
if big_endian_discriminant:
template = get_jinja2_template(environment, "decoder", "case_spec_be")
else:
template = get_jinja2_template(environment, "decoder", "case_spec")
for case in node.values:
print(template.render(case=case))
@ -136,7 +141,11 @@ def emit_union_decoder(environment: Environment, node: _XdrUnion) -> None:
emit_union_switch_spec_decoder(environment, node.discriminant)
for case in node.cases:
emit_union_case_spec_decoder(environment, case)
emit_union_case_spec_decoder(
environment,
case,
node.discriminant.spec.type_name in big_endian,
)
emit_union_default_spec_decoder(environment, node)
@ -153,17 +162,21 @@ def emit_union_switch_spec_encoder(
print(template.render(name=node.name, type=node.spec.type_name))
def emit_union_case_spec_encoder(environment: Environment, node: _XdrCaseSpec) -> None:
def emit_union_case_spec_encoder(
environment: Environment, node: _XdrCaseSpec, big_endian_discriminant: bool
) -> None:
"""Emit encoder functions for an XDR union's case arm"""
if isinstance(node.arm, _XdrVoid):
return
template = get_jinja2_template(environment, "encoder", "case_spec")
if big_endian_discriminant:
template = get_jinja2_template(environment, "encoder", "case_spec_be")
else:
template = get_jinja2_template(environment, "encoder", "case_spec")
for case in node.values:
print(template.render(case=case))
assert isinstance(node.arm, _XdrBasic)
template = get_jinja2_template(environment, "encoder", node.arm.template)
print(
template.render(
@ -192,7 +205,6 @@ def emit_union_default_spec_encoder(environment: Environment, node: _XdrUnion) -
print(template.render())
return
assert isinstance(default_case.arm, _XdrBasic)
template = get_jinja2_template(environment, "encoder", default_case.arm.template)
print(
template.render(
@ -210,7 +222,11 @@ def emit_union_encoder(environment, node: _XdrUnion) -> None:
emit_union_switch_spec_encoder(environment, node.discriminant)
for case in node.cases:
emit_union_case_spec_encoder(environment, case)
emit_union_case_spec_encoder(
environment,
case,
node.discriminant.spec.type_name in big_endian,
)
emit_union_default_spec_encoder(environment, node)

View File

@ -87,12 +87,14 @@ procedure_def : type_specifier identifier "(" type_specifier ")" "=" c
pragma_def : "pragma" directive identifier [ identifier ] ";"
directive : exclude_directive
directive : big_endian_directive
| exclude_directive
| header_directive
| pages_directive
| public_directive
| skip_directive
big_endian_directive : "big_endian"
exclude_directive : "exclude"
header_directive : "header"
pages_directive : "pages"

View File

@ -0,0 +1,14 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* enum {{ name }} (big-endian) */
{% endif %}
{% if name in public_apis %}
bool
{% else %}
static bool __maybe_unused
{% endif %}
xdrgen_decode_{{ name }}(struct xdr_stream *xdr, {{ name }} *ptr)
{
return xdr_stream_decode_be32(xdr, ptr) == 0;
}

View File

@ -0,0 +1,3 @@
{# SPDX-License-Identifier: GPL-2.0 #}
};
typedef __be32 {{ name }};

View File

@ -0,0 +1,14 @@
{# SPDX-License-Identifier: GPL-2.0 #}
{% if annotate %}
/* enum {{ name }} (big-endian) */
{% endif %}
{% if name in public_apis %}
bool
{% else %}
static bool __maybe_unused
{% endif %}
xdrgen_encode_{{ name }}(struct xdr_stream *xdr, {{ name }} value)
{
return xdr_stream_encode_be32(xdr, value) == XDR_UNIT;
}

View File

@ -0,0 +1,2 @@
{# SPDX-License-Identifier: GPL-2.0 #}
case __constant_cpu_to_be32({{ case }}):

View File

@ -0,0 +1,2 @@
{# SPDX-License-Identifier: GPL-2.0 #}
case __constant_cpu_to_be32({{ case }}):

View File

@ -12,6 +12,7 @@ from lark.tree import Meta
this_module = sys.modules[__name__]
big_endian = []
excluded_apis = []
header_name = "none"
public_apis = []
@ -480,6 +481,8 @@ class ParseToAst(Transformer):
"""Instantiate one _Pragma object"""
directive = children[0].children[0].data
match directive:
case "big_endian_directive":
big_endian.append(children[1].symbol)
case "exclude_directive":
excluded_apis.append(children[1].symbol)
case "header_directive":