ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_fmt_portable_convert.c
Go to the documentation of this file.
1
11
12#include <stddef.h>
13#include <stdint.h>
14#include <string.h>
15#include <unistd.h>
16
17#include "ra8_attributes.h"
20#include "ra8_fmt_stream.h"
21
32
34typedef struct {
35 const char* input;
36 const char* output;
37 const char* format;
39
55static ra8_err_t internal_text(const ra8_fmt_sink_t* sink, const char* text)
56{
57 return sink->write(sink->ctx, (const uint8_t*)text, strlen(text));
58}
59
75static ra8_err_t internal_u64(const ra8_fmt_sink_t* sink, uint64_t value)
76{
77 char reverse[k_convert_cli_decimal];
78 size_t count = 0U;
79 do {
80 reverse[count++] = (char)('0' + (char)(value % k_convert_cli_radix));
81 value /= k_convert_cli_radix;
82 } while (value != 0U);
83 char text[k_convert_cli_decimal];
84 for (size_t i = 0U; i < count; ++i) {
85 text[i] = reverse[count - i - 1U];
86 }
87 return sink->write(sink->ctx, (const uint8_t*)text, count);
88}
89
105static void
106internal_field(const ra8_fmt_sink_t* sink, uint64_t value, const char* suffix, ra8_err_t* status)
107{
108 if (*status == k_ra8_ok) {
109 *status = internal_u64(sink, value);
110 }
111 if (*status == k_ra8_ok) {
112 *status = internal_text(sink, suffix);
113 }
114}
115
130static void internal_status(const ra8_fmt_sink_t* errors, const char* prefix, ra8_err_t status)
131{
132 ra8_err_t rc = internal_text(errors, prefix);
133 if (rc == k_ra8_ok) {
134 rc = internal_u64(errors, (uint64_t)status);
135 }
136 if (rc == k_ra8_ok) {
137 (void)internal_text(errors, ")\n");
138 }
139}
140
158static bool internal_parse(int argc, char** argv, convert_cli_args_t* args)
159{
160 for (int i = 2; i < argc; ++i) {
161 if ((strcmp(argv[i], "--format") == 0) && ((i + 1) < argc)) {
162 args->format = argv[++i];
163 } else if ((strcmp(argv[i], "--in") == 0) && ((i + 1) < argc)) {
164 args->input = argv[++i];
165 } else if ((strcmp(argv[i], "--out") == 0) && ((i + 1) < argc)) {
166 args->output = argv[++i];
167 } else if ((strcmp(argv[i], "--verbose") == 0) || (strcmp(argv[i], "-v") == 0)) {
168 continue;
169 } else if ((argv[i][0] != '-') && (args->input == nullptr)) {
170 args->input = argv[i];
171 } else {
172 return false;
173 }
174 }
175 return true;
176}
177
194static bool internal_align(size_t value, size_t* aligned)
195{
196 const size_t mask = (size_t)k_convert_cli_align - 1U;
197 if (value > (SIZE_MAX - mask)) {
198 return false;
199 }
200 *aligned = (value + mask) & ~mask;
201 return true;
202}
203
222 size_t* webp_offset,
223 size_t* total)
224{
225 if (!internal_align(requirements->work_bytes, webp_offset)) {
226 return false;
227 }
228 if ((size_t)requirements->webp_work_bytes > (SIZE_MAX - *webp_offset)) {
229 return false;
230 }
231 *total = *webp_offset + requirements->webp_work_bytes;
232 return true;
233}
234
250static void internal_capacity(const ra8_fmt_sink_t* errors,
251 const ra8_fmt_jof_convert_requirements_t* requirements,
252 size_t required,
253 size_t supplied)
254{
255 ra8_err_t rc = internal_text(errors, "ra8_fmt: JOF convert workspace too small: required ");
256 internal_field(errors, required, " supplied ", &rc);
257 internal_field(errors, supplied, " (work ", &rc);
258 internal_field(errors, requirements->work_bytes, ", webp ", &rc);
259 internal_field(errors, requirements->webp_work_bytes, ")\n", &rc);
260}
261
287 size_t arena_cap,
288 const ra8_fmt_sink_t* errors,
290 size_t* webp_offset,
291 size_t* high_water,
292 bool* sized)
293{
294 ra8_err_t rc = ra8_fmt_jof_convert_requirements(source, requirements);
295 *sized = (rc == k_ra8_ok) && internal_high_water(requirements, webp_offset, high_water);
296 if ((rc == k_ra8_ok) && (!*sized || (*high_water > arena_cap))) {
297 internal_capacity(errors, requirements, *sized ? *high_water : SIZE_MAX, arena_cap);
299 }
300 return rc;
301}
302
324 const char* output,
325 const ra8_fmt_jof_convert_requirements_t* requirements,
326 uint8_t* arena,
327 size_t webp_offset,
328 const ra8_fmt_sink_t* report)
329{
330 ra8_fmt_host_transaction_t host_transaction;
331 ra8_fmt_transaction_t transaction;
332 ra8_err_t rc = priv_fmt_host_transaction_begin(output, &host_transaction, &transaction);
333 if (rc != k_ra8_ok) {
334 return rc;
335 }
337 .work = arena,
338 .work_cap = requirements->work_bytes,
339 .webp_work = (requirements->webp_work_bytes == 0U) ? nullptr : &arena[webp_offset],
340 .webp_work_cap = requirements->webp_work_bytes,
341 };
342 rc = ra8_fmt_jof_convert_stream(source, requirements, &workspace, &transaction, report, output);
343 return rc;
344}
345
364 ra8_err_t rc,
365 bool sized,
366 size_t high_water,
367 size_t arena_cap)
368{
369 if (rc == k_ra8_ok) {
370 return;
371 }
372 if (sized && (high_water <= arena_cap)) {
373 internal_status(errors, "ra8_fmt: JOF convert failed (rc=", rc);
374 } else if (!sized) {
375 internal_status(errors, "ra8_fmt: cannot size JOF convert (rc=", rc);
376 }
377}
378
380 char** argv,
381 uint8_t* arena,
382 size_t arena_cap,
383 bool* handled)
384{
385 if ((handled == nullptr) || (arena == nullptr)) {
386 return (int)k_convert_cli_fail;
387 }
388 *handled = false;
389 if ((argc < 2) || (strcmp(argv[1], "convert") != 0)) {
390 return (int)k_convert_cli_ok;
391 }
392 convert_cli_args_t args = {};
393 if (!internal_parse(argc, argv, &args) || (args.format == nullptr) ||
394 (strcmp(args.format, "jof") != 0)) {
395 return (int)k_convert_cli_ok;
396 }
397 *handled = true;
398 ra8_fmt_host_fd_sink_t error_state = {.fd = STDERR_FILENO};
399 const ra8_fmt_sink_t errors = priv_fmt_host_fd_sink(&error_state);
400 if ((args.input == nullptr) || (args.output == nullptr)) {
401 (void)internal_text(&errors, "ra8_fmt: JOF convert needs --in and --out\n");
402 return (int)k_convert_cli_usage;
403 }
404 ra8_fmt_host_source_t source = {.fd = -1};
406 if (rc != k_ra8_ok) {
407 internal_status(&errors, "ra8_fmt: cannot open convert input (rc=", rc);
408 return (int)k_convert_cli_fail;
409 }
410 ra8_fmt_jof_convert_requirements_t requirements = {};
411 size_t webp_offset = 0U;
412 size_t high_water = 0U;
413 bool sized = false;
414 rc = internal_size_input(&source.source,
415 arena_cap,
416 &errors,
417 &requirements,
418 &webp_offset,
419 &high_water,
420 &sized);
421 ra8_fmt_host_fd_sink_t output_state = {.fd = STDOUT_FILENO};
422 const ra8_fmt_sink_t report = priv_fmt_host_fd_sink(&output_state);
423 if (rc == k_ra8_ok) {
424 rc = internal_run(&source.source, args.output, &requirements, arena, webp_offset, &report);
425 }
427 internal_report_run_failure(&errors, rc, sized, high_water, arena_cap);
428 return (rc == k_ra8_ok) ? (int)k_convert_cli_ok : (int)k_convert_cli_fail;
429}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_PRIV
Module-private helper: shared across TUs but only inside one library.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_invalid_size
Invalid size parameter (too large, too small, or misaligned).
Definition ra8_err.h:167
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
Raw file-descriptor adapters for the portable format-tool contracts.
ra8_err_t priv_fmt_host_transaction_begin(const char *path, ra8_fmt_host_transaction_t *state, ra8_fmt_transaction_t *out)
Begin a sibling-temp durable replacement transaction.
ra8_err_t priv_fmt_host_source_open(const char *path, uint64_t max_size, ra8_fmt_host_source_t *out)
Open a bounded, regular, non-symlink input object.
ra8_fmt_sink_t priv_fmt_host_fd_sink(ra8_fmt_host_fd_sink_t *state)
Obtain the exact-write portable sink for a raw descriptor.
void priv_fmt_host_source_close(ra8_fmt_host_source_t *source)
Close an open host source; safe after failed open.
static bool internal_high_water(const ra8_fmt_jof_convert_requirements_t *requirements, size_t *webp_offset, size_t *total)
Compute the exact shared-arena high-water for one conversion.
static void internal_report_run_failure(const ra8_fmt_sink_t *errors, ra8_err_t rc, bool sized, size_t high_water, size_t arena_cap)
Emit the one diagnostic matching how a failed conversion failed.
convert_cli_const_t
CLI status, input, and decimal formatting bounds.
@ k_convert_cli_input
Maximum source file (256 MiB).
@ k_convert_cli_decimal
Digits in uint64_t.
@ k_convert_cli_ok
Successful conversion.
@ k_convert_cli_align
Producer-arena slice alignment.
@ k_convert_cli_usage
Invalid command line.
@ k_convert_cli_fail
Command execution failed.
@ k_convert_cli_radix
Diagnostic number radix.
static void internal_status(const ra8_fmt_sink_t *errors, const char *prefix, ra8_err_t status)
Emit one canonical status diagnostic through fixed local formatting.
static ra8_err_t internal_text(const ra8_fmt_sink_t *sink, const char *text)
Append one NUL-terminated diagnostic fragment.
int priv_fmt_try_portable_convert(int argc, char **argv, uint8_t *arena, size_t arena_cap, bool *handled)
Try the caller-workspace JOF-convert command path.
static bool internal_parse(int argc, char **argv, convert_cli_args_t *args)
Parse the options accepted by the legacy convert command.
static ra8_err_t internal_size_input(const ra8_fmt_source_t *source, size_t arena_cap, const ra8_fmt_sink_t *errors, ra8_fmt_jof_convert_requirements_t *requirements, size_t *webp_offset, size_t *high_water, bool *sized)
Probe exact producer requirements and confirm the caller arena fits.
static ra8_err_t internal_run(const ra8_fmt_source_t *source, const char *output, const ra8_fmt_jof_convert_requirements_t *requirements, uint8_t *arena, size_t webp_offset, const ra8_fmt_sink_t *report)
Bind exact work slices and execute one already-open conversion.
static void internal_field(const ra8_fmt_sink_t *sink, uint64_t value, const char *suffix, ra8_err_t *status)
Append one numeric field and suffix while status remains successful.
static void internal_capacity(const ra8_fmt_sink_t *errors, const ra8_fmt_jof_convert_requirements_t *requirements, size_t required, size_t supplied)
Emit exact required/supplied workspace evidence.
static bool internal_align(size_t value, size_t *aligned)
Round one byte count up to the composition arena alignment.
static ra8_err_t internal_u64(const ra8_fmt_sink_t *sink, uint64_t value)
Append one unsigned decimal diagnostic field.
Caller-workspace CLI composition for every supported tool verb.
Caller-workspace I/O contracts for portable format-tool engines.
ra8_err_t ra8_fmt_jof_convert_requirements(const ra8_fmt_source_t *source, ra8_fmt_jof_convert_requirements_t *out)
Derive source geometry and exact JOF conversion workspace needs.
ra8_err_t ra8_fmt_jof_convert_stream(const ra8_fmt_source_t *source, const ra8_fmt_jof_convert_requirements_t *requirements, const ra8_fmt_jof_convert_workspace_t *workspace, ra8_fmt_transaction_t *transaction, const ra8_fmt_sink_t *report, const char *output_name)
Stream one encoded image into a durably published JOF artifact.
int strcmp(const char *s1, const char *s2)
Compare two null-terminated strings.
size_t strlen(const char *s)
Calculate string length.
Parsed arguments accepted by the portable convert composition.
const char * output
Destination JOF path.
const char * input
Source image path.
const char * format
Explicit format selector.
Append sink backed by a caller-owned descriptor.
Open raw-fd source and its portable view.
ra8_fmt_source_t source
Portable positioned-read view.
Caller-owned state for one sibling-file transaction.
Source geometry and exact arenas required by one JOF conversion.
uint32_t webp_work_bytes
Exact WebP whole-frame bytes, or zero.
uint32_t work_bytes
Exact streaming-producer arena bytes.
Caller-owned arenas supplied to the streaming JOF converter.
Injected append-only sink.
ra8_fmt_sink_write_fn write
Exact append callback.
void * ctx
Backend-owned context.
Immutable, randomly readable input object.
One caller-owned artifact transaction.