From d40f20afb798a55665d43b03b53fe3d15a604af5 Mon Sep 17 00:00:00 2001 From: Tibor Bizjak Date: Tue, 25 Jul 2023 16:55:19 +0200 Subject: [PATCH] complete rewrite. the previous implementetion was incorrect, not all occurences of pattern were found (eg. xy in xxy). --- .../04-find/find-no-overflow.c | 205 ++++++++++-------- 1 file changed, 111 insertions(+), 94 deletions(-) diff --git a/k&r/04-funcs-and-prog-struct/04-find/find-no-overflow.c b/k&r/04-funcs-and-prog-struct/04-find/find-no-overflow.c index d3d3e89..73c4660 100644 --- a/k&r/04-funcs-and-prog-struct/04-find/find-no-overflow.c +++ b/k&r/04-funcs-and-prog-struct/04-find/find-no-overflow.c @@ -2,114 +2,131 @@ #include #include -int flush_lnbuff(void); -int getch_lnbuff(void); -void ignoreln_lnbuff(void); +#define BUFF_BITS 10 +#define BUFF_SIZE (1 << BUFF_BITS) -/* find: prints lines containing pattern, returns number of matching lines - * gracefully handle line overflow - * compile with -O3 */ -int find(const char *pattern, char lineno_flag, char except_flag) +/* buffered_ln: structure for buffered input line */ +struct buffered_ln { + unsigned long lineno; /* line number */ + unsigned int len; /* line length */ + + char self[BUFF_SIZE]; /* line buffer */ + int last_char; /* last read char of line + when equals to EOF or \n the line + has been read to completion */ +}; + +void init_bln(struct buffered_ln *); +int get_bln(struct buffered_ln *); +void flush_bln(struct buffered_ln *); +int sub_of_bln(const char *pattern, struct buffered_ln *, unsigned plen); + +/* find: finds occurences of pattern in input lines */ +int find(char *pattern, char lineno_flag, char except_flag) { - - extern long lineno; - int c, found = 0; - const char * const pstart = pattern; - - while ((c = getch_lnbuff()) != EOF) { - if (c == *pattern) { - pattern++; - continue; - } - - char match = !*pattern - (c == '\n'); - match *= except_flag ? -1 : 1; - - if (match < 0 && except_flag) // no match - ignoreln_lnbuff(); - else if (match > 0) { // match - found++; + size_t plen = strlen(pattern); + if (plen-1 > BUFF_SIZE) { + printf("error: length of pattern > max line\n"); + return -1; + } + + struct buffered_ln bln; + init_bln(&bln); + + while (get_bln(&bln)) + if (sub_of_bln(pattern, &bln, plen) == !except_flag) { if (lineno_flag) - printf("%ld:", lineno); - flush_lnbuff(); + printf("%ld:", bln.lineno); + flush_bln(&bln); } - pattern = pstart; - } - - return found; } - -/* ------------------------ line buffer ---------------------- */ - -#define LNBUFF_LEN 1024 - -/* the line buffer lenght must be a power of two */ -static_assert(!(LNBUFF_LEN % 2), "Line buffer length is not a power of two!"); - - -long lineno; -/* line_buffer: global overflowing line buffer */ -struct { - char self[LNBUFF_LEN]; - unsigned len; - char newline; -} lnbuff = {.len=0, .newline=1}; - -/* LNBUFF_I: current index in line buffer */ -#define LNBUFF_I (lnbuff.len & (LNBUFF_LEN - 1)) - -/* getch_lnbuff: getchar and add it to buffer */ -int getch_lnbuff() +/* init_bln: initalizes buffered line struct */ +void init_bln(struct buffered_ln *bln) { - int c = getchar(); - - if (lnbuff.newline) { - lnbuff.len = 0; - lnbuff.newline = 0; - lineno++; - } - if (c == EOF || c == '\n') // new line - lnbuff.newline = 1; - else { - lnbuff.self[LNBUFF_I] = c; - lnbuff.len++; - } - - return c; + bln->lineno = 0; + bln->len = 0; + bln->last_char = '\n'; } -/* flush_lnbuff: print contents of line buffer, return length */ -int flush_lnbuff() -{ - int i; +#define OVERFLOW_MASK (~0 << BUFF_BITS) +#define INDEX_MASK (~OVERFLOW_MASK) +#define bln_i(i) ((i) & INDEX_MASK) /* casts unsigned integer i to a valid bln index */ - /* handle overflowing chars */ - if (lnbuff.len > LNBUFF_LEN) { - for (i = LNBUFF_LEN; i < lnbuff.len; i++) +#define overflowed_bln(bln) (bln->len & OVERFLOW_MASK) /* 1 if bln is overflowed */ +/* eol_bln: 1 if bln si finalized (last_char is \n or EOF) */ +#define eol_bln(bln) (bln->last_char == EOF || bln->last_char == '\n') +#define bln_next_i(bln) bln_i(bln->len) /* next writable index of bln */ +#define bln_start_i(bln) (overflowed_bln(bln) ? bln_next_i(bln) : 0) /* starting index of bln */ + +/* getchar_bln: gets char from input and writes it to bln, returns it */ +#define getchar_bln(bln) \ + (bln->self[bln_next_i(bln)] = bln->last_char, ++bln->len, bln->last_char = getchar()) + +/* get_bln: skips current line in buffer line bln, sets bln to next line of input + * returns 0 on EOF and a postivie value otherwise */ +int get_bln(struct buffered_ln *bln) +{ + /* finalize previous line */ + while (!eol_bln(bln)) + bln->last_char = getchar(); + if (bln->last_char == EOF) + return 0; + /* init next line */ + bln->lineno++; + bln->len = 0; + return (bln->last_char = getchar()) != EOF; +} + +/* flush_bln: flushes buffered line bln to stdout */ +void flush_bln(struct buffered_ln *bln) +{ + unsigned i; + /* print overflowed chars */ + if (overflowed_bln(bln)) { + for (i = BUFF_SIZE; i < bln->len; i++) putchar('.'); - for (i = LNBUFF_I; i < LNBUFF_LEN; i++) - putchar(lnbuff.self[i]); + for (i = bln_next_i(bln); i < BUFF_SIZE; i++) + putchar(bln->self[i]); } - /* handle rest of chars */ - for (i = 0; i < LNBUFF_I; i++) - putchar(lnbuff.self[i]); - /* print unbuffered part of line */ - if (!lnbuff.newline) - while ((i = getchar()) != EOF && i != '\n') - putchar(i); + /* print buffered chars */ + for (i = 0; i < bln_next_i(bln); i++) + putchar(bln->self[i]); + /* print not yet read chars */ + for (; !eol_bln(bln); bln->last_char = getchar()) + putchar(bln->last_char); putchar('\n'); - lnbuff.newline = 1; - return lnbuff.len; } -/* ignoreln_lnbuff: ignore line of input */ -void ignoreln_lnbuff() +/* sub_of_bln: returns 0 if pattern with length plen is not a substring of + * buffered line bln and a positive value otherwise */ +int sub_of_bln(const char *pattern, struct buffered_ln *bln, const unsigned plen) { - int c; - if (lnbuff.newline) - return; - while ((c = getchar()) != EOF && c != '\n') - ; - lnbuff.newline = 1; + /* buffered line must not be overflowed */ + assert(!overflowed_bln(bln)); + + /* fill buffer with plen-1 chars */ + for (; bln->len < plen-1; getchar_bln(bln)) + if (eol_bln(bln)) + return 0; + + unsigned i, j; + int k; + /* handle buffered chars */ + for (i = plen-1; i < bln->len; i++) { + for (j = i, k = plen-1; k >= 0 && bln->self[j] == pattern[k]; k--, j--) + ; + if (k < 0) + return 1; + } + /* handle no yet read chars */ + for (; !eol_bln(bln); getchar_bln(bln)) { + if (bln->last_char != pattern[plen-1]) + continue; + for (j = bln_i(bln_next_i(bln)-1), k = plen-2; k >=0 && bln->self[j] == pattern[k]; k--, j = bln_i(j-1)) + ; + if (k < 0) + return 1; + } + return 0; }