-
Notifications
You must be signed in to change notification settings - Fork 22
/
tokstr.c
167 lines (145 loc) · 4.2 KB
/
tokstr.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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// tokstr -- textual token iterator with some input independence
// 2022-01-29 [revised during code review, add regions]
// 2022-01-25 [initially released inside dnsdbq]
/* externals. */
#define _GNU_SOURCE
#include <sys/types.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "tokstr.h"
/* private data types. */
enum tokstr_type { ts_buffer, ts_string };
struct tokstr_class {
enum tokstr_type type;
};
struct tokstr_region {
struct tokstr_class class;
struct tokstr_reg source;
};
struct tokstr_string {
struct tokstr_class class;
const char *source;
};
struct tokstr_pvt {
union {
struct tokstr_class class;
struct tokstr_region region;
struct tokstr_string string;
} data;
};
/* forward. */
static struct tokstr_reg next_region(struct tokstr_region *, const char *);
static struct tokstr_reg next_string(struct tokstr_string *, const char *);
/* public. */
// tokstr_region -- create an iterator for a counted string
struct tokstr *
tokstr_region(struct tokstr_reg source) {
struct tokstr_region *ts = malloc(sizeof(struct tokstr_region));
if (ts != NULL) {
*ts = (struct tokstr_region) {
.class = (struct tokstr_class) {
.type = ts_buffer,
},
.source = source,
};
}
return (struct tokstr *) ts;
}
// tokstr_string -- create an iterator for a nul-terminated string
struct tokstr *
tokstr_string(const char *source) {
struct tokstr_string *ts = malloc(sizeof(struct tokstr_string));
if (ts != NULL) {
*ts = (struct tokstr_string) {
.class = (struct tokstr_class) {
.type = ts_string,
},
.source = source,
};
}
return (struct tokstr *) ts;
}
// tokstr_next -- return next token from an iterator (which must be free()'d)
// (NULL means no more tokens are available.)
char *
tokstr_next(struct tokstr *ts_pub, const char *delims) {
struct tokstr_reg reg = tokstr_next_region(ts_pub, delims);
if (reg.base == NULL)
return NULL;
return strndup(reg.base, reg.size);
}
// tokstr_next_copy -- copy next token from an iterator; return size, 0, or -1
// (0 means no more tokens are available.)
ssize_t
tokstr_next_copy(struct tokstr *ts, const char *delims,
char *buffer, size_t size)
{
struct tokstr_reg reg = tokstr_next_region(ts, delims);
if (reg.base == NULL)
return 0;
if (reg.size >= size)
return -1;
memcpy(buffer, reg.base, reg.size);
buffer[reg.size] = '\0';
return (ssize_t) reg.size;
}
// tokstr_next_region -- return next token from an iterator (zero-copy)
// (.base == NULL means no more tokens are available.)
struct tokstr_reg
tokstr_next_region(struct tokstr *ts_pub, const char *delims) {
struct tokstr_pvt *ts = (struct tokstr_pvt *) ts_pub;
struct tokstr_reg reg = {};
switch (ts->data.class.type) {
case ts_buffer:
reg = next_region(&ts->data.region, delims);
break;
case ts_string:
reg = next_string(&ts->data.string, delims);
break;
default:
abort();
}
assert((reg.base == NULL) == (reg.size == 0));
return reg;
}
// tokstr_last -- destroy an iterator and release all of its internal resources
void
tokstr_last(struct tokstr **pts) {
free(*pts);
*pts = NULL;
}
/* private functions. */
// next_buffer -- implement tokstr_next for counted string iterators
static struct tokstr_reg
next_region(struct tokstr_region *reg, const char *delims) {
if (reg->source.size != 0) {
while (reg->source.size != 0 &&
strchr(delims, *reg->source.base) != 0)
reg->source.size--, reg->source.base++;
const char *prev = reg->source.base;
while (reg->source.size != 0 &&
strchr(delims, *reg->source.base) == 0)
reg->source.size--, reg->source.base++;
size_t size = (size_t) (reg->source.base - prev);
if (size != 0)
return (struct tokstr_reg) {prev, size};
}
return (struct tokstr_reg) {};
}
// next_string -- implement tokstr_next for nul-terminated string iterators
static struct tokstr_reg
next_string(struct tokstr_string *str, const char *delims) {
int ch = *str->source;
if (ch != '\0') {
while (ch != '\0' && strchr(delims, ch) != NULL)
ch = *++str->source;
const char *prev = str->source;
while (ch != '\0' && strchr(delims, ch) == NULL)
ch = *++str->source;
size_t size = (size_t) (str->source - prev);
if (size != 0)
return (struct tokstr_reg) {prev, size};
}
return (struct tokstr_reg) {};
}