ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
alphabet_soup.c
Go to the documentation of this file.
1
19
20#include "alphabet_soup.h"
21
22#include <stddef.h>
23#include <stdint.h>
24#include <string.h>
25
26#include "ra8_attributes.h"
27#include "ra8_err.h"
28#include "ra8_io_stream.h"
29
31typedef struct {
32 int32_t delta_row;
33 int32_t delta_col;
35
37typedef enum : uint32_t {
41
43{
44 if (ctx == nullptr) {
45 return k_ra8_err_null_ptr;
46 }
47 (void)memset(ctx, 0, sizeof(soup_context_t));
48 return k_ra8_ok;
49}
50
77 const char* key,
78 int32_t span,
79 int32_t r,
80 int32_t c,
81 soup_dir_t dir)
82{
83 int32_t end_r = r + (dir.delta_row * span);
84 int32_t end_c = c + (dir.delta_col * span);
85 int32_t rows = (int32_t)grid->row_count;
86 int32_t cols = (int32_t)grid->col_count;
87
88 if ((end_r < 0) || (end_r >= rows) || (end_c < 0) || (end_c >= cols)) {
89 return false;
90 }
91
92 for (int32_t k = 0; k <= span; ++k) {
93 int32_t cur_r = r + (dir.delta_row * k);
94 int32_t cur_c = c + (dir.delta_col * k);
95 if (grid->cells[cur_r][cur_c] != key[k]) {
96 return false;
97 }
98 }
99
100 return true;
101}
102
130 const char* key,
131 int32_t span,
132 int32_t r,
133 int32_t c,
134 uint32_t* out_end_row,
135 uint32_t* out_end_col)
136{
138 static const soup_dir_t s_search_directions[k_soup_direction_count] = {
139 {.delta_row = 0, .delta_col = 1},
140 {.delta_row = 1, .delta_col = 1},
141 {.delta_row = 1, .delta_col = 0},
142 {.delta_row = 1, .delta_col = -1},
143 {.delta_row = 0, .delta_col = -1},
144 {.delta_row = -1, .delta_col = -1},
145 {.delta_row = -1, .delta_col = 0},
146 {.delta_row = -1, .delta_col = 1},
147 };
148
149 for (uint32_t d = 0U; d < k_soup_direction_count; ++d) {
150 soup_dir_t dir = s_search_directions[d];
151 if (internal_match_ray(grid, key, span, r, c, dir)) {
152 const int32_t end_row = r + (dir.delta_row * span);
153 const int32_t end_col = c + (dir.delta_col * span);
154 *out_end_row = (uint32_t)end_row;
155 *out_end_col = (uint32_t)end_col;
156 return true;
157 }
158 }
159 return false;
160}
161
163 const char* search_key,
164 uint32_t key_len,
165 uint32_t* out_start_row,
166 uint32_t* out_start_col,
167 uint32_t* out_end_row,
168 uint32_t* out_end_col)
169{
170 if ((grid == nullptr) || (search_key == nullptr) || (out_start_row == nullptr) ||
171 (out_start_col == nullptr) || (out_end_row == nullptr) || (out_end_col == nullptr) ||
172 (key_len == 0U) || (key_len > k_soup_max_word_chars) || (grid->row_count == 0U) ||
173 (grid->row_count > k_soup_max_grid_rows) || (grid->col_count == 0U) ||
175 return false;
176 }
177
178 int32_t rows = (int32_t)grid->row_count;
179 int32_t cols = (int32_t)grid->col_count;
180 int32_t span = (int32_t)key_len - 1;
181 char lead = search_key[0];
182
183 for (int32_t r = 0; r < rows; ++r) {
184 for (int32_t c = 0; c < cols; ++c) {
185 if (grid->cells[r][c] != lead) {
186 continue;
187 }
188 if (internal_check_cell(grid, search_key, span, r, c, out_end_row, out_end_col)) {
189 *out_start_row = (uint32_t)r;
190 *out_start_col = (uint32_t)c;
191 return true;
192 }
193 }
194 }
195
196 return false;
197}
198
223internal_parse_uint32(const char** text_ptr, const char* text_end, uint32_t* out_val)
224{
225 const char* ptr = *text_ptr;
226 uint32_t val = 0U;
227 bool found = false;
228
229 for (uint32_t i = 0U; i < k_soup_max_dim_digits; ++i) {
230 if ((ptr >= text_end) || (*ptr < '0') || (*ptr > '9')) {
231 break;
232 }
233 uint32_t digit = (uint32_t)(*ptr - '0');
234 if (val > k_decimal_overflow_cap) {
236 }
237 val = (val * k_decimal_base) + digit;
238 found = true;
239 ptr++;
240 }
241
242 if (!found) {
244 }
245
246 *out_val = val;
247 *text_ptr = ptr;
248 return k_ra8_ok;
249}
250
276 const char* text_end,
277 uint32_t* out_rows,
278 uint32_t* out_cols)
279{
280 uint32_t rows = 0U;
281 uint32_t cols = 0U;
282 ra8_err_t err = internal_parse_uint32(text_ptr, text_end, &rows);
283 if (err != k_ra8_ok) {
284 return err;
285 }
286
287 const char* ptr = *text_ptr;
288 if ((ptr >= text_end) || ((*ptr != 'x') && (*ptr != 'X'))) {
290 }
291 ptr++;
292 *text_ptr = ptr;
293
294 err = internal_parse_uint32(text_ptr, text_end, &cols);
295 if (err != k_ra8_ok) {
296 return err;
297 }
298
299 ptr = *text_ptr;
300 for (uint32_t step = 0U; step < k_soup_max_line_steps; ++step) {
301 if ((ptr >= text_end) || ((*ptr != '\r') && (*ptr != '\n'))) {
302 break;
303 }
304 ptr++;
305 }
306
307 if ((rows == 0U) || (rows > k_soup_max_grid_rows) || (cols == 0U) ||
308 (cols > k_soup_max_grid_cols)) {
310 }
311
312 *out_rows = rows;
313 *out_cols = cols;
314 *text_ptr = ptr;
315 return k_ra8_ok;
316}
317
342 const char* text_end,
343 char* row_cells,
344 uint32_t expected_cols)
345{
346 const char* ptr = *text_ptr;
347 uint32_t c = 0U;
348
349 for (uint32_t step = 0U; step < k_soup_max_line_steps; ++step) {
350 if ((ptr >= text_end) || (*ptr == '\0') || (*ptr == '\n') || (*ptr == '\r')) {
351 break;
352 }
353 if ((*ptr != ' ') && (*ptr != '\t')) {
354 if (c >= expected_cols) {
356 }
357 row_cells[c] = *ptr;
358 c++;
359 }
360 ptr++;
361 }
362
363 if (c != expected_cols) {
365 }
366
367 for (uint32_t step = 0U; step < k_soup_max_line_steps; ++step) {
368 if ((ptr >= text_end) || ((*ptr != '\r') && (*ptr != '\n'))) {
369 break;
370 }
371 ptr++;
372 }
373
374 *text_ptr = ptr;
375 return k_ra8_ok;
376}
377
401internal_parse_grid(const char** text_ptr, const char* text_end, soup_grid_t* grid)
402{
403 for (uint32_t r = 0U; r < grid->row_count; ++r) {
404 ra8_err_t err = internal_parse_single_row(text_ptr, text_end, grid->cells[r], grid->col_count);
405 if (err != k_ra8_ok) {
406 return err;
407 }
408 }
409 return k_ra8_ok;
410}
411
434 const char* word,
435 uint32_t start_row,
436 uint32_t start_col,
437 uint32_t end_row,
438 uint32_t end_col)
439{
440 (void)ra8_io_stream_puts(out_stream, word);
441 (void)ra8_io_stream_putc(out_stream, ' ');
442 (void)ra8_io_stream_put_u32(out_stream, start_row);
443 (void)ra8_io_stream_putc(out_stream, ':');
444 (void)ra8_io_stream_put_u32(out_stream, start_col);
445 (void)ra8_io_stream_putc(out_stream, ' ');
446 (void)ra8_io_stream_put_u32(out_stream, end_row);
447 (void)ra8_io_stream_putc(out_stream, ':');
448 (void)ra8_io_stream_put_u32(out_stream, end_col);
449 (void)ra8_io_stream_putc(out_stream, '\n');
450}
451
474RA8_INTERNAL static void internal_parse_word_entry(const char** text_ptr,
475 const char* text_end,
476 char* word,
477 size_t word_cap,
478 char* search_key,
479 size_t key_cap,
480 uint32_t* out_search_len)
481{
482 const char* ptr = *text_ptr;
483 uint32_t word_len = 0U;
484 uint32_t search_len = 0U;
485
486 for (uint32_t step = 0U; step < k_soup_max_line_steps; ++step) {
487 if ((ptr >= text_end) || (*ptr == '\0') || (*ptr == '\r') || (*ptr == '\n')) {
488 break;
489 }
490 if ((word_len + 1U) < word_cap) {
491 word[word_len] = *ptr;
492 word_len++;
493 }
494 if ((*ptr != ' ') && (*ptr != '\t')) {
495 if ((search_len + 1U) < key_cap) {
496 search_key[search_len] = *ptr;
497 search_len++;
498 }
499 }
500 ptr++;
501 }
502
503 for (uint32_t step = 0U; step < k_soup_max_line_steps; ++step) {
504 if ((ptr >= text_end) || ((*ptr != '\r') && (*ptr != '\n'))) {
505 break;
506 }
507 ptr++;
508 }
509
510 *out_search_len = search_len;
511 *text_ptr = ptr;
512}
513
538 const char* ptr,
539 const char* text_end,
540 ra8_io_stream_t* out_stream)
541{
542 for (uint32_t step = 0U; step < k_soup_max_parse_steps; ++step) {
543 if ((ptr >= text_end) || (*ptr == '\0')) {
544 break;
545 }
546
547 (void)memset(ctx->word_buffer, 0, sizeof(ctx->word_buffer));
548 (void)memset(ctx->search_key_buffer, 0, sizeof(ctx->search_key_buffer));
549 uint32_t search_len = 0U;
550
552 text_end,
553 ctx->word_buffer,
554 sizeof(ctx->word_buffer),
556 sizeof(ctx->search_key_buffer),
557 &search_len);
558
559 if (search_len == 0U) {
560 continue;
561 }
562
563 uint32_t start_row = 0U;
564 uint32_t start_col = 0U;
565 uint32_t end_row = 0U;
566 uint32_t end_col = 0U;
567 if (soup_find_word(&ctx->grid,
569 search_len,
570 &start_row,
571 &start_col,
572 &end_row,
573 &end_col)) {
574 internal_emit_match(out_stream, ctx->word_buffer, start_row, start_col, end_row, end_col);
575 }
576 }
577
578 return ra8_io_stream_flush(out_stream);
579}
580
582soup_solve(soup_context_t* ctx, const char* text, uint32_t text_len, ra8_io_stream_t* out_stream)
583{
584 if ((ctx == nullptr) || (text == nullptr) || (out_stream == nullptr)) {
585 return k_ra8_err_null_ptr;
586 }
587 if ((text_len == 0U) || (text_len > k_soup_max_file_capacity)) {
589 }
590
591 ra8_err_t err = soup_init(ctx);
592 if (err != k_ra8_ok) {
593 return err;
594 }
595
596 const char* ptr = text;
597 const char* text_end = &text[text_len];
598
599 err = internal_parse_dimensions(&ptr, text_end, &ctx->grid.row_count, &ctx->grid.col_count);
600 if (err != k_ra8_ok) {
601 return err;
602 }
603
604 err = internal_parse_grid(&ptr, text_end, &ctx->grid);
605 if (err != k_ra8_ok) {
606 return err;
607 }
608
609 return internal_solve_words(ctx, ptr, text_end, out_stream);
610}
static ra8_err_t internal_parse_dimensions(const char **text_ptr, const char *text_end, uint32_t *out_rows, uint32_t *out_cols)
Parse board header dimensions and validate against capacity limits.
static ra8_err_t internal_parse_grid(const char **text_ptr, const char *text_end, soup_grid_t *grid)
Parse complete grid character matrix from text stream.
ra8_err_t soup_init(soup_context_t *ctx)
Initialize a caller-owned puzzle solver context.
static void internal_emit_match(ra8_io_stream_t *out_stream, const char *word, uint32_t start_row, uint32_t start_col, uint32_t end_row, uint32_t end_col)
Emit single solved word result line to the stream.
static ra8_err_t internal_solve_words(soup_context_t *ctx, const char *ptr, const char *text_end, ra8_io_stream_t *out_stream)
Solve all words following the grid in the text buffer.
soup_numeric_constants_t
Numeric constants for parsing and search navigation.
@ k_decimal_overflow_cap
(UINT32_MAX - 9) / 10 threshold.
bool soup_find_word(const soup_grid_t *grid, const char *search_key, uint32_t key_len, uint32_t *out_start_row, uint32_t *out_start_col, uint32_t *out_end_row, uint32_t *out_end_col)
Search a grid for a normalized word along all 8 compass directions.
static ra8_err_t internal_parse_uint32(const char **text_ptr, const char *text_end, uint32_t *out_val)
Parse a positive decimal integer with overflow detection.
static void internal_parse_word_entry(const char **text_ptr, const char *text_end, char *word, size_t word_cap, char *search_key, size_t key_cap, uint32_t *out_search_len)
Parse one target word line into word and search key buffers.
static ra8_err_t internal_parse_single_row(const char **text_ptr, const char *text_end, char *row_cells, uint32_t expected_cols)
Parse a single grid row into grid cells with eager validation.
ra8_err_t soup_solve(soup_context_t *ctx, const char *text, uint32_t text_len, ra8_io_stream_t *out_stream)
Parse board and words from puzzle text and emit solutions to the output stream.
static bool internal_check_cell(const soup_grid_t *grid, const char *key, int32_t span, int32_t r, int32_t c, uint32_t *out_end_row, uint32_t *out_end_col)
Check all directions starting at a specific cell.
static bool internal_match_ray(const soup_grid_t *grid, const char *key, int32_t span, int32_t r, int32_t c, soup_dir_t dir)
Match word characters along a single directional ray.
Word search puzzle solver API and bounded workspace definitions.
@ k_soup_max_line_steps
Upper bound on line parse iterations.
@ k_soup_max_grid_rows
Maximum board row dimension.
@ k_soup_max_parse_steps
Upper bound on file parse iterations.
@ k_soup_max_dim_digits
Maximum digits in dimension specifier.
@ k_soup_max_word_chars
Maximum characters per target word.
@ k_soup_max_grid_cols
Maximum board column dimension.
@ k_soup_max_file_capacity
Maximum supported puzzle file size.
@ k_soup_direction_count
8-way directional navigation rays.
@ k_decimal_base
Decimal conversion radix.
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Error Code Definitions for ra8-firmware.
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_err_range_check_failed
Value outside range enforced by RA8_CHECK_RANGE / RA8_CHECK_RANGE_TAG.
Definition ra8_err.h:471
@ 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 * memset(void *dst, int value, size_t n)
Fill memory with a constant byte value.
ra8_io targetable byte-stream facade – one writer, many destinations.
ra8_err_t ra8_io_stream_puts(ra8_io_stream_t *s, const char *str)
Write a NUL-terminated string (without the NUL) to the bound sink.
ra8_err_t ra8_io_stream_put_u32(ra8_io_stream_t *s, uint32_t value)
Write value as unsigned decimal ASCII (no leading zeros).
ra8_err_t ra8_io_stream_flush(ra8_io_stream_t *s)
Flush any sink-side buffering to its destination.
ra8_err_t ra8_io_stream_putc(ra8_io_stream_t *s, char c)
Write a single byte to the bound sink.
Caller-allocated byte-stream handle binding a sink to its context.
Caller-owned bounded workspace for puzzle solving (zero-heap).
soup_grid_t grid
Board grid storage.
char word_buffer[k_soup_max_word_chars]
Current word buffer.
char search_key_buffer[k_soup_max_word_chars]
Normalized key buffer.
Direction vector for 2D grid navigation.
int32_t delta_col
Column offset step.
int32_t delta_row
Row offset step.
In-memory word search board matrix.
char cells[k_soup_max_grid_rows][k_soup_max_grid_cols]
Character grid storage.
uint32_t row_count
Active row dimension.
uint32_t col_count
Active column dimension.