ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
jof_worker.c
Go to the documentation of this file.
1
16
17#include "jof_worker.h"
18
19#include <errno.h>
20#include <fcntl.h>
21#include <stdlib.h>
22#include <string.h>
23#include <sys/stat.h>
24#include <unistd.h>
25
26#include "jof_produce.h"
27#include "ra8_attributes.h"
28
30typedef enum : uint64_t {
31 k_worker_max_input = 256U * 1024U * 1024U,
35
43
45static const uint8_t s_worker_riff[k_worker_webp_tag_len] = {'R', 'I', 'F', 'F'};
46
48static const uint8_t s_worker_webp[k_worker_webp_tag_len] = {'W', 'E', 'B', 'P'};
49
51typedef struct {
52 const uint8_t* data;
53 size_t len;
54 size_t pos;
56
58typedef struct {
59 int fd;
60 bool failed;
62
81static bool worker_read_all(int fd, uint8_t* buf, size_t len)
82{
83 size_t done = 0U;
84 while (done < len) {
85 const ssize_t got = read(fd, buf + done, len - done);
86 if (got == 0) {
87 return false;
88 }
89 if (got < 0) {
90 if (errno == EINTR) {
91 continue;
92 }
93 return false;
94 }
95 done += (size_t)got;
96 }
97 return true;
98}
99
119static ra8_err_t worker_sink(void* ctx, const uint8_t* buf, size_t len)
120{
121 worker_sink_t* s = (worker_sink_t*)ctx;
122 size_t done = 0U;
123 while (done < len) {
124 const ssize_t wrote = write(s->fd, buf + done, len - done);
125 if (wrote == 0) {
126 s->failed = true;
127 return k_ra8_fail;
128 }
129 if (wrote < 0) {
130 if (errno == EINTR) {
131 continue;
132 }
133 s->failed = true;
134 return k_ra8_fail;
135 }
136 done += (size_t)wrote;
137 }
138 return k_ra8_ok;
139}
140
159static ra8_err_t worker_pull(void* ctx, uint8_t* buf, size_t cap, size_t* got)
160{
161 worker_pull_t* s = (worker_pull_t*)ctx;
162 size_t n = s->len - s->pos;
163 if (n > cap) {
164 n = cap;
165 }
166 memcpy(buf, s->data + s->pos, n);
167 s->pos += n;
168 *got = n;
169 return k_ra8_ok;
170}
171
189static bool worker_is_webp(const uint8_t* data, size_t len)
190{
191 if (len < (size_t)k_worker_webp_head_len) {
192 return false;
193 }
194 return (memcmp(&data[k_worker_webp_riff_ofs], s_worker_riff, sizeof(s_worker_riff)) == 0) &&
196}
197
218{
219 switch (err) {
223 case k_ra8_err_no_mem:
224 return k_jof_worker_memory;
225 default:
226 return k_jof_worker_decode;
227 }
228}
229
251worker_read_source(const char* in_path, uint8_t** out_src, size_t* out_len)
252{
253 const int in_fd = open(in_path, O_RDONLY);
254 if (in_fd < 0) {
255 return k_jof_worker_input;
256 }
257 struct stat st;
258 if (fstat(in_fd, &st) != 0) {
259 (void)close(in_fd);
260 return k_jof_worker_input;
261 }
262 if ((st.st_size <= 0) || ((uint64_t)st.st_size > (uint64_t)k_worker_max_input)) {
263 (void)close(in_fd);
264 return k_jof_worker_input;
265 }
266 const size_t src_len = (size_t)st.st_size;
267 uint8_t* src = (uint8_t*)malloc(src_len); /* alloc-allow: bounded probe-sized source image */
268 if (src == nullptr) {
269 (void)close(in_fd);
270 return k_jof_worker_memory;
271 }
272 if (!worker_read_all(in_fd, src, src_len)) {
273 free(src); /* alloc-allow: bounded probe-sized source image */
274 (void)close(in_fd);
275 return k_jof_worker_input;
276 }
277 (void)close(in_fd);
278 *out_src = src;
279 *out_len = src_len;
280 return k_jof_worker_ok;
281}
282
306static jof_worker_result_t worker_alloc_webp(const uint8_t* src,
307 size_t src_len,
308 uint16_t w,
309 uint16_t h,
310 uint8_t** out_webp_work,
311 size_t* out_webp_cap)
312{
313 *out_webp_work = nullptr;
314 *out_webp_cap = 0U;
315 if (!worker_is_webp(src, src_len)) {
316 return k_jof_worker_ok;
317 }
318 if (src_len > UINT32_MAX) {
320 }
321 const uint32_t webp_need = jof_webp_work_bytes(w, h, (uint32_t)src_len);
322 if (webp_need == 0U) {
324 }
325 uint8_t* webp_work =
326 (uint8_t*)malloc(webp_need); /* alloc-allow: exact jof_webp_work_bytes arena */
327 if (webp_work == nullptr) {
328 return k_jof_worker_memory;
329 }
330 *out_webp_work = webp_work;
331 *out_webp_cap = (size_t)webp_need;
332 return k_jof_worker_ok;
333}
334
364static jof_worker_result_t worker_produce_to_file(const char* out_path,
365 const uint8_t* src,
366 size_t src_len,
367 uint16_t w,
368 uint16_t h,
369 uint16_t tile_h,
370 uint8_t* work,
371 size_t work_cap,
372 uint8_t* webp_work,
373 size_t webp_work_cap)
374{
375 const int out_fd = open(out_path, O_WRONLY | O_CREAT | O_TRUNC, (mode_t)k_worker_out_mode);
376 if (out_fd < 0) {
377 return k_jof_worker_output;
378 }
379 worker_pull_t pull = {.data = src, .len = src_len, .pos = 0U};
380 worker_sink_t sink = {.fd = out_fd, .failed = false};
381 const jof_produce_cfg_t cfg = {
382 .pull = worker_pull,
383 .pull_ctx = &pull,
384 .sink = worker_sink,
385 .sink_ctx = &sink,
386 .tile_w = w,
387 .tile_h = tile_h,
388 .codec = (uint8_t)k_jof_codec_deflate,
389 .max_width = w,
390 .max_height = h,
391 .work = work,
392 .work_cap = work_cap,
393 .webp_work = webp_work,
394 .webp_work_cap = webp_work_cap,
395 };
396 jof_info_t info = {};
397 const ra8_err_t produce_rc = jof_produce(&cfg, &info);
398 const int close_rc = close(out_fd);
399
400 if ((close_rc != 0) || sink.failed) {
401 return k_jof_worker_output;
402 }
403 if (produce_rc != k_ra8_ok) {
404 return worker_map_producer(produce_rc);
405 }
406 return k_jof_worker_ok;
407}
408
409RA8_NASA_RULE_3_OK("host-only single-image transcode: three bounded arenas sized by probe")
410jof_worker_result_t jof_worker_convert(const char* in_path, const char* out_path)
411{
412 if ((in_path == nullptr) || (out_path == nullptr)) {
413 return k_jof_worker_usage;
414 }
415 uint8_t* src = nullptr;
416 size_t src_len = 0U;
417 jof_worker_result_t rc = worker_read_source(in_path, &src, &src_len);
418 if (rc != k_jof_worker_ok) {
419 return rc;
420 }
421 uint16_t w = 0U;
422 uint16_t h = 0U;
423 if (jof_probe_dims(src, src_len, &w, &h) != k_ra8_ok) {
424 free(src); /* alloc-allow: bounded probe-sized source image */
426 }
427 const uint16_t tile_h = (h > (uint16_t)k_worker_tile_h) ? (uint16_t)k_worker_tile_h : h;
428 const uint32_t work_cap = jof_work_bytes(w, h, w, tile_h);
429 if (work_cap == 0U) {
430 free(src); /* alloc-allow: bounded probe-sized source image */
432 }
433 uint8_t* work = (uint8_t*)malloc(work_cap); /* alloc-allow: exact jof_work_bytes arena */
434 if (work == nullptr) {
435 free(src); /* alloc-allow: bounded probe-sized source image */
436 return k_jof_worker_memory;
437 }
438 uint8_t* webp_work = nullptr;
439 size_t webp_work_cap = 0U;
440 rc = worker_alloc_webp(src, src_len, w, h, &webp_work, &webp_work_cap);
441 if (rc == k_jof_worker_ok) {
442 rc = worker_produce_to_file(out_path,
443 src,
444 src_len,
445 w,
446 h,
447 tile_h,
448 work,
449 (size_t)work_cap,
450 webp_work,
451 webp_work_cap);
452 }
453 free(webp_work); /* alloc-allow: exact jof_webp_work_bytes arena */
454 free(work); /* alloc-allow: exact jof_work_bytes arena */
455 free(src); /* alloc-allow: bounded probe-sized source image */
456 if (rc != k_jof_worker_ok) {
457 (void)unlink(out_path);
458 }
459 return rc;
460}
@ k_jof_codec_deflate
Tile stream is one raw-DEFLATE run.
Definition jof.h:192
Import-time transcode producer: JPEG/PNG/WebP -> JOF band-tile atlas in bounded RAM (#231,...
ra8_err_t jof_probe_dims(const uint8_t *data, size_t len, uint16_t *out_w, uint16_t *out_h)
Read a source image's pixel dimensions without decoding its body.
uint32_t jof_webp_work_bytes(uint16_t max_width, uint16_t max_height, uint32_t max_src_bytes)
Compute the whole-frame webp_work arena a WebP source needs (#290).
uint32_t jof_work_bytes(uint16_t max_width, uint16_t max_height, uint16_t tile_w, uint16_t tile_h)
Compute the work-arena size the producer needs for given caps.
ra8_err_t jof_produce(const jof_produce_cfg_t *cfg, jof_info_t *out_info)
Transcode one encoded JPEG/PNG/WebP source into a JOF atlas (#231, #290).
static const uint8_t s_worker_webp[k_worker_webp_tag_len]
WebP form-type fourCC (source head bytes 8..11).
Definition jof_worker.c:48
static bool worker_is_webp(const uint8_t *data, size_t len)
Test whether source bytes carry the WebP RIFF container head.
Definition jof_worker.c:189
static jof_worker_result_t worker_read_source(const char *in_path, uint8_t **out_src, size_t *out_len)
Read encoded source file into an allocated RAM buffer.
Definition jof_worker.c:251
static jof_worker_result_t worker_alloc_webp(const uint8_t *src, size_t src_len, uint16_t w, uint16_t h, uint8_t **out_webp_work, size_t *out_webp_cap)
Allocate the WebP frame arena if the source format requires one.
Definition jof_worker.c:306
worker_limit_t
Named resource limits for the worker.
Definition jof_worker.c:30
@ k_worker_out_mode
File mode: rw-r–r–.
Definition jof_worker.c:33
@ k_worker_tile_h
Band-tile height cap, pixels.
Definition jof_worker.c:32
@ k_worker_max_input
Largest accepted input, bytes.
Definition jof_worker.c:31
static bool worker_read_all(int fd, uint8_t *buf, size_t len)
Read exactly len bytes unless the descriptor fails or ends early.
Definition jof_worker.c:81
worker_webp_t
WebP container-head offsets for the whole-frame-arena decision.
Definition jof_worker.c:37
@ k_worker_webp_form_ofs
Offset of the "WEBP" fourCC.
Definition jof_worker.c:39
@ k_worker_webp_head_len
Bytes needed to sniff both tags.
Definition jof_worker.c:40
@ k_worker_webp_riff_ofs
Offset of the "RIFF" fourCC.
Definition jof_worker.c:38
@ k_worker_webp_tag_len
Length of one fourCC tag.
Definition jof_worker.c:41
static ra8_err_t worker_pull(void *ctx, uint8_t *buf, size_t cap, size_t *got)
Copy the next encoded source span into a producer buffer.
Definition jof_worker.c:159
static jof_worker_result_t worker_produce_to_file(const char *out_path, const uint8_t *src, size_t src_len, uint16_t w, uint16_t h, uint16_t tile_h, uint8_t *work, size_t work_cap, uint8_t *webp_work, size_t webp_work_cap)
Stream JOF producer output into the destination atlas file.
Definition jof_worker.c:364
static ra8_err_t worker_sink(void *ctx, const uint8_t *buf, size_t len)
Append producer output bytes to the output descriptor.
Definition jof_worker.c:119
static const uint8_t s_worker_riff[k_worker_webp_tag_len]
WebP RIFF container tag (source head bytes 0..3).
Definition jof_worker.c:45
static jof_worker_result_t worker_map_producer(ra8_err_t err)
Map a producer return code to the worker result contract.
Definition jof_worker.c:217
Single-image to JOF worker: the per-page transcode the cbz2jof Go driver shells out to.
jof_worker_result_t
Process exit code contract between the worker and its Go driver.
Definition jof_worker.h:33
@ k_jof_worker_ok
Atlas fully written to the output path.
Definition jof_worker.h:34
@ k_jof_worker_output
Output open/write/close failed.
Definition jof_worker.h:37
@ k_jof_worker_geometry
Source dimensions unusable or unproduceable.
Definition jof_worker.h:38
@ k_jof_worker_input
Input open/stat/read failed or was refused.
Definition jof_worker.h:36
@ k_jof_worker_usage
Wrong argument count or null path.
Definition jof_worker.h:35
@ k_jof_worker_decode
Source is hostile or an unsupported variant.
Definition jof_worker.h:39
@ k_jof_worker_memory
A bounded work arena could not be allocated.
Definition jof_worker.h:40
jof_worker_result_t jof_worker_convert(const char *in_path, const char *out_path)
Transcode one encoded image file into one JOF atlas file.
Definition jof_worker.c:410
-proof
Annotation-attribute framework macros for ra8-firmware.
#define RA8_NASA_RULE_3_OK(reason)
Documented exception to NASA Power-of-10 Rule 3 (no dynamic alloc).
@ k_ra8_err_no_mem
Static buffer exhausted (no dynamic memory on this project).
Definition ra8_err.h:142
@ k_ra8_fail
Generic unspecified failure.
Definition ra8_err.h:133
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ 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
int memcmp(const void *a, const void *b, size_t n)
Compare bytes in two memory areas.
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
Parsed + validated geometry of one JOF atlas.
Definition jof.h:208
Producer configuration: source, sink, tile geometry and work arena.
Pull cursor over the in-RAM encoded source.
Definition jof_worker.c:51
size_t pos
Read cursor.
Definition jof_worker.c:54
size_t len
Total length.
Definition jof_worker.c:53
const uint8_t * data
Encoded bytes.
Definition jof_worker.c:52
Sink state over the raw output descriptor.
Definition jof_worker.c:58
int fd
Open output descriptor.
Definition jof_worker.c:59
bool failed
Latched true on any write failure.
Definition jof_worker.c:60