Fix bugs. Properly handle partial match.

This commit is contained in:
CismonX 2020-12-08 17:19:02 +08:00
parent a47c6f78eb
commit 342ff4d8f0
Signed by: cismonx
GPG Key ID: 3094873E29A482FB
4 changed files with 44 additions and 50 deletions

View File

@ -120,13 +120,6 @@ before
You may want this option enabled if
.I fd
is maintained in an event loop.
.TP
.B CTLSEQS_READER_RETAIN_PARTIAL
When
.BR ctlseqs_read ()
returns
.BR CTLSEQS_PARTIAL ,
preserve the partially matched sequence in the internal buffer for future calls.
.
.SH RETURN VALUE
.TP
@ -135,14 +128,11 @@ Success.
.TP
.B CTLSEQS_NOMEM
Fails to allocate sufficient memory.
.SH BUGS
After a successful
.BR ctlseqs_read ()
call, changing
.TP
.B CTLSEQS_ERROR
Attempts to change
.I maxlen
to a smaller value on the same
.I reader
may result in data loss due to truncation of the internal read buffer.
value, but data in the internal read buffer will be lost due to truncation, if done so.
.
.SH SEE ALSO
.BR ctlseqs_read (3)

View File

@ -32,7 +32,6 @@
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <poll.h>
#include <unistd.h>
@ -136,15 +135,15 @@ struct ctlseqs_reader {
union ctlseqs_value *buffer;
size_t readlen;
struct pollfd pollfd;
bool no_poll;
bool retain_partial;
char *rbuf;
size_t buf_start;
size_t buf_end;
size_t last_idx;
enum ctlseqs_state state;
bool no_poll;
};
CTLSEQS_HOT static int
CTLSEQS_HOT static inline int
ctlseqs_poll(struct pollfd *pollfd, int timeout)
{
int nevents = poll(pollfd, 1, timeout);
@ -160,10 +159,11 @@ ctlseqs_poll(struct pollfd *pollfd, int timeout)
return pollfd->revents & POLLHUP ? CTLSEQS_EOF : CTLSEQS_ERROR;
}
CTLSEQS_HOT static int
CTLSEQS_HOT static inline int
ctlseqs_do_read(struct ctlseqs_reader *reader)
{
ssize_t nbytes = read(reader->pollfd.fd, reader->rbuf + reader->buf_start, reader->readlen - reader->buf_start);
char *buf = reader->rbuf + reader->buf_start + reader->last_idx;
ssize_t nbytes = read(reader->pollfd.fd, buf, reader->readlen - reader->buf_start);
if (CTLSEQS_UNLIKELY(nbytes == -1)) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
return CTLSEQS_TIMEOUT;
@ -231,7 +231,7 @@ ctlseqs_state_transition(enum ctlseqs_state state, char ch)
}
}
CTLSEQS_HOT bool
CTLSEQS_HOT static bool
ctlseqs_fetch(char **seq, int type, union ctlseqs_value *buf, size_t *buf_offset)
{
unsigned long cnt, num;
@ -322,13 +322,13 @@ ctlseqs_match_pattern(struct ctlseqs_reader *reader, struct ctlseqs_matcher cons
}
CTLSEQS_HOT static ssize_t
ctlseqs_match(struct ctlseqs_reader *reader, struct ctlseqs_matcher const *matcher, bool retain_partial)
ctlseqs_match(struct ctlseqs_reader *reader, struct ctlseqs_matcher const *matcher)
{
ssize_t retval = CTLSEQS_PARTIAL;
char const *buf = reader->rbuf + reader->buf_start;
size_t idx, len = reader->buf_end - reader->buf_start;
enum ctlseqs_state state = reader->state;
for (idx = 0; idx < len; ++idx) {
for (idx = reader->last_idx; idx < len; ++idx) {
state = ctlseqs_state_transition(state, buf[idx]);
if (state == ctlseqs_state_err) {
for (; idx < len; ++idx) {
@ -344,13 +344,16 @@ ctlseqs_match(struct ctlseqs_reader *reader, struct ctlseqs_matcher const *match
break;
}
}
if (retval == CTLSEQS_PARTIAL || retval == CTLSEQS_NOSEQ) {
reader->state = state >= ctlseqs_state_done ? ctlseqs_state_none : state;
if (retval == CTLSEQS_NOMATCH || retval == CTLSEQS_PARTIAL || retval == CTLSEQS_NOSEQ) {
reader->buffer[0].num = idx;
reader->buffer[1].str = buf;
}
reader->state = state;
if (!retain_partial || retval != CTLSEQS_PARTIAL) {
if (retval == CTLSEQS_PARTIAL) {
reader->last_idx = idx;
} else {
reader->buf_start += idx;
reader->last_idx = 0;
}
if (reader->buf_start == reader->buf_end) {
reader->buf_start = 0;
@ -443,7 +446,9 @@ ctlseqs_reader_init()
{
struct ctlseqs_reader *reader = malloc(sizeof(struct ctlseqs_reader));
if (CTLSEQS_LIKELY(reader != NULL)) {
*reader = (struct ctlseqs_reader) { 0 };
*reader = (struct ctlseqs_reader) {
.pollfd.events = POLLIN,
};
}
return reader;
}
@ -451,13 +456,15 @@ ctlseqs_reader_init()
int
ctlseqs_reader_setopt(struct ctlseqs_reader *reader, struct ctlseqs_reader_opts const *options)
{
reader->buffer = options->buffer;
size_t readlen = options->maxlen;
if (reader->readlen != readlen) {
char *rbuf;
if (reader->rbuf == NULL) {
rbuf = malloc(readlen);
} else {
if (readlen < reader->buf_end) {
return CTLSEQS_ERROR;
}
rbuf = realloc(reader->rbuf, readlen);
}
if (rbuf == NULL) {
@ -469,39 +476,34 @@ ctlseqs_reader_setopt(struct ctlseqs_reader *reader, struct ctlseqs_reader_opts
reader->buf_end = reader->readlen;
}
}
reader->pollfd = (struct pollfd) { .fd = options->fd, .events = POLLIN };
reader->buffer = options->buffer;
reader->pollfd.fd = options->fd;
reader->no_poll = options->flags & CTLSEQS_READER_NO_POLL;
reader->retain_partial = options->flags & CTLSEQS_READER_RETAIN_PARTIAL;
return CTLSEQS_OK;
}
CTLSEQS_HOT ssize_t
ctlseqs_read(struct ctlseqs_reader *reader, struct ctlseqs_matcher const *matcher, int timeout)
{
ssize_t match_result;
if (reader->buf_start != 0) {
match_result = ctlseqs_match(reader, matcher, true);
if (match_result != CTLSEQS_PARTIAL) {
goto terminate;
ssize_t result;
// Whether we have read more than we could match in the preview call.
if (reader->state == ctlseqs_state_none && reader->buf_start != 0) {
result = ctlseqs_match(reader, matcher);
if (result != CTLSEQS_PARTIAL) {
return result;
}
}
if (!reader->no_poll) {
match_result = ctlseqs_poll(&reader->pollfd, timeout);
if (match_result < 0) {
goto terminate;
result = ctlseqs_poll(&reader->pollfd, timeout);
if (result < 0) {
return result;
}
}
match_result = ctlseqs_do_read(reader);
if (CTLSEQS_UNLIKELY(match_result < 0)) {
if (match_result == CTLSEQS_TIMEOUT && reader->state != ctlseqs_state_none) {
match_result = CTLSEQS_PARTIAL;
}
goto terminate;
result = ctlseqs_do_read(reader);
if (CTLSEQS_UNLIKELY(result < 0)) {
return reader->state == ctlseqs_state_none ? result : CTLSEQS_PARTIAL;
}
match_result = ctlseqs_match(reader, matcher, reader->retain_partial);
terminate:
reader->state = ctlseqs_state_none;
return match_result;
return ctlseqs_match(reader, matcher);
}
CTLSEQS_COLD void

View File

@ -349,8 +349,7 @@
/* Reader option flags */
#define CTLSEQS_READER_NO_POLL (1 << 0) // Do not poll() before read()
#define CTLSEQS_READER_RETAIN_PARTIAL (1 << 1) // Retain data after a partial match
#define CTLSEQS_READER_NO_POLL (1 << 0) // Do not poll() before read()
/* Function return status codes */

View File

@ -389,6 +389,9 @@ main(int argc, char **argv)
case CTLSEQS_PARTIAL:
print_generic_seq("PARTIAL", buffer);
break;
case CTLSEQS_NOMATCH:
print_generic_seq("NOMATCH", buffer);
break;
case CTLSEQS_NOSEQ:
print_generic_seq("NOSEQ", buffer);
if (buffer[1].str[0] == 0x03) {