-
Notifications
You must be signed in to change notification settings - Fork 19
/
fline.c
112 lines (99 loc) · 3.91 KB
/
fline.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// Copyright (C) 2016 Mark Adler
// See fline.h for a description of these functions and the license.
#include <stdio.h> // size_t, FILE, NULL, fread()
#include <stdlib.h> // malloc(), free(), realloc()
#include <string.h> // memchr(), memcpy()
#include "fline.h"
// Internal structure for line reading. This structure is not visible outside
// of this source file.
struct fline_s {
char *buf; // input buffer -- returned line is entirely in here
size_t size; // allocated size of the input buffer, which can grow
size_t have; // index right after end of available data in buf
size_t pos; // index of start of available data in buf
FILE *in; // input file, set to NULL when EOF is reached
};
void fline_reuse(fline_t *state, FILE *in) {
state->have = 0;
state->pos = 0;
state->in = in;
}
fline_t *fline_start(FILE *in) {
fline_t *state = malloc(sizeof(fline_t));
if (state == NULL)
return NULL; // out of memory
state->size = 32768U; // must be > 1 and a power of 2
state->buf = malloc(state->size);
if (state->buf == NULL) {
free(state);
return NULL; // out of memory
}
fline_reuse(state, in);
return state;
}
void fline_end(fline_t *state) {
if (state != NULL) {
if (state->buf != NULL)
free(state->buf);
free(state);
}
}
char *fline_delim(fline_t *state, size_t *len, int delim) {
// if the buffer is empty, fill it with input data
if (state->pos == state->have) {
if (state->in == NULL) {
*len = 0; // end of file, return 0
return state->buf;
}
state->pos = 0;
state->have = fread(state->buf, 1, state->size, state->in);
if (state->have < state->size)
state->in = NULL; // end of file reached
}
// scan and read input until a delimiter is found or end of file
size_t next = state->pos; // start of unscanned input
for (;;) {
// scan for the delimiter in the available and unscanned input
char *hit = delim < 0 || delim > 255 ? NULL :
memchr(state->buf + next, delim, state->have - next);
// if found or if at end of file, return the line
if (hit != NULL || state->in == NULL) {
size_t beg = state->pos;
state->pos = hit == NULL ? state->have :
(size_t)(hit - state->buf) + 1;
*len = state->pos - beg;
return state->buf + beg;
}
// if the first half of the buffer is now unused, then move the second
// half of the buffer to the first half (move only the available input)
next = state->size >> 1;
if (state->pos >= next) {
memcpy(state->buf + state->pos - next, state->buf + state->pos,
state->size - state->pos);
state->pos -= next;
}
// otherwise allocate more memory, doubling the size of the buffer
else {
next = state->size;
if ((next << 1) == 0)
return NULL; // size_t overflow
char *mem = realloc(state->buf, next << 1);
if (mem == NULL)
return NULL; // out of memory
state->buf = mem;
state->size = next << 1;
}
// either way, load more input into the second half of the buffer,
// then search again for a delimiter
state->have = next + fread(state->buf + next, 1, next, state->in);
if (state->have < state->size)
state->in = NULL; // end of file reached
}
}
char *fline(fline_t *state, size_t *len) {
return fline_delim(state, len, '\n');
}
char *fline_remains(fline_t *state, size_t *len) {
*len = state->have - state->pos;
return state->buf + state->pos;
}