parent
cab55d835b
commit
ab1b39598c
@ -0,0 +1,46 @@ |
|||||||
|
/**
|
||||||
|
* TODO file description |
||||||
|
* |
||||||
|
* Created on 2021/11/21. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef FORTH_FH_INPUT_H |
||||||
|
#define FORTH_FH_INPUT_H |
||||||
|
|
||||||
|
struct fh_thread_s; |
||||||
|
struct fh_input_spec_s; |
||||||
|
|
||||||
|
/** Refill the input buffer, returns false on failure / EOF */ |
||||||
|
typedef bool (*fh_input_refill_t)(struct fh_thread_s *fh, struct fh_input_spec_s *spec); |
||||||
|
/** Spec free func */ |
||||||
|
typedef void (*fh_input_free_t)(void *spec); |
||||||
|
|
||||||
|
struct fh_input_spec_s { |
||||||
|
struct fh_input_spec_s *previous; |
||||||
|
fh_input_refill_t refill_input_buffer; |
||||||
|
fh_input_free_t free_self; |
||||||
|
uint32_t linenum; |
||||||
|
|
||||||
|
// saved values, filled when pushing
|
||||||
|
char saved_buffer[INPUT_BUFFER_SIZE]; |
||||||
|
uint32_t saved_inputptr; |
||||||
|
uint32_t saved_inputlen; |
||||||
|
}; |
||||||
|
|
||||||
|
/**
|
||||||
|
* Push current input spec and state, replace with new one |
||||||
|
*/ |
||||||
|
void fh_push_input(struct fh_thread_s *fh, struct fh_input_spec_s *newinput); |
||||||
|
|
||||||
|
/**
|
||||||
|
* Discard current input spec, restore previous. |
||||||
|
* fh->input will be NULL if this was the topmost one |
||||||
|
*/ |
||||||
|
void fh_pop_input(struct fh_thread_s *fh); |
||||||
|
|
||||||
|
struct fh_input_spec_s *fh_create_input_from_filename(char *path); |
||||||
|
struct fh_input_spec_s *fh_create_input_from_filestruct(FILE *f); |
||||||
|
struct fh_input_spec_s *fh_create_input_from_string(char *str, size_t len); |
||||||
|
void fh_input_teardown(struct fh_thread_s *fh); |
||||||
|
|
||||||
|
#endif //FORTH_FH_INPUT_H
|
@ -0,0 +1,191 @@ |
|||||||
|
#include "forth_internal.h" |
||||||
|
|
||||||
|
void fh_push_input(struct fh_thread_s *fh, struct fh_input_spec_s *newinput) |
||||||
|
{ |
||||||
|
if (newinput == NULL) { |
||||||
|
LOGE("push input with NULL"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
struct fh_input_spec_s *oldinput = fh->input; |
||||||
|
if (NULL == oldinput) { |
||||||
|
// no previous input spec, just use the new one
|
||||||
|
fh->input = newinput; |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
fh->input = NULL; |
||||||
|
|
||||||
|
memcpy(&oldinput->saved_buffer[0], &fh->heap[INPUTBUF_ADDR], INPUT_BUFFER_SIZE); |
||||||
|
oldinput->saved_inputlen = fh->inputlen; |
||||||
|
oldinput->saved_inputptr = fh->inputptr; |
||||||
|
newinput->previous = oldinput; |
||||||
|
|
||||||
|
fh->input = newinput; |
||||||
|
} |
||||||
|
|
||||||
|
void fh_pop_input(struct fh_thread_s *fh) |
||||||
|
{ |
||||||
|
struct fh_input_spec_s *discarded = fh->input; |
||||||
|
fh->input = NULL; |
||||||
|
|
||||||
|
struct fh_input_spec_s *restored = discarded->previous; |
||||||
|
if (!restored) { |
||||||
|
return; |
||||||
|
} |
||||||
|
fh->input = restored; // this can be NULL, that must be checked by caller.
|
||||||
|
|
||||||
|
memcpy(&fh->heap[INPUTBUF_ADDR], &restored->saved_buffer[0], INPUT_BUFFER_SIZE); |
||||||
|
fh->inputlen = restored->saved_inputlen; |
||||||
|
fh->inputptr = restored->saved_inputptr; |
||||||
|
|
||||||
|
if (discarded->free_self) { |
||||||
|
discarded->free_self(discarded); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
struct file_input_spec { |
||||||
|
struct fh_input_spec_s spec; |
||||||
|
FILE *file; |
||||||
|
}; |
||||||
|
|
||||||
|
struct string_input_spec { |
||||||
|
struct fh_input_spec_s spec; |
||||||
|
char *str; |
||||||
|
size_t len; |
||||||
|
size_t readpos; |
||||||
|
}; |
||||||
|
|
||||||
|
static inline uint8_t *inputbuf_at(struct fh_thread_s *fh, size_t pos) |
||||||
|
{ |
||||||
|
return &fh->heap[INPUTBUF_ADDR + pos]; |
||||||
|
} |
||||||
|
|
||||||
|
void fh_input_memmove_leftovers(struct fh_thread_s *fh) |
||||||
|
{ |
||||||
|
if (fh->inputptr < fh->inputlen) { |
||||||
|
// something is left
|
||||||
|
uint32_t remains = fh->inputlen - fh->inputptr; |
||||||
|
if (remains > 0) { |
||||||
|
LOG("Refill, reuse %d bytes left in buffer", remains); |
||||||
|
memmove(inputbuf_at(fh, 0), inputbuf_at(fh, fh->inputptr), remains); |
||||||
|
fh->inputptr = 0; |
||||||
|
fh->inputlen = remains; |
||||||
|
} else { |
||||||
|
LOG("Refill, nothing reused (1)"); |
||||||
|
fh->inputptr = 0; |
||||||
|
fh->inputlen = 0; |
||||||
|
} |
||||||
|
} else { |
||||||
|
LOG("Refill, nothing reused (2)"); |
||||||
|
fh->inputptr = 0; |
||||||
|
fh->inputlen = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static bool file_refill(struct fh_thread_s *fh, struct fh_input_spec_s *spec) |
||||||
|
{ |
||||||
|
struct file_input_spec *fis = (struct file_input_spec *) spec; |
||||||
|
fh_input_memmove_leftovers(fh); |
||||||
|
uint32_t space_left = INPUT_BUFFER_SIZE - fh->inputlen; |
||||||
|
char *wp = (char *) inputbuf_at(fh, fh->inputptr); |
||||||
|
LOG("spec %p, fgets %d", spec, space_left); |
||||||
|
if (fgets(wp, (int) space_left, fis->file)) { |
||||||
|
spec->linenum++; |
||||||
|
fh->inputlen = strnlen(wp, INPUT_BUFFER_SIZE); |
||||||
|
return true; |
||||||
|
} else { |
||||||
|
return fh->inputptr > fh->inputlen; // return false only if there is nothing left
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static bool str_refill(struct fh_thread_s *fh, struct fh_input_spec_s *spec) |
||||||
|
{ |
||||||
|
struct string_input_spec *fis = (struct string_input_spec *) spec; |
||||||
|
fh_input_memmove_leftovers(fh); |
||||||
|
uint32_t space_left = INPUTBUF_ADDR - fh->inputlen; |
||||||
|
char *wp = (char *) inputbuf_at(fh, fh->inputptr); |
||||||
|
|
||||||
|
uint32_t chars_remaining_in_string = fis->len - fis->readpos; |
||||||
|
if (chars_remaining_in_string > 0) { |
||||||
|
if (chars_remaining_in_string < space_left) { |
||||||
|
space_left = chars_remaining_in_string; |
||||||
|
} |
||||||
|
|
||||||
|
memcpy(wp, &fis->str[fis->readpos], space_left); |
||||||
|
return true; |
||||||
|
} else { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void free_filespec(void *p) |
||||||
|
{ |
||||||
|
struct file_input_spec *spec = (struct file_input_spec *) p; |
||||||
|
if (spec->file != stdin) { |
||||||
|
fclose(spec->file); |
||||||
|
spec->file = NULL; |
||||||
|
} |
||||||
|
free(spec); |
||||||
|
} |
||||||
|
|
||||||
|
struct fh_input_spec_s *fh_create_input_from_filestruct(FILE *f) |
||||||
|
{ |
||||||
|
struct file_input_spec *spec = calloc(sizeof(struct file_input_spec), 1); |
||||||
|
if (!spec) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
spec->spec.free_self = free; |
||||||
|
spec->spec.refill_input_buffer = file_refill; |
||||||
|
spec->file = f; |
||||||
|
return (struct fh_input_spec_s*) spec; |
||||||
|
} |
||||||
|
|
||||||
|
struct fh_input_spec_s *fh_create_input_from_filename(char *path) |
||||||
|
{ |
||||||
|
struct file_input_spec *spec = calloc(sizeof(struct file_input_spec), 1); |
||||||
|
if (!spec) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
FILE *f = fopen(path, "r"); |
||||||
|
if (!f) { |
||||||
|
free(spec); |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
spec->spec.free_self = free_filespec; |
||||||
|
spec->spec.refill_input_buffer = file_refill; |
||||||
|
spec->file = f; |
||||||
|
return (struct fh_input_spec_s*) spec; |
||||||
|
} |
||||||
|
|
||||||
|
struct fh_input_spec_s *fh_create_input_from_string(char *str, size_t len) |
||||||
|
{ |
||||||
|
struct string_input_spec *spec = calloc(sizeof(struct string_input_spec), 1); |
||||||
|
if (!spec) { |
||||||
|
return NULL; |
||||||
|
} |
||||||
|
|
||||||
|
spec->spec.free_self = free; |
||||||
|
spec->spec.refill_input_buffer = str_refill; |
||||||
|
spec->str = str; |
||||||
|
spec->readpos = 0; |
||||||
|
spec->len = len; |
||||||
|
return (struct fh_input_spec_s*) spec; |
||||||
|
} |
||||||
|
|
||||||
|
void fh_input_teardown(struct fh_thread_s *fh) |
||||||
|
{ |
||||||
|
struct fh_input_spec_s *s = fh->input; |
||||||
|
if (!s) return; |
||||||
|
|
||||||
|
while (s) { |
||||||
|
struct fh_input_spec_s *prev = s->previous; |
||||||
|
if (s->free_self) { |
||||||
|
s->free_self(s); |
||||||
|
} |
||||||
|
s = prev; |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue