Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 30 additions & 37 deletions compost_rpc/compost_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -656,7 +656,7 @@ def __init__(self, len_: int, txn: int, resp: bool, rpc_id: int) -> None:

def __repr__(self) -> str:
return f"Header(len={self.len}, txn={self.txn}, rpc_id={hex(self.rpc_id)}, resp={self.resp})"

def payload_byte_len(self) -> int:
"""Returns the length of the payload in bytes."""
return 4 * self.len
Expand Down Expand Up @@ -779,7 +779,7 @@ class Endpoint(Enum):
LOCAL = 1

def is_call_outbound(self, call: Rpc):
return (call.direction == CallDirection.TO_LOCAL and self == Endpoint.REMOTE
return (call.direction == CallDirection.TO_LOCAL and self == Endpoint.REMOTE
or call.direction == CallDirection.TO_REMOTE and self == Endpoint.LOCAL
or call.direction == CallDirection.TWO_WAY)
def is_call_inbound(self, call: Rpc):
Expand Down Expand Up @@ -1286,7 +1286,7 @@ def __init__(self, protocol: type[Protocol]):
# Public properties, common to all generators - with sensible defaults set
self._filename: str = self._protocol.__name__
self._path: Path = Path(sys.path[0])

def _validate_endpoint(self, endpoint: Endpoint):
for rpc in self._protocol._rpcs.values():
if rpc.is_notification:
Expand All @@ -1300,9 +1300,9 @@ def _validate_endpoint(self, endpoint: Endpoint):
elif endpoint.is_call_outbound(rpc) and self.features.outbound_rpc:
continue
msg = f"{self.name} generator cannot generate {self._protocol.__name__} for {'remote' if endpoint == Endpoint.REMOTE else 'local'} endpoint, because "
msg += f"{rpc.name} {'notification' if rpc.is_notification else 'RPC'} has unsupported call direction {rpc.direction.name}."
msg += f"{rpc.name} {'notification' if rpc.is_notification else 'RPC'} has unsupported call direction {rpc.direction.name}."
raise ValueError(msg)

@property
def filename(self) -> str:
"""Filename used for generated source files."""
Expand Down Expand Up @@ -1339,7 +1339,7 @@ def name(cls) -> str:
def short_name(cls) -> str:
"""Short name used for user input"""
...

@property
@abstractmethod
def features(cls) -> CodeGeneratorFeatures:
Expand Down Expand Up @@ -1405,7 +1405,7 @@ def path_source(self) -> Path:
@path_source.setter
def path_source(self, value: Path):
self._path_source = value

_primitive_type_map = {
U8: "uint8_t",
I8: "int8_t",
Expand Down Expand Up @@ -1475,8 +1475,8 @@ def _member_default_value(cls, struct: type, member_annotation: tuple[str, type]
@classmethod
def _handler_signature(cls, rpc: Rpc, as_caller: bool = False) -> _String:
sig = rpc.call_sig
call_site_params = []
prototype_params = []
call_site_params = ["ctx"]
prototype_params = ["struct CompostCtx *ctx"]
parameters_items = rpc.get_param_items()
for name, typ in parameters_items:
prototype_params.append(f"{cls._type(typ.annotation)} {name}")
Expand All @@ -1486,13 +1486,10 @@ def _handler_signature(cls, rpc: Rpc, as_caller: bool = False) -> _String:
elif rpc.is_notification:
raise TypeError("Notification return value must be None")
else:
if _is_type_dynamic(sig.return_annotation):
prototype_params.append("struct CompostAlloc *alloc")
call_site_params.append("&alloc")
return_type = cls._type(sig.return_annotation)
result_assignment = f"{return_type} ret = " if return_type != "void" else ""
call_site = f"{result_assignment}{rpc.name}_handler({', '.join(call_site_params)});"
prototype = f"{return_type} {rpc.name}_handler({', '.join(prototype_params) if prototype_params else 'void'});"
prototype = f"{return_type} {rpc.name}_handler({', '.join(prototype_params)});"
return _String(call_site if as_caller else prototype)

def _define_type_helper(self, t: type, helper: str) -> None:
Expand Down Expand Up @@ -1638,7 +1635,7 @@ def _byte_align(code: _String, offset: MemUnit, ptr: str) -> tuple[_String, MemU
code.add(f"{ptr} += {offset.bytes};")
offset.bytes = 0
return code, offset

def generate(self, endpoint: Endpoint = Endpoint.REMOTE) -> list[tuple[Path, str]]:
self._validate_endpoint(endpoint)
self._define_types()
Expand Down Expand Up @@ -1713,11 +1710,8 @@ def generate(self, endpoint: Endpoint = Endpoint.REMOTE) -> list[tuple[Path, str
sig = rpc.call_sig
parameters_items = rpc.get_param_items()
doc_prefix = "Deserialization/Serialization"
invoke_params = ["struct CompostMsg *tx"]
if rpc.is_notification:
doc_prefix = "Deserialization"
if parameters_items:
invoke_params.append("const struct CompostMsg rx")
invoke_prototype = _String()
invoke_prototype += f"""
/**
Expand All @@ -1730,55 +1724,54 @@ def generate(self, endpoint: Endpoint = Endpoint.REMOTE) -> list[tuple[Path, str
/**
* {doc_prefix} function for {rpc.name} function
*/
void invoke_{rpc.name}({", ".join(invoke_params)})
void invoke_{rpc.name}(struct CompostCtx *ctx)
{{
"""
invoke_fn.indent_inc()
if parameters_items:
invoke_fn.add("const uint8_t *src = rx.payload_buf;")
invoke_fn.add("const uint8_t *src = ctx->rx.payload_buf;")
for name, t in parameters_items:
invoke_fn.add(self._load_call(t.annotation, dest = f"l_{name}", src = "&src"))
if get_origin(sig.return_annotation) is list or _issubclass(sig.return_annotation, (bytes, str)):
invoke_fn.add((
"struct CompostAlloc alloc = compost_alloc_init(tx->payload_buf + 2, tx->payload_buf_size - 2);"
"ctx->alloc = compost_alloc_init(ctx->tx.payload_buf + 2, ctx->tx.payload_buf_size - 2);"
))
elif _issubclass(sig.return_annotation, _CompostStruct):
if sig.return_annotation.dynamic_members:
invoke_fn.add((
f"struct CompostAlloc alloc = {sig.return_annotation.__name__}_alloc_init(tx->payload_buf + {sig.return_annotation.layout[0].bytes}, tx->payload_buf_size - {sig.return_annotation.layout[0].bytes});",
f"ctx->alloc = {sig.return_annotation.__name__}_alloc_init(ctx->tx.payload_buf + {sig.return_annotation.layout[0].bytes}, ctx->tx.payload_buf_size - {sig.return_annotation.layout[0].bytes});",
))
invoke_fn.add(f"{self._handler_signature(rpc, as_caller=True)}")
if sig.return_annotation is not Signature.empty:
invoke_fn.add('uint8_t *dest = tx->payload_buf;')
invoke_fn.add('uint8_t *dest = ctx->tx.payload_buf;')
invoke_fn.add(self._store_call(sig.return_annotation, "ret"))
if sig.return_annotation is Signature.empty:
invoke_fn.add("tx->header.wlen = 0;")
invoke_fn.add("ctx->tx.header.wlen = 0;")
else:
invoke_fn.add("tx->header.wlen = compost_bytes_to_words(dest - tx->payload_buf);")
invoke_fn.add(f"tx->header.rpc_id = {rpc.name.upper()};")
invoke_fn.add("ctx->tx.header.wlen = compost_bytes_to_words(dest - ctx->tx.payload_buf);")
invoke_fn.add(f"ctx->tx.header.rpc_id = {rpc.name.upper()};")
invoke_fn += "}\n"

protocol_header += invoke_prototype
protocol_source += str(invoke_fn)


rpc_id_fns = """
void compost_unknown_msg(struct CompostMsg *tx)
void compost_unknown_msg(struct CompostCtx *ctx)
{
tx->header.rpc_id = UNKNOWN_MSG;
tx->header.wlen = 0;
ctx->tx.header.rpc_id = UNKNOWN_MSG;
ctx->tx.header.wlen = 0;
}

void compost_invoke_switch(struct CompostMsg *tx, const struct CompostMsg rx)
void compost_invoke_switch(struct CompostCtx *ctx)
{
switch (rx.header.rpc_id) {
switch (ctx->rx.header.rpc_id) {
"""
for rpc in self._protocol._rpcs.values():
if rpc.is_notification and not endpoint.is_call_inbound(rpc):
continue
params = "(tx, rx)" if rpc.get_param_items() else "(tx)"
rpc_id_fns += f" case {rpc.name.upper()}: invoke_{rpc.name}{params}; break;\n"
rpc_id_fns += """ default: compost_unknown_msg(tx);
rpc_id_fns += f" case {rpc.name.upper()}: invoke_{rpc.name}(ctx); break;\n"
rpc_id_fns += """ default: compost_unknown_msg(ctx);
}
}
"""
Expand Down Expand Up @@ -1822,7 +1815,7 @@ def name(cls) -> str:
@property
def short_name(cls) -> str:
return "cs"

@property
def features(cls) -> CodeGeneratorFeatures:
"""Features that the generator supports"""
Expand Down Expand Up @@ -2021,11 +2014,11 @@ class _OutputKey:
def as_info_str (self, with_endpoint: bool = True) -> str:
endpoint_str = f" (endpoint: {self.endpoint.name.lower()})" if with_endpoint else ""
return f"{self.generator.name}{endpoint_str}"

def as_typed_str (self, with_endpoint: bool = True) -> str:
endpoint_str = f"_{self.endpoint.name.lower()}" if with_endpoint else ""
return f"{self.generator.short_name}{endpoint_str}"

def __init__(self, protocol: type[Protocol]):
if _issubclass(protocol, Protocol):
self._protocol_class = protocol
Expand Down Expand Up @@ -2120,7 +2113,7 @@ def run(self):
for path in existing:
print(path)
to_overwrite[lang] = existing

langs_typed = [x.as_typed_str(verbose[x.generator.short_name]) for x in to_overwrite.keys()]
overwrite_response = langs_typed
if to_overwrite:
Expand Down
10 changes: 9 additions & 1 deletion compost_rpc/lib/c/header_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,17 +252,25 @@
uint16_t alloc_ctr;
};

struct CompostCtx {
struct CompostMsg rx; //< Received message
struct CompostMsg tx; //< Message to be transmitted
struct CompostAlloc alloc; //< Allocator for the TX buffer
void* user_ctx; //< Pointer passed to compost_msg_process()
};

/**
* @brief Processes incoming Compost message and prepares the response.
* Call this function for every Compost message you receive.
* @param tx_buf Pointer to the transmit buffer where to put an outgoing message
* @param tx_buf_size Transmit buffer size in bytes
* @param rx_buf Pointer to the buffer with incoming message
* @param rx_buf_size Receive buffer valid data size in bytes
* @param user_ctx Pointer that is passed to the handler. Compost doesn't use it.
* @return Size of valid data in bytes in the transmit buffer, or zero if there
* is no message to send, or negative value if there was an error
*/
int compost_msg_process(uint8_t *tx_buf, const size_t tx_buf_size, uint8_t *const rx_buf, const size_t rx_buf_size);
int compost_msg_process(uint8_t *tx_buf, const size_t tx_buf_size, uint8_t *const rx_buf, const size_t rx_buf_size, void *user_ctx);

/**
* @brief Loads the message header from the buffer.
Expand Down
13 changes: 7 additions & 6 deletions compost_rpc/lib/c/source_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class SourcePart:

static void (*compost_assert_func)(uint32_t line) = NULL;

void compost_invoke_switch(struct CompostMsg *tx, const struct CompostMsg rx);
void compost_invoke_switch(struct CompostCtx *ctx);


/******************************************************************************/
Expand Down Expand Up @@ -438,7 +438,7 @@ class SourcePart:
}

int compost_msg_process(uint8_t *tx_buf, const size_t tx_buf_size, uint8_t *const rx_buf,
const size_t rx_buf_size)
const size_t rx_buf_size, void *user_ctx)
{
if ((rx_buf_size < 4) || (tx_buf_size < 4) || tx_buf == NULL || rx_buf == NULL) {
return COMPOST_EINVAL;
Expand All @@ -465,14 +465,15 @@ class SourcePart:
return COMPOST_ETXN; // We don't support sending RPC requests, so we can't get a response
}

compost_invoke_switch(&tx, rx);
struct CompostCtx ctx = { .rx = rx, .tx = tx, .alloc = {0}, .user_ctx = user_ctx };
compost_invoke_switch(&ctx);

if (tx.header.txn || tx.header.wlen) {
ret = compost_header_store(tx_buf, tx.header);
if (ctx.tx.header.txn || ctx.tx.header.wlen) {
ret = compost_header_store(tx_buf, ctx.tx.header);
if (ret) {
return ret;
}
return 4 + (tx.header.wlen * 4); // Total message length is header (4 bytes) + payload
return 4 + (ctx.tx.header.wlen * 4); // Total message length is header (4 bytes) + payload
} else {
return 0; // Nothing to send
}
Expand Down
4 changes: 2 additions & 2 deletions examples/pc_to_pc/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ int main(int argc, char *argv[])

// Process the request and prepare the response
int16_t tx_msg_size =
compost_msg_process(tx_buf, sizeof(tx_buf), rx_buf, COMPOST_MSG_LEN(rx_buf));
compost_msg_process(tx_buf, sizeof(tx_buf), rx_buf, COMPOST_MSG_LEN(rx_buf), NULL);

// If the message size is negative, it indicates an error
if (tx_msg_size > 0) {
Expand All @@ -127,4 +127,4 @@ int main(int argc, char *argv[])
#endif

return 0;
}
}
5 changes: 3 additions & 2 deletions test/mock/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ int main(int argc, char *argv[])
}
size_t msg_len = COMPOST_MSG_LEN(rx_buf);
log_msg(" mock <- ", rx_buf, msg_len);
int16_t tx_len = compost_msg_process(tx_buf, sizeof(tx_buf), rx_buf, msg_len);
int16_t tx_len = compost_msg_process(tx_buf, sizeof(tx_buf), rx_buf, msg_len, NULL);
log_msg(" mock -> ", tx_buf, tx_len);

if (tx_len) {
Expand All @@ -119,7 +119,8 @@ int main(int argc, char *argv[])
switch (notif_request) {
case 0x0e00:
time(&current_epoch);
struct MockDate date = epoch_to_date_handler(current_epoch, &alloc);
struct CompostCtx ctx = {.alloc = alloc};
struct MockDate date = epoch_to_date_handler(&ctx, current_epoch);
tx_len = notify_date_store(tx_buf, date);
break;
case 0x0e02:
Expand Down
Loading