Source code for ifgen.struct.methods
"""
A module implementing interfaces for generating struct method code.
"""
# built-in
from typing import Any
# third-party
from vcorelib.io import IndentedFileWriter
# internal
from ifgen.generation.interface import GenerateTask
from ifgen.generation.json import to_json_method
from ifgen.struct.methods.common import swap_fields
[docs]
def protocol_json(task: GenerateTask) -> dict[str, Any]:
"""Get JSON data for this struct task."""
protocol = task.protocol()
# use something better for this
return protocol.export_json()
[docs]
def span_method(task: GenerateTask, writer: IndentedFileWriter) -> None:
"""Generate a span method."""
with writer.javadoc():
writer.write(("Get this instance as a byte span."))
span_type = task.cpp_namespace("Span")
method = task.cpp_namespace("span")
writer.write(f"inline {span_type} {method}()")
with writer.scope():
writer.write("return Span(*raw());")
[docs]
def struct_buffer_method(
task: GenerateTask,
writer: IndentedFileWriter,
read_only: bool,
) -> None:
"""Generate a method for raw buffer access."""
with writer.javadoc():
writer.write(
(
"Get this instance as a "
f"{'read-only ' if read_only else ''}"
"fixed-size byte array."
)
)
buff_type = task.cpp_namespace("Buffer")
if read_only:
buff_type = "const " + buff_type
# Returns a pointer.
method = task.cpp_namespace(
"raw()" if not read_only else "raw_ro()", prefix="*"
)
writer.write(
f"inline {buff_type} {method}" + (" const" if read_only else "")
)
with writer.scope():
writer.write(
"return reinterpret_cast"
f"<{'const ' if read_only else ''}Buffer *>(this);"
)
[docs]
def endian_str(task: GenerateTask) -> str:
"""Get the appropriate default endian argument for this struct."""
return (
f"std::endian::{task.instance['default_endianness']}"
if task.instance["default_endianness"]
!= task.env.config.data["struct"]["default_endianness"]
else "default_endian"
)
[docs]
def handle_endian(task: GenerateTask, writer: IndentedFileWriter) -> None:
"""Write a struct method for byte swapping."""
with writer.javadoc():
writer.write("Handle swapping bytes for endian conversion (native).")
writer.empty()
writer.write(
task.command(
"tparam",
"endianness Byte order for encoding elements.",
)
)
writer.write(f"template <std::endian endianness = {endian_str(task)}>")
writer.write("inline void endian(void)")
with writer.indented():
writer.write("requires(endianness == std::endian::native)")
with writer.scope():
pass
writer.empty()
with writer.javadoc():
writer.write(
"Handle swapping bytes for endian conversion (swap required)."
)
writer.empty()
writer.write(
task.command(
"tparam",
"endianness Byte order for encoding elements.",
)
)
writer.write(f"template <std::endian endianness = {endian_str(task)}>")
writer.write("inline void endian(void)")
with writer.indented():
writer.write("requires(endianness != std::endian::native)")
with writer.scope():
swap_fields(task, writer)
[docs]
def encode_endian(task: GenerateTask, writer: IndentedFileWriter) -> None:
"""Write a struct method for encoding buffers."""
with writer.javadoc():
writer.write("Encode this instance to a buffer.")
writer.empty()
writer.write(
task.command(
"tparam",
" endianness Byte order for encoding elements.",
)
)
writer.write(task.command("param[out]", "buffer Buffer to write."))
writer.write(
task.command(
"return", " The number of bytes encoded."
)
)
writer.write(f"template <std::endian endianness = {endian_str(task)}>")
writer.write("inline std::size_t encode(Buffer *buffer) const")
with writer.scope():
writer.write("*buffer = *raw_ro();")
with writer.padding():
writer.write(
f"reinterpret_cast<{task.name} *>"
"(buffer)->endian<endianness>();"
)
writer.write("return size;")
[docs]
def decode_endian(task: GenerateTask, writer: IndentedFileWriter) -> None:
"""Write a struct method for decoding buffers."""
with writer.javadoc():
writer.write("Update this instance from a buffer.")
writer.empty()
writer.write(
task.command(
"tparam",
" endianness Byte order from decoding elements.",
)
)
writer.write(task.command("param[in]", "buffer Buffer to read."))
writer.write(
task.command(
"return", " The number of bytes decoded."
)
)
writer.write(f"template <std::endian endianness = {endian_str(task)}>")
writer.write("inline std::size_t decode(const Buffer *buffer)")
with writer.scope():
writer.write("*raw() = *buffer;")
with writer.padding():
writer.write("endian<endianness>();")
writer.write("return size;")
[docs]
def struct_methods(task: GenerateTask, writer: IndentedFileWriter) -> None:
"""Write generated-struct methods."""
writer.write("using Buffer = byte_array<size>;")
writer.write("using Span = byte_span<size>;")
with writer.padding():
writer.write(f"auto operator<=>(const {task.name} &) const = default;")
struct_buffer_method(task, writer, False)
with writer.padding():
span_method(task, writer)
struct_buffer_method(task, writer, True)
if task.instance["codec"]:
writer.empty()
handle_endian(task, writer)
writer.empty()
encode_endian(task, writer)
writer.empty()
decode_endian(task, writer)
if task.instance["json"]:
to_json_method(
task,
writer,
protocol_json(task),
dumps_indent=task.instance["json_indent"],
task_name=False,
static=True,
definition=True,
)