strptime 0.1.0-x86-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.clang-format +67 -0
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/.travis.yml +12 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +26 -0
- data/README.md +41 -0
- data/Rakefile +28 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/ext/strptime/extconf.rb +5 -0
- data/ext/strptime/ruby_time.c +380 -0
- data/ext/strptime/strptime.c +764 -0
- data/ext/strptime/strptime.h +11 -0
- data/lib/strptime.rb +6 -0
- data/lib/strptime/version.rb +3 -0
- data/strptime.gemspec +29 -0
- metadata +132 -0
@@ -0,0 +1,764 @@
|
|
1
|
+
#include "strptime.h"
|
2
|
+
#include <time.h>
|
3
|
+
|
4
|
+
VALUE rb_cStrptime;
|
5
|
+
|
6
|
+
#define GetStrptimeval(obj, tobj) ((tobj) = get_strptimeval(obj))
|
7
|
+
#define GetNewStrptimeval(obj, tobj) ((tobj) = get_new_strptimeval(obj))
|
8
|
+
#define STRPTIME_INIT_P(tobj) ((tobj)->isns)
|
9
|
+
|
10
|
+
#define LIKELY(x) (__builtin_expect((x), 1))
|
11
|
+
#define UNLIKELY(x) (__builtin_expect((x), 0))
|
12
|
+
|
13
|
+
#define REG_PC (pc)
|
14
|
+
#define GET_PC() REG_PC
|
15
|
+
#define SET_PC(x) (REG_PC = (x))
|
16
|
+
#define GET_CURRENT_INSN() (*GET_PC())
|
17
|
+
#define GET_OPERAND(n) (GET_PC()[(n)])
|
18
|
+
#define ADD_PC(n) (SET_PC(REG_PC + (n)))
|
19
|
+
|
20
|
+
#define JUMP(dst) (REG_PC += (dst))
|
21
|
+
|
22
|
+
#define LABEL(x) INSN_LABEL_##x
|
23
|
+
#define ELABEL(x) INSN_ELABEL_##x
|
24
|
+
#define LABEL_PTR(x) &&LABEL(x)
|
25
|
+
|
26
|
+
#define INSN_ENTRY(insn) LABEL(insn) :
|
27
|
+
|
28
|
+
#define TC_DISPATCH(insn) \
|
29
|
+
goto *(void const *)GET_CURRENT_INSN(); \
|
30
|
+
;
|
31
|
+
#define END_INSN(insn) TC_DISPATCH(insn);
|
32
|
+
|
33
|
+
#define INSN_DISPATCH() \
|
34
|
+
TC_DISPATCH(__START__) \
|
35
|
+
{
|
36
|
+
|
37
|
+
#define END_INSNS_DISPATCH() \
|
38
|
+
rb_bug("unknown insn: %p", GET_CURRENT_INSN()); \
|
39
|
+
} /* end of while loop */
|
40
|
+
|
41
|
+
#define NEXT_INSN() TC_DISPATCH(__NEXT_INSN__)
|
42
|
+
|
43
|
+
static const char *month_names[] = {
|
44
|
+
"January", "February", "March", "April", "May", "June",
|
45
|
+
"July", "August", "September", "October", "November", "December",
|
46
|
+
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
47
|
+
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
48
|
+
|
49
|
+
#define sizeof_array(o) (sizeof o / sizeof o[0])
|
50
|
+
|
51
|
+
#define issign(c) ((c) == '-' || (c) == '+')
|
52
|
+
#undef isdigit
|
53
|
+
#define isdigit(c) ((unsigned char)((c) - '0') <= 9u)
|
54
|
+
#undef isspace
|
55
|
+
#define isspace(c) \
|
56
|
+
((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\v' || (c) == '\f' || \
|
57
|
+
(c) == '\r')
|
58
|
+
|
59
|
+
/* imply NUL-terminated string */
|
60
|
+
static size_t
|
61
|
+
read_digits(const char *s, int *n, size_t width)
|
62
|
+
{
|
63
|
+
const char *s0 = s;
|
64
|
+
const char *se = s + width;
|
65
|
+
int r = 0;
|
66
|
+
|
67
|
+
for (; s < se && isdigit(*s); s++) {
|
68
|
+
r *= 10;
|
69
|
+
r += (unsigned char)((*s) - '0');
|
70
|
+
}
|
71
|
+
*n = r;
|
72
|
+
return (size_t)(s - s0);
|
73
|
+
}
|
74
|
+
|
75
|
+
#define fail() \
|
76
|
+
{ \
|
77
|
+
return -1; \
|
78
|
+
}
|
79
|
+
|
80
|
+
#define READ_DIGITS(n, w) \
|
81
|
+
{ \
|
82
|
+
size_t l; \
|
83
|
+
l = read_digits(&str[si], &n, w); \
|
84
|
+
if (l == 0) fail(); \
|
85
|
+
si += l; \
|
86
|
+
}
|
87
|
+
|
88
|
+
static int
|
89
|
+
valid_range_p(int v, int a, int b)
|
90
|
+
{
|
91
|
+
return !(v < a || v > b);
|
92
|
+
}
|
93
|
+
|
94
|
+
static int
|
95
|
+
strptime_exec0(void **pc, const char *fmt, const char *str, size_t slen,
|
96
|
+
struct timespec *tsp, int *gmtoffp)
|
97
|
+
{
|
98
|
+
size_t si = 0;
|
99
|
+
int year = INT_MAX, mon = -1, mday = -1, hour = -1, min = -1, sec = -1,
|
100
|
+
nsec = 0, gmtoff = INT_MAX;
|
101
|
+
if (UNLIKELY(tsp == NULL)) {
|
102
|
+
static const void *const insns_address_table[] = {
|
103
|
+
LABEL_PTR(A), LABEL_PTR(B), LABEL_PTR(C), LABEL_PTR(D),
|
104
|
+
LABEL_PTR(E), LABEL_PTR(F), LABEL_PTR(G), LABEL_PTR(H),
|
105
|
+
LABEL_PTR(I), NULL, NULL, LABEL_PTR(L),
|
106
|
+
LABEL_PTR(M), LABEL_PTR(N), LABEL_PTR(O), LABEL_PTR(P),
|
107
|
+
LABEL_PTR(Q), LABEL_PTR(R), LABEL_PTR(S), LABEL_PTR(T),
|
108
|
+
LABEL_PTR(U), LABEL_PTR(V), LABEL_PTR(W), LABEL_PTR(X),
|
109
|
+
LABEL_PTR(Y), LABEL_PTR(Z), LABEL_PTR(_25), LABEL_PTR(_2b),
|
110
|
+
LABEL_PTR(_3a), NULL, LABEL_PTR(_5f), LABEL_PTR(_60),
|
111
|
+
LABEL_PTR(a), LABEL_PTR(B), LABEL_PTR(c), LABEL_PTR(d),
|
112
|
+
LABEL_PTR(d), NULL, LABEL_PTR(g), LABEL_PTR(B),
|
113
|
+
NULL, LABEL_PTR(j), NULL, LABEL_PTR(l),
|
114
|
+
LABEL_PTR(m), LABEL_PTR(n), NULL, LABEL_PTR(p),
|
115
|
+
NULL, LABEL_PTR(r), LABEL_PTR(s), LABEL_PTR(t),
|
116
|
+
LABEL_PTR(u), LABEL_PTR(v), LABEL_PTR(w), LABEL_PTR(x),
|
117
|
+
LABEL_PTR(y), LABEL_PTR(z),
|
118
|
+
};
|
119
|
+
*pc = (void *)insns_address_table;
|
120
|
+
return 0;
|
121
|
+
}
|
122
|
+
|
123
|
+
first:
|
124
|
+
INSN_DISPATCH();
|
125
|
+
INSN_ENTRY(A)
|
126
|
+
{
|
127
|
+
ADD_PC(1);
|
128
|
+
END_INSN(A)
|
129
|
+
}
|
130
|
+
INSN_ENTRY(B)
|
131
|
+
{
|
132
|
+
int i;
|
133
|
+
for (i = 0; i < (int)sizeof_array(month_names); i++) {
|
134
|
+
size_t l = strlen(month_names[i]);
|
135
|
+
if (strncasecmp(month_names[i], &str[si], l) == 0) {
|
136
|
+
si += l;
|
137
|
+
mon = (i % 12) + 1;
|
138
|
+
ADD_PC(1);
|
139
|
+
END_INSN(B)
|
140
|
+
}
|
141
|
+
}
|
142
|
+
fail();
|
143
|
+
}
|
144
|
+
INSN_ENTRY(C)
|
145
|
+
{
|
146
|
+
ADD_PC(1);
|
147
|
+
END_INSN(C)
|
148
|
+
}
|
149
|
+
INSN_ENTRY(D)
|
150
|
+
{
|
151
|
+
ADD_PC(1);
|
152
|
+
END_INSN(D)
|
153
|
+
}
|
154
|
+
INSN_ENTRY(E)
|
155
|
+
{
|
156
|
+
ADD_PC(1);
|
157
|
+
END_INSN(E)
|
158
|
+
}
|
159
|
+
INSN_ENTRY(F)
|
160
|
+
{
|
161
|
+
ADD_PC(1);
|
162
|
+
END_INSN(F)
|
163
|
+
}
|
164
|
+
INSN_ENTRY(G)
|
165
|
+
{
|
166
|
+
ADD_PC(1);
|
167
|
+
END_INSN(G)
|
168
|
+
}
|
169
|
+
INSN_ENTRY(H)
|
170
|
+
{
|
171
|
+
READ_DIGITS(hour, 2);
|
172
|
+
if (!valid_range_p(hour, 0, 23)) fail();
|
173
|
+
ADD_PC(1);
|
174
|
+
END_INSN(H)
|
175
|
+
}
|
176
|
+
INSN_ENTRY(I)
|
177
|
+
{
|
178
|
+
ADD_PC(1);
|
179
|
+
END_INSN(I)
|
180
|
+
}
|
181
|
+
INSN_ENTRY(L)
|
182
|
+
{
|
183
|
+
ADD_PC(1);
|
184
|
+
END_INSN(L)
|
185
|
+
}
|
186
|
+
INSN_ENTRY(M)
|
187
|
+
{
|
188
|
+
READ_DIGITS(min, 2);
|
189
|
+
if (!valid_range_p(min, 0, 59)) fail();
|
190
|
+
ADD_PC(1);
|
191
|
+
END_INSN(M)
|
192
|
+
}
|
193
|
+
INSN_ENTRY(N)
|
194
|
+
{
|
195
|
+
size_t l;
|
196
|
+
l = read_digits(&str[si], &nsec, 9);
|
197
|
+
if (!l) fail();
|
198
|
+
si += l;
|
199
|
+
for (; l < 9; l++) {
|
200
|
+
nsec *= 10;
|
201
|
+
}
|
202
|
+
ADD_PC(1);
|
203
|
+
END_INSN(N)
|
204
|
+
}
|
205
|
+
INSN_ENTRY(O)
|
206
|
+
{
|
207
|
+
ADD_PC(1);
|
208
|
+
END_INSN(O)
|
209
|
+
}
|
210
|
+
INSN_ENTRY(P)
|
211
|
+
{
|
212
|
+
ADD_PC(1);
|
213
|
+
END_INSN(P)
|
214
|
+
}
|
215
|
+
INSN_ENTRY(Q)
|
216
|
+
{
|
217
|
+
ADD_PC(1);
|
218
|
+
END_INSN(Q)
|
219
|
+
}
|
220
|
+
INSN_ENTRY(R)
|
221
|
+
{
|
222
|
+
ADD_PC(1);
|
223
|
+
END_INSN(R)
|
224
|
+
}
|
225
|
+
INSN_ENTRY(S)
|
226
|
+
{
|
227
|
+
READ_DIGITS(sec, 2);
|
228
|
+
if (!valid_range_p(sec, 0, 60)) fail();
|
229
|
+
ADD_PC(1);
|
230
|
+
END_INSN(S)
|
231
|
+
}
|
232
|
+
INSN_ENTRY(T)
|
233
|
+
{
|
234
|
+
ADD_PC(1);
|
235
|
+
END_INSN(T)
|
236
|
+
}
|
237
|
+
INSN_ENTRY(U)
|
238
|
+
{
|
239
|
+
ADD_PC(1);
|
240
|
+
END_INSN(U)
|
241
|
+
}
|
242
|
+
INSN_ENTRY(V)
|
243
|
+
{
|
244
|
+
ADD_PC(1);
|
245
|
+
END_INSN(V)
|
246
|
+
}
|
247
|
+
INSN_ENTRY(W)
|
248
|
+
{
|
249
|
+
ADD_PC(1);
|
250
|
+
END_INSN(W)
|
251
|
+
}
|
252
|
+
INSN_ENTRY(X)
|
253
|
+
{
|
254
|
+
ADD_PC(1);
|
255
|
+
END_INSN(X)
|
256
|
+
}
|
257
|
+
INSN_ENTRY(Y)
|
258
|
+
{
|
259
|
+
char c = str[si];
|
260
|
+
if (issign(c)) si++;
|
261
|
+
READ_DIGITS(year, 4);
|
262
|
+
if (c == '-') year *= -1;
|
263
|
+
ADD_PC(1);
|
264
|
+
END_INSN(Y)
|
265
|
+
}
|
266
|
+
INSN_ENTRY(Z)
|
267
|
+
{
|
268
|
+
ADD_PC(1);
|
269
|
+
END_INSN(Z)
|
270
|
+
}
|
271
|
+
INSN_ENTRY(a)
|
272
|
+
{
|
273
|
+
ADD_PC(1);
|
274
|
+
END_INSN(a)
|
275
|
+
}
|
276
|
+
INSN_ENTRY(c)
|
277
|
+
{
|
278
|
+
ADD_PC(1);
|
279
|
+
END_INSN(c)
|
280
|
+
}
|
281
|
+
INSN_ENTRY(d)
|
282
|
+
{
|
283
|
+
READ_DIGITS(mday, 2);
|
284
|
+
if (!valid_range_p(mday, 1, 31)) fail();
|
285
|
+
ADD_PC(1);
|
286
|
+
END_INSN(d)
|
287
|
+
}
|
288
|
+
INSN_ENTRY(g)
|
289
|
+
{
|
290
|
+
ADD_PC(1);
|
291
|
+
END_INSN(g)
|
292
|
+
}
|
293
|
+
INSN_ENTRY(j)
|
294
|
+
{
|
295
|
+
ADD_PC(1);
|
296
|
+
END_INSN(j)
|
297
|
+
}
|
298
|
+
INSN_ENTRY(l)
|
299
|
+
{
|
300
|
+
ADD_PC(1);
|
301
|
+
END_INSN(l)
|
302
|
+
}
|
303
|
+
INSN_ENTRY(m)
|
304
|
+
{
|
305
|
+
READ_DIGITS(mon, 2);
|
306
|
+
if (!valid_range_p(mon, 1, 12)) fail();
|
307
|
+
ADD_PC(1);
|
308
|
+
END_INSN(m)
|
309
|
+
}
|
310
|
+
INSN_ENTRY(n)
|
311
|
+
{
|
312
|
+
for (; si < slen && isspace(str[si]); si++) {
|
313
|
+
}
|
314
|
+
ADD_PC(1);
|
315
|
+
END_INSN(n)
|
316
|
+
}
|
317
|
+
INSN_ENTRY(p)
|
318
|
+
{
|
319
|
+
ADD_PC(1);
|
320
|
+
END_INSN(p)
|
321
|
+
}
|
322
|
+
INSN_ENTRY(r)
|
323
|
+
{
|
324
|
+
ADD_PC(1);
|
325
|
+
END_INSN(r)
|
326
|
+
}
|
327
|
+
INSN_ENTRY(s)
|
328
|
+
{
|
329
|
+
ADD_PC(1);
|
330
|
+
END_INSN(s)
|
331
|
+
}
|
332
|
+
INSN_ENTRY(t)
|
333
|
+
{
|
334
|
+
ADD_PC(1);
|
335
|
+
END_INSN(t)
|
336
|
+
}
|
337
|
+
INSN_ENTRY(u)
|
338
|
+
{
|
339
|
+
ADD_PC(1);
|
340
|
+
END_INSN(u)
|
341
|
+
}
|
342
|
+
INSN_ENTRY(v)
|
343
|
+
{
|
344
|
+
ADD_PC(1);
|
345
|
+
END_INSN(v)
|
346
|
+
}
|
347
|
+
INSN_ENTRY(w)
|
348
|
+
{
|
349
|
+
ADD_PC(1);
|
350
|
+
END_INSN(w)
|
351
|
+
}
|
352
|
+
INSN_ENTRY(x)
|
353
|
+
{
|
354
|
+
ADD_PC(1);
|
355
|
+
END_INSN(x)
|
356
|
+
}
|
357
|
+
INSN_ENTRY(y)
|
358
|
+
{
|
359
|
+
ADD_PC(1);
|
360
|
+
END_INSN(y)
|
361
|
+
}
|
362
|
+
INSN_ENTRY(z)
|
363
|
+
{
|
364
|
+
const char *p0 = str + si;
|
365
|
+
int r;
|
366
|
+
size_t len;
|
367
|
+
if (*p0 == 'z') {
|
368
|
+
gmtoff = 0;
|
369
|
+
ADD_PC(1);
|
370
|
+
END_INSN(z)
|
371
|
+
}
|
372
|
+
if (issign(*p0)) si++;
|
373
|
+
READ_DIGITS(r, 2);
|
374
|
+
gmtoff = r * 60;
|
375
|
+
if (str[si] == ':') si++;
|
376
|
+
len = read_digits(&str[si], &r, 2);
|
377
|
+
if (len) {
|
378
|
+
si += len;
|
379
|
+
gmtoff += r;
|
380
|
+
}
|
381
|
+
gmtoff *= 60;
|
382
|
+
if (*p0 == '-') gmtoff = -gmtoff;
|
383
|
+
ADD_PC(1);
|
384
|
+
END_INSN(z)
|
385
|
+
}
|
386
|
+
INSN_ENTRY(_25)
|
387
|
+
{
|
388
|
+
ADD_PC(1);
|
389
|
+
END_INSN(_25)
|
390
|
+
}
|
391
|
+
INSN_ENTRY(_2b)
|
392
|
+
{
|
393
|
+
ADD_PC(1);
|
394
|
+
END_INSN(_2b)
|
395
|
+
}
|
396
|
+
INSN_ENTRY(_3a)
|
397
|
+
{
|
398
|
+
ADD_PC(1);
|
399
|
+
END_INSN(_3a)
|
400
|
+
}
|
401
|
+
INSN_ENTRY(_60)
|
402
|
+
{
|
403
|
+
size_t v = (size_t)GET_OPERAND(1);
|
404
|
+
size_t fi = v & 0xFFFF;
|
405
|
+
size_t cnt = v >> 16;
|
406
|
+
if (memcmp(str + si, fmt + fi, cnt)) return Qnil;
|
407
|
+
pc += 2;
|
408
|
+
si += cnt;
|
409
|
+
END_INSN(_60)
|
410
|
+
}
|
411
|
+
INSN_ENTRY(_5f)
|
412
|
+
{
|
413
|
+
struct timespec ts;
|
414
|
+
struct tm tm;
|
415
|
+
time_t t;
|
416
|
+
|
417
|
+
/* get current time with timezone */
|
418
|
+
timespec_now(&ts);
|
419
|
+
{
|
420
|
+
static time_t ct;
|
421
|
+
static struct tm ctm;
|
422
|
+
static long cgmtoff;
|
423
|
+
static long cloff;
|
424
|
+
long off;
|
425
|
+
if (ct == ts.tv_sec) {
|
426
|
+
off = cgmtoff;
|
427
|
+
}
|
428
|
+
else {
|
429
|
+
ct = ts.tv_sec;
|
430
|
+
localtime_with_gmtoff_zone(&ct, &ctm, &off, NULL);
|
431
|
+
cloff = off;
|
432
|
+
cgmtoff = INT_MAX;
|
433
|
+
}
|
434
|
+
if (gmtoff == INT_MAX) {
|
435
|
+
gmtoff = cloff;
|
436
|
+
}
|
437
|
+
if (gmtoff != off) {
|
438
|
+
tm_add_offset(&ctm, gmtoff - off);
|
439
|
+
}
|
440
|
+
memcpy(&tm, &ctm, sizeof(struct tm));
|
441
|
+
cgmtoff = gmtoff;
|
442
|
+
}
|
443
|
+
|
444
|
+
/* overwrite time */
|
445
|
+
if (year != INT_MAX) {
|
446
|
+
tm.tm_year = year - 1900;
|
447
|
+
if (mon == -1) mon = 1;
|
448
|
+
setmonth:
|
449
|
+
tm.tm_mon = mon - 1;
|
450
|
+
if (mday == -1) mday = 1;
|
451
|
+
setmday:
|
452
|
+
tm.tm_mday = mday;
|
453
|
+
if (hour == -1) hour = 0;
|
454
|
+
sethour:
|
455
|
+
tm.tm_hour = hour;
|
456
|
+
if (min == -1) min = 0;
|
457
|
+
setmin:
|
458
|
+
tm.tm_min = min;
|
459
|
+
if (sec == -1) sec = 0;
|
460
|
+
tm.tm_sec = sec;
|
461
|
+
}
|
462
|
+
else {
|
463
|
+
if (mon != -1) goto setmonth;
|
464
|
+
if (mday != -1) goto setmday;
|
465
|
+
if (hour != -1) goto sethour;
|
466
|
+
if (min != -1) goto setmin;
|
467
|
+
if (sec != -1) tm.tm_sec = sec;
|
468
|
+
}
|
469
|
+
|
470
|
+
{
|
471
|
+
static time_t ct;
|
472
|
+
static struct tm cache;
|
473
|
+
/* struct tm to time_t */
|
474
|
+
if (ct && cache.tm_year == tm.tm_year &&
|
475
|
+
cache.tm_mon == tm.tm_mon && cache.tm_mday == tm.tm_mday) {
|
476
|
+
t = ct + (tm.tm_hour - cache.tm_hour) * 3600 +
|
477
|
+
(tm.tm_min - cache.tm_min) * 60 +
|
478
|
+
(tm.tm_sec - cache.tm_sec);
|
479
|
+
}
|
480
|
+
else {
|
481
|
+
ct = t = timegm_noleapsecond(&tm);
|
482
|
+
memcpy((void *)&cache, &tm, sizeof(struct tm));
|
483
|
+
}
|
484
|
+
t -= gmtoff;
|
485
|
+
}
|
486
|
+
tsp->tv_sec = t;
|
487
|
+
tsp->tv_nsec = nsec;
|
488
|
+
*gmtoffp = gmtoff;
|
489
|
+
return 0;
|
490
|
+
END_INSN(_5f)
|
491
|
+
}
|
492
|
+
END_INSNS_DISPATCH();
|
493
|
+
|
494
|
+
/* unreachable */
|
495
|
+
rb_bug("vm_eval: unreachable");
|
496
|
+
goto first;
|
497
|
+
}
|
498
|
+
|
499
|
+
void **
|
500
|
+
strptime_compile(const char *fmt, size_t flen)
|
501
|
+
{
|
502
|
+
size_t fi = 0;
|
503
|
+
char c;
|
504
|
+
void **isns0 = ALLOC_N(void *, flen + 2);
|
505
|
+
void **isns = isns0;
|
506
|
+
void **insns_address_table;
|
507
|
+
void *tmp;
|
508
|
+
strptime_exec0((void **)&insns_address_table, NULL, NULL, 0, NULL, NULL);
|
509
|
+
|
510
|
+
while (fi < flen) {
|
511
|
+
switch (fmt[fi]) {
|
512
|
+
case '%':
|
513
|
+
fi++;
|
514
|
+
c = fmt[fi];
|
515
|
+
switch (c) {
|
516
|
+
case 'B':
|
517
|
+
case 'H':
|
518
|
+
case 'M':
|
519
|
+
case 'N':
|
520
|
+
case 'S':
|
521
|
+
case 'Y':
|
522
|
+
case 'b':
|
523
|
+
case 'd':
|
524
|
+
case 'e':
|
525
|
+
case 'h':
|
526
|
+
case 'm':
|
527
|
+
case 'n':
|
528
|
+
case 'z':
|
529
|
+
tmp = insns_address_table[c - 'A'];
|
530
|
+
if (tmp) {
|
531
|
+
*isns++ = tmp;
|
532
|
+
fi++;
|
533
|
+
continue;
|
534
|
+
}
|
535
|
+
default: rb_raise(rb_eArgError, "invalid format"); break;
|
536
|
+
}
|
537
|
+
case ' ':
|
538
|
+
case '\t':
|
539
|
+
case '\n':
|
540
|
+
case '\v':
|
541
|
+
case '\f':
|
542
|
+
case '\r':
|
543
|
+
*isns++ = insns_address_table['n' - 'A'];
|
544
|
+
fi++;
|
545
|
+
break;
|
546
|
+
default: {
|
547
|
+
const char *p0 = fmt + fi, *p = p0, *pe = fmt + flen;
|
548
|
+
size_t v = fi;
|
549
|
+
while (p < pe && *p != '%' && !ISSPACE(*p))
|
550
|
+
p++;
|
551
|
+
v += (p - p0) << 16;
|
552
|
+
fi += p - p0;
|
553
|
+
*isns++ = insns_address_table['`' - 'A'];
|
554
|
+
*isns++ = (void *)v;
|
555
|
+
} break;
|
556
|
+
}
|
557
|
+
}
|
558
|
+
*isns++ = insns_address_table['_' - 'A'];
|
559
|
+
REALLOC_N(isns0, void *, isns - isns0);
|
560
|
+
return isns0;
|
561
|
+
}
|
562
|
+
|
563
|
+
struct strptime_object {
|
564
|
+
void **isns;
|
565
|
+
VALUE fmt;
|
566
|
+
};
|
567
|
+
|
568
|
+
static void
|
569
|
+
strptime_mark(void *ptr)
|
570
|
+
{
|
571
|
+
struct strptime_object *tobj = ptr;
|
572
|
+
rb_gc_mark(tobj->fmt);
|
573
|
+
}
|
574
|
+
|
575
|
+
static size_t
|
576
|
+
strptime_memsize(const void *tobj)
|
577
|
+
{
|
578
|
+
return sizeof(struct strptime_object);
|
579
|
+
}
|
580
|
+
|
581
|
+
static const rb_data_type_t strptime_data_type = {
|
582
|
+
"strptime",
|
583
|
+
{
|
584
|
+
strptime_mark, RUBY_TYPED_DEFAULT_FREE, strptime_memsize,
|
585
|
+
},
|
586
|
+
#ifdef RUBY_TYPED_FREE_IMMEDIATELY
|
587
|
+
0,
|
588
|
+
0,
|
589
|
+
RUBY_TYPED_FREE_IMMEDIATELY
|
590
|
+
#endif
|
591
|
+
};
|
592
|
+
|
593
|
+
static VALUE
|
594
|
+
strptime_s_alloc(VALUE klass)
|
595
|
+
{
|
596
|
+
VALUE obj;
|
597
|
+
struct strptime_object *tobj;
|
598
|
+
|
599
|
+
obj = TypedData_Make_Struct(klass, struct strptime_object,
|
600
|
+
&strptime_data_type, tobj);
|
601
|
+
|
602
|
+
return obj;
|
603
|
+
}
|
604
|
+
|
605
|
+
static struct strptime_object *
|
606
|
+
get_strptimeval(VALUE obj)
|
607
|
+
{
|
608
|
+
struct strptime_object *tobj;
|
609
|
+
TypedData_Get_Struct(obj, struct strptime_object, &strptime_data_type,
|
610
|
+
tobj);
|
611
|
+
if (!STRPTIME_INIT_P(tobj)) {
|
612
|
+
rb_raise(rb_eTypeError, "uninitialized %" PRIsVALUE, rb_obj_class(obj));
|
613
|
+
}
|
614
|
+
return tobj;
|
615
|
+
}
|
616
|
+
|
617
|
+
static struct strptime_object *
|
618
|
+
get_new_strptimeval(VALUE obj)
|
619
|
+
{
|
620
|
+
struct strptime_object *tobj;
|
621
|
+
TypedData_Get_Struct(obj, struct strptime_object, &strptime_data_type,
|
622
|
+
tobj);
|
623
|
+
if (STRPTIME_INIT_P(tobj)) {
|
624
|
+
rb_raise(rb_eTypeError, "already initialized %" PRIsVALUE,
|
625
|
+
rb_obj_class(obj));
|
626
|
+
}
|
627
|
+
return tobj;
|
628
|
+
}
|
629
|
+
|
630
|
+
/*
|
631
|
+
* call-seq:
|
632
|
+
* Strptime.new(format) -> object
|
633
|
+
*
|
634
|
+
* returns parser object
|
635
|
+
*/
|
636
|
+
static VALUE
|
637
|
+
strptime_init(VALUE self, VALUE fmt)
|
638
|
+
{
|
639
|
+
struct strptime_object *tobj;
|
640
|
+
void **isns;
|
641
|
+
StringValue(fmt);
|
642
|
+
TypedData_Get_Struct(self, struct strptime_object, &strptime_data_type,
|
643
|
+
tobj);
|
644
|
+
isns = strptime_compile(RSTRING_PTR(fmt), RSTRING_LEN(fmt));
|
645
|
+
tobj->isns = isns;
|
646
|
+
tobj->fmt = rb_str_new_frozen(fmt);
|
647
|
+
return self;
|
648
|
+
}
|
649
|
+
|
650
|
+
/* :nodoc: */
|
651
|
+
static VALUE
|
652
|
+
strptime_init_copy(VALUE copy, VALUE self)
|
653
|
+
{
|
654
|
+
struct strptime_object *tobj, *tcopy;
|
655
|
+
|
656
|
+
if (!OBJ_INIT_COPY(copy, self)) return copy;
|
657
|
+
GetStrptimeval(self, tobj);
|
658
|
+
GetNewStrptimeval(copy, tcopy);
|
659
|
+
MEMCPY(tcopy, tobj, struct strptime_object, 1);
|
660
|
+
|
661
|
+
return copy;
|
662
|
+
}
|
663
|
+
|
664
|
+
typedef uint64_t WIDEVALUE;
|
665
|
+
typedef WIDEVALUE wideval_t;
|
666
|
+
#ifndef PACKED_STRUCT_UNALIGNED
|
667
|
+
#define PACKED_STRUCT_UNALIGNED(x) x
|
668
|
+
#endif
|
669
|
+
PACKED_STRUCT_UNALIGNED(struct vtm {
|
670
|
+
VALUE year; /* 2000 for example. Integer. */
|
671
|
+
VALUE subsecx; /* 0 <= subsecx < TIME_SCALE. possibly Rational. */
|
672
|
+
VALUE utc_offset; /* -3600 as -01:00 for example. possibly Rational. */
|
673
|
+
const char *zone; /* "JST", "EST", "EDT", etc. */
|
674
|
+
uint16_t yday : 9; /* 1..366 */
|
675
|
+
uint8_t mon : 4; /* 1..12 */
|
676
|
+
uint8_t mday : 5; /* 1..31 */
|
677
|
+
uint8_t hour : 5; /* 0..23 */
|
678
|
+
uint8_t min : 6; /* 0..59 */
|
679
|
+
uint8_t sec : 6; /* 0..60 */
|
680
|
+
uint8_t wday : 3; /* 0:Sunday, 1:Monday, ..., 6:Saturday 7:init */
|
681
|
+
uint8_t isdst : 2; /* 0:StandardTime 1:DayLightSavingTime 3:init */
|
682
|
+
});
|
683
|
+
PACKED_STRUCT_UNALIGNED(struct time_object {
|
684
|
+
wideval_t timew; /* time_t value * TIME_SCALE. possibly Rational. */
|
685
|
+
struct vtm vtm;
|
686
|
+
uint8_t gmt : 3; /* 0:utc 1:localtime 2:fixoff 3:init */
|
687
|
+
uint8_t tm_got : 1;
|
688
|
+
});
|
689
|
+
|
690
|
+
/*
|
691
|
+
* call-seq:
|
692
|
+
* Strptime#exec(str) -> time
|
693
|
+
*
|
694
|
+
*
|
695
|
+
*/
|
696
|
+
static VALUE
|
697
|
+
strptime_exec(VALUE self, VALUE str)
|
698
|
+
{
|
699
|
+
struct strptime_object *tobj;
|
700
|
+
int r, gmtoff = 0;
|
701
|
+
StringValue(str);
|
702
|
+
GetStrptimeval(self, tobj);
|
703
|
+
struct timespec ts;
|
704
|
+
|
705
|
+
r = strptime_exec0(tobj->isns, RSTRING_PTR(tobj->fmt), RSTRING_PTR(str),
|
706
|
+
RSTRING_LEN(str), &ts, &gmtoff);
|
707
|
+
if (r) rb_raise(rb_eArgError, "string doesn't match");
|
708
|
+
return rbtime_timespec_new(&ts, gmtoff);
|
709
|
+
}
|
710
|
+
|
711
|
+
/*
|
712
|
+
* call-seq:
|
713
|
+
* Strptime#execi(str) -> integer
|
714
|
+
*
|
715
|
+
* returns epoch as integer
|
716
|
+
*/
|
717
|
+
static VALUE
|
718
|
+
strptime_execi(VALUE self, VALUE str)
|
719
|
+
{
|
720
|
+
struct strptime_object *tobj;
|
721
|
+
struct timespec ts;
|
722
|
+
int r, gmtoff = 0;
|
723
|
+
StringValue(str);
|
724
|
+
GetStrptimeval(self, tobj);
|
725
|
+
|
726
|
+
r = strptime_exec0(tobj->isns, RSTRING_PTR(tobj->fmt), RSTRING_PTR(str),
|
727
|
+
RSTRING_LEN(str), &ts, &gmtoff);
|
728
|
+
if (r) rb_raise(rb_eArgError, "string doesn't match");
|
729
|
+
return TIMET2NUM(ts.tv_sec);
|
730
|
+
}
|
731
|
+
|
732
|
+
/*
|
733
|
+
* call-seq:
|
734
|
+
* Strptime#source -> string
|
735
|
+
*
|
736
|
+
* returns source format string
|
737
|
+
*/
|
738
|
+
static VALUE
|
739
|
+
strptime_source(VALUE self)
|
740
|
+
{
|
741
|
+
struct strptime_object *tobj;
|
742
|
+
GetStrptimeval(self, tobj);
|
743
|
+
|
744
|
+
return tobj->fmt;
|
745
|
+
}
|
746
|
+
|
747
|
+
/*
|
748
|
+
* Strptime is a faster way to parse time strings.
|
749
|
+
*
|
750
|
+
* parser = Strptime.new('%Y-%m-%dT%H:%M:%S%z')
|
751
|
+
* parser.exec('2015-12-25T12:34:56+09') #=> 2015-12-25 12:34:56 +09:00
|
752
|
+
* parser.execi('2015-12-25T12:34:56+09') #=> 1451014496
|
753
|
+
*/
|
754
|
+
void
|
755
|
+
Init_strptime(void)
|
756
|
+
{
|
757
|
+
rb_cStrptime = rb_define_class("Strptime", rb_cObject);
|
758
|
+
rb_define_alloc_func(rb_cStrptime, strptime_s_alloc);
|
759
|
+
rb_define_method(rb_cStrptime, "initialize", strptime_init, 1);
|
760
|
+
rb_define_method(rb_cStrptime, "initialize_copy", strptime_init_copy, 1);
|
761
|
+
rb_define_method(rb_cStrptime, "exec", strptime_exec, 1);
|
762
|
+
rb_define_method(rb_cStrptime, "execi", strptime_execi, 1);
|
763
|
+
rb_define_method(rb_cStrptime, "source", strptime_source, 0);
|
764
|
+
}
|