ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
jof_audit.c
Go to the documentation of this file.
1
19
20#include "jof_audit.h"
21
22#include <stddef.h>
23#include <stdint.h>
24#include <string.h>
25
26#include "ra8_attributes.h"
27
29typedef enum : uint32_t {
30 k_jof_audit_fnv_basis = 2166136261U,
34
36typedef struct internal_span_t {
37 uintptr_t begin;
38 uintptr_t end;
40
60internal_read_exact(jof_pread_fn pread, void* ctx, uint64_t offset, uint8_t* buf, size_t len)
61{
62 size_t got = 0U;
63 const ra8_err_t rc = pread(ctx, offset, buf, len, &got);
64 if (rc != k_ra8_ok) {
65 return rc;
66 }
67 return (got == len) ? k_ra8_ok : k_ra8_err_validation_failed;
68}
69
83RA8_INTERNAL static uint32_t internal_rd_u32(const uint8_t* p)
84{
85 return (uint32_t)p[0] | ((uint32_t)p[1] << 8U) | ((uint32_t)p[2] << 16U) |
86 ((uint32_t)p[3] << k_jof_audit_u32_b3_shift);
87}
88
104RA8_INTERNAL static uint32_t internal_hash(const uint8_t* bytes, size_t len, bool* out_uniform)
105{
106 uint32_t h = (uint32_t)k_jof_audit_fnv_basis;
107 bool uniform = true;
108 for (size_t i = 0U; i < len; ++i) {
109 if (i != 0U) {
110 if (bytes[i] != bytes[0]) {
111 uniform = false;
112 }
113 }
114 h ^= (uint32_t)bytes[i];
115 h *= (uint32_t)k_jof_audit_fnv_prime;
116 }
117 *out_uniform = uniform;
118 return h;
119}
120
138RA8_INTERNAL static ra8_err_t internal_make_span(const void* ptr, size_t len, internal_span_t* out)
139{
140 if (len == 0U) {
141 *out = (internal_span_t){};
142 return k_ra8_ok;
143 }
144 if (ptr == nullptr) {
145 return k_ra8_err_null_ptr;
146 }
147 uintptr_t begin = 0U;
148 static_assert(sizeof(begin) == sizeof(ptr), "uintptr_t must preserve a pointer representation");
149 (void)memcpy((void*)&begin, (const void*)&ptr, sizeof(begin));
150 if (len > (size_t)(UINTPTR_MAX - begin)) {
152 }
153 *out = (internal_span_t){.begin = begin, .end = begin + (uintptr_t)len};
154 return k_ra8_ok;
155}
156
174 const internal_span_t* right)
175{
176 if (left->begin >= right->end) {
177 return false;
178 }
179 return right->begin < left->end;
180}
181
202 const internal_span_t* records,
203 const internal_span_t* tile,
204 const internal_span_t* scratch,
205 const internal_span_t* result)
206{
207 if (internal_spans_overlap(workspace, records)) {
208 return true;
209 }
210 if (internal_spans_overlap(workspace, tile)) {
211 return true;
212 }
213 if (internal_spans_overlap(workspace, scratch)) {
214 return true;
215 }
216 if (internal_spans_overlap(workspace, result)) {
217 return true;
218 }
219 if (internal_spans_overlap(records, tile)) {
220 return true;
221 }
222 if (internal_spans_overlap(records, scratch)) {
223 return true;
224 }
225 if (internal_spans_overlap(records, result)) {
226 return true;
227 }
228 if (internal_spans_overlap(tile, scratch)) {
229 return true;
230 }
231 if (internal_spans_overlap(tile, result)) {
232 return true;
233 }
234 return internal_spans_overlap(scratch, result);
235}
236
238 void* pread_ctx,
239 uint64_t total_size,
241{
242 if (pread == nullptr) {
243 return k_ra8_err_null_ptr;
244 }
245 if (out == nullptr) {
246 return k_ra8_err_null_ptr;
247 }
248 jof_info_t info = {};
249 const ra8_err_t rc = jof_parse(pread, pread_ctx, total_size, &info);
250 if (rc != k_ra8_ok) {
251 return rc;
252 }
253 const uint64_t tile64 = (uint64_t)info.tile_w * (uint64_t)info.tile_h * (uint64_t)info.bpp;
254 if (tile64 > UINT32_MAX) {
256 }
257 const uint32_t tile = (uint32_t)tile64;
258 const jof_audit_requirements_t candidate = {
259 .record_count = info.tile_count,
260 .tile_bytes = tile,
261 .scratch_bytes = (info.codec == (uint8_t)k_jof_codec_deflate) ? jof_stored_bound(tile) : 0U,
262 };
263 if (candidate.tile_bytes == 0U) {
265 }
266 if (info.codec == (uint8_t)k_jof_codec_deflate) {
267 if (candidate.scratch_bytes == 0U) {
269 }
270 }
271 *out = candidate;
272 return k_ra8_ok;
273}
274
295 const jof_audit_requirements_t* need,
296 const jof_audit_result_t* out,
297 size_t record_bytes)
298{
299 internal_span_t workspace_span = {};
300 internal_span_t record_span = {};
301 internal_span_t tile_span = {};
302 internal_span_t scratch_span = {};
303 internal_span_t result_span = {};
304 ra8_err_t rc = internal_make_span(ws, sizeof(*ws), &workspace_span);
305 if (rc == k_ra8_ok) {
306 rc = internal_make_span(ws->records, record_bytes, &record_span);
307 }
308 if (rc == k_ra8_ok) {
309 rc = internal_make_span(ws->tile, need->tile_bytes, &tile_span);
310 }
311 if (rc == k_ra8_ok) {
312 rc = internal_make_span(ws->scratch, need->scratch_bytes, &scratch_span);
313 }
314 if (rc == k_ra8_ok) {
315 rc = internal_make_span(out, sizeof(*out), &result_span);
316 }
317 if (rc != k_ra8_ok) {
318 return rc;
319 }
320 if (internal_any_workspace_overlap(&workspace_span,
321 &record_span,
322 &tile_span,
323 &scratch_span,
324 &result_span)) {
326 }
327 return k_ra8_ok;
328}
329
349 const jof_audit_requirements_t* need,
350 const jof_audit_result_t* out)
351{
352 if (ws == nullptr) {
353 return k_ra8_err_null_ptr;
354 }
355 if (ws->records == nullptr) {
356 return k_ra8_err_null_ptr;
357 }
358 if (ws->tile == nullptr) {
359 return k_ra8_err_null_ptr;
360 }
361 if (need->scratch_bytes != 0U) {
362 if (ws->scratch == nullptr) {
363 return k_ra8_err_null_ptr;
364 }
365 }
366 if (ws->record_cap < need->record_count) {
368 }
369 if (ws->tile_cap < need->tile_bytes) {
371 }
372 if (ws->scratch_cap < need->scratch_bytes) {
374 }
375 const size_t record_bytes = (size_t)need->record_count * sizeof(*ws->records);
376 if (need->record_count != 0U) {
377 if ((record_bytes / sizeof(*ws->records)) != (size_t)need->record_count) {
379 }
380 }
381 return internal_check_workspace_spans(ws, need, out, record_bytes);
382}
383
402 uint32_t count,
403 const jof_audit_record_t* item)
404{
405 if (item->uniform) {
406 return 0U;
407 }
408 uint32_t matches = 0U;
409 for (uint32_t i = 0U; i < count; ++i) {
410 if (records[i].uniform) {
411 continue;
412 }
413 if (records[i].payload != item->payload) {
414 continue;
415 }
416 if (records[i].content_hash != item->content_hash) {
417 continue;
418 }
419 matches++;
420 }
421 return matches;
422}
423
432
451static ra8_err_t internal_audit_tile(internal_audit_context_t* context, uint32_t index_number)
452{
453 uint8_t index[k_jof_index_entry] = {};
454 const uint64_t index_at = (uint64_t)context->candidate->info.index_off +
455 ((uint64_t)index_number * (uint64_t)k_jof_index_entry);
456 ra8_err_t rc =
457 internal_read_exact(context->pread, context->pread_ctx, index_at, index, sizeof(index));
458 if (rc != k_ra8_ok) {
459 return rc;
460 }
461 jof_audit_record_t* const record = &context->workspace->records[index_number];
462 *record = (jof_audit_record_t){
463 .offset = internal_rd_u32(&index[k_jof_idx_ofs_offset]),
464 .length = internal_rd_u32(&index[k_jof_idx_ofs_length]),
465 };
466 if (record->offset != context->expected_offset) {
467 context->candidate->coverage_errors++;
468 }
469 const uint64_t next = (uint64_t)record->offset + (uint64_t)record->length;
470 if (next > UINT32_MAX) {
472 }
473 context->expected_offset = (uint32_t)next;
474
475 const uint16_t tx = (uint16_t)(index_number % (uint32_t)context->candidate->info.tile_cols);
476 const uint16_t ty = (uint16_t)(index_number / (uint32_t)context->candidate->info.tile_cols);
477 rc = jof_read_tile(context->pread,
478 context->pread_ctx,
479 &context->candidate->info,
480 tx,
481 ty,
482 context->workspace->scratch,
483 context->workspace->scratch_cap,
484 context->workspace->tile,
485 context->workspace->tile_cap,
486 &record->width,
487 &record->height);
488 if (rc != k_ra8_ok) {
489 return rc;
490 }
491 record->payload =
492 (uint32_t)record->width * (uint32_t)record->height * (uint32_t)context->candidate->info.bpp;
493 uint16_t want_w = 0U;
494 uint16_t want_h = 0U;
495 rc = jof_tile_dims(&context->candidate->info, tx, ty, &want_w, &want_h);
496 if (rc != k_ra8_ok) {
497 return rc;
498 }
499 bool geometry_error = record->width != want_w;
500 if (record->height != want_h) {
501 geometry_error = true;
502 }
503 if (geometry_error) {
504 context->candidate->geometry_errors++;
505 }
506 record->content_hash = internal_hash(context->workspace->tile, record->payload, &record->uniform);
508 internal_duplicate_count(context->workspace->records, index_number, record);
509 context->candidate->decoded_tiles++;
510 return k_ra8_ok;
511}
512
514 void* pread_ctx,
515 uint64_t total_size,
516 jof_audit_workspace_t* workspace,
518{
519 if (pread == nullptr) {
520 return k_ra8_err_null_ptr;
521 }
522 if (out == nullptr) {
523 return k_ra8_err_null_ptr;
524 }
525 jof_audit_requirements_t need = {};
526 ra8_err_t rc = jof_audit_requirements(pread, pread_ctx, total_size, &need);
527 if (rc != k_ra8_ok) {
528 return rc;
529 }
530 rc = internal_check_workspace(workspace, &need, out);
531 if (rc != k_ra8_ok) {
532 return rc;
533 }
534 jof_audit_result_t candidate = {};
535 rc = jof_parse(pread, pread_ctx, total_size, &candidate.info);
536 if (rc != k_ra8_ok) {
537 return rc;
538 }
539
540 internal_audit_context_t context = {.pread = pread,
541 .pread_ctx = pread_ctx,
542 .workspace = workspace,
543 .candidate = &candidate,
544 .expected_offset = (uint32_t)k_jof_hdr_bytes};
545 for (uint32_t i = 0U; i < candidate.info.tile_count; ++i) {
546 rc = internal_audit_tile(&context, i);
547 if (rc != k_ra8_ok) {
548 return rc;
549 }
550 }
551 if (context.expected_offset != candidate.info.index_off) {
552 candidate.coverage_errors++;
553 }
554 *out = candidate;
555 if (candidate.coverage_errors != 0U) {
557 }
558 if (candidate.geometry_errors != 0U) {
560 }
561 return k_ra8_ok;
562}
@ k_jof_index_entry
Bytes per tile-index entry.
Definition jof.h:145
@ k_jof_hdr_bytes
Header length.
Definition jof.h:143
@ k_jof_idx_ofs_length
Index entry: u32 tile length.
Definition jof.h:162
@ k_jof_idx_ofs_offset
Index entry: u32 tile offset.
Definition jof.h:161
ra8_err_t(* jof_pread_fn)(void *ctx, uint64_t offset, uint8_t *buf, size_t len, size_t *got)
Positioned-read seam over an atlas backing store (DIP).
Definition jof.h:241
ra8_err_t jof_tile_dims(const jof_info_t *info, uint16_t tile_x, uint16_t tile_y, uint16_t *out_w, uint16_t *out_h)
Report the true (edge-clamped) pixel dimensions of one tile.
Definition jof.c:414
ra8_err_t jof_read_tile(jof_pread_fn pread, void *pread_ctx, const jof_info_t *info, uint16_t tile_x, uint16_t tile_y, uint8_t *scratch, uint32_t scratch_cap, uint8_t *out_px, uint32_t out_cap, uint16_t *out_w, uint16_t *out_h)
Read + decode one tile into caller pixels, in bounded RAM.
Definition jof.c:637
ra8_err_t jof_parse(jof_pread_fn pread, void *pread_ctx, uint64_t total_size, jof_info_t *out_info)
Parse + validate a JOF atlas's header, footer and index bounds.
Definition jof.c:379
@ k_jof_codec_deflate
Tile stream is one raw-DEFLATE run.
Definition jof.h:192
uint32_t jof_stored_bound(uint32_t raw_bytes)
Worst-case stored-tile byte bound for scratch/cell sizing.
Definition jof.c:439
static uint32_t internal_rd_u32(const uint8_t *p)
Decode a little-endian u32 from an index entry.
Definition jof_audit.c:83
static ra8_err_t internal_check_workspace_spans(const jof_audit_workspace_t *ws, const jof_audit_requirements_t *need, const jof_audit_result_t *out, size_t record_bytes)
Validate the writable audit span layout.
Definition jof_audit.c:294
static ra8_err_t internal_make_span(const void *ptr, size_t len, internal_span_t *out)
Convert one caller region into an overflow-checked address span.
Definition jof_audit.c:138
jof_audit_hash_t
FNV-1a constants used for decoded tile evidence.
Definition jof_audit.c:29
@ k_jof_audit_fnv_prime
FNV-1a 32-bit prime.
Definition jof_audit.c:31
@ k_jof_audit_u32_b3_shift
LE32 high-byte shift.
Definition jof_audit.c:32
@ k_jof_audit_fnv_basis
FNV-1a 32-bit offset basis.
Definition jof_audit.c:30
static ra8_err_t internal_audit_tile(internal_audit_context_t *context, uint32_t index_number)
Audit one index entry and its decoded tile evidence.
Definition jof_audit.c:451
static bool internal_any_workspace_overlap(const internal_span_t *workspace, const internal_span_t *records, const internal_span_t *tile, const internal_span_t *scratch, const internal_span_t *result)
Check every pair of writable audit spans for overlap.
Definition jof_audit.c:201
ra8_err_t jof_audit_requirements(jof_pread_fn pread, void *pread_ctx, uint64_t total_size, jof_audit_requirements_t *out)
Parse an atlas and report exact caller-storage requirements.
Definition jof_audit.c:237
static uint32_t internal_hash(const uint8_t *bytes, size_t len, bool *out_uniform)
Hash decoded bytes and report whether all bytes are equal.
Definition jof_audit.c:104
static uint32_t internal_duplicate_count(const jof_audit_record_t *records, uint32_t count, const jof_audit_record_t *item)
Count earlier records matching one non-uniform tile fingerprint.
Definition jof_audit.c:401
static bool internal_spans_overlap(const internal_span_t *left, const internal_span_t *right)
Report whether two non-wrapping half-open spans overlap.
Definition jof_audit.c:173
static ra8_err_t internal_read_exact(jof_pread_fn pread, void *ctx, uint64_t offset, uint8_t *buf, size_t len)
Read exactly one bounded window from the injected backing.
Definition jof_audit.c:60
ra8_err_t jof_audit(jof_pread_fn pread, void *pread_ctx, uint64_t total_size, jof_audit_workspace_t *workspace, jof_audit_result_t *out)
Audit coverage, geometry and duplicate-content evidence in bounded RAM.
Definition jof_audit.c:513
static ra8_err_t internal_check_workspace(const jof_audit_workspace_t *ws, const jof_audit_requirements_t *need, const jof_audit_result_t *out)
Validate caller workspace against parsed requirements.
Definition jof_audit.c:348
No-heap, backing-agnostic structural audit for JOF atlases.
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_err_validation_failed
Validation rule failed (caller-supplied invariant not satisfied).
Definition ra8_err.h:459
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_null_ptr
Pointer was NULL where a valid pointer was required.
Definition ra8_err.h:478
@ 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
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
Mutable state shared by the bounded per-tile audit operation.
Definition jof_audit.c:425
jof_pread_fn pread
Injected positioned reader.
Definition jof_audit.c:426
uint32_t expected_offset
Next canonical payload offset.
Definition jof_audit.c:430
jof_audit_workspace_t * workspace
Caller-owned reusable storage.
Definition jof_audit.c:428
jof_audit_result_t * candidate
Transactional result candidate.
Definition jof_audit.c:429
void * pread_ctx
Reader-specific context.
Definition jof_audit.c:427
Half-open address span used for caller-buffer separation checks.
Definition jof_audit.c:36
uintptr_t end
One-past-last byte address.
Definition jof_audit.c:38
uintptr_t begin
First byte address.
Definition jof_audit.c:37
One decoded tile's stored window and content evidence.
Definition jof_audit.h:38
uint16_t height
Edge-clamped decoded height.
Definition jof_audit.h:44
uint32_t length
Stored-stream length.
Definition jof_audit.h:40
uint16_t width
Edge-clamped decoded width.
Definition jof_audit.h:43
uint32_t offset
Absolute stored-stream offset.
Definition jof_audit.h:39
bool uniform
All decoded bytes have the same value.
Definition jof_audit.h:45
uint32_t content_hash
FNV-1a over decoded bytes.
Definition jof_audit.h:42
uint32_t payload
Exact decoded payload bytes.
Definition jof_audit.h:41
Exact caller-storage requirements derived from a parsed atlas.
Definition jof_audit.h:53
uint32_t scratch_bytes
Stored-stream scratch (zero for raw).
Definition jof_audit.h:56
uint32_t record_count
Record entries required.
Definition jof_audit.h:54
uint32_t tile_bytes
Decoded tile-buffer bytes required.
Definition jof_audit.h:55
Geometry plus anomaly counts emitted by a complete audit.
Definition jof_audit.h:78
uint32_t coverage_errors
Gap/overlap/order/end mismatches.
Definition jof_audit.h:81
jof_info_t info
Parsed atlas geometry.
Definition jof_audit.h:79
uint32_t geometry_errors
Decoded size disagreed with tile geometry.
Definition jof_audit.h:82
uint32_t decoded_tiles
Tiles decoded and hashed.
Definition jof_audit.h:80
uint32_t duplicate_candidates
Matching non-uniform hash/size evidence.
Definition jof_audit.h:83
Caller-owned buffers consumed by one audit.
Definition jof_audit.h:64
jof_audit_record_t * records
Tile record array.
Definition jof_audit.h:65
uint32_t tile_cap
Bytes available at tile.
Definition jof_audit.h:68
uint32_t record_cap
Entries available at records.
Definition jof_audit.h:66
uint32_t scratch_cap
Bytes available at scratch.
Definition jof_audit.h:70
uint8_t * tile
Reusable decoded tile.
Definition jof_audit.h:67
uint8_t * scratch
Reusable compressed staging.
Definition jof_audit.h:69
Parsed + validated geometry of one JOF atlas.
Definition jof.h:208
uint8_t bpp
Bytes per pixel (1, 3 or 4).
Definition jof.h:215
uint8_t codec
jof_codec_t member.
Definition jof.h:216
uint16_t tile_w
Tile width, pixels.
Definition jof.h:211
uint32_t index_off
Absolute byte offset of the tile index.
Definition jof.h:218
uint16_t tile_h
Tile height, pixels.
Definition jof.h:212
uint32_t tile_count
Total tiles (== cols * rows).
Definition jof.h:217
uint16_t tile_cols
Ceil(width / tile_w) tile columns.
Definition jof.h:213