strptime 0.1.9 → 0.2.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3e58078405ddf596c330d736081aba86b05d7383
4
- data.tar.gz: 97a3bda8844dc1d304217fc8fc578454d2e0407c
3
+ metadata.gz: ac15db6d3cd5ce15e820662e1df5048358c90234
4
+ data.tar.gz: 31bd74fdff1ace3f17a6ffebdb27fb901e33bd30
5
5
  SHA512:
6
- metadata.gz: a9f0987398bdf252fb075747292c97d604ec6b66357c3c488355cd39096a44f85e61749ba9e9b5c7f958cf87344bd8d03a96bc92ba852091e01716ad5e1017cc
7
- data.tar.gz: 47d30ca890fadd651314aeb6474f3637bacb9ec0496300badb36879cc34e15cfc0878c0b906f0f3c3f588d6215a80c547be2988b2c1ae80ab928291dc0647177
6
+ metadata.gz: b58b8e9916945a3df04942cfbf637e2618daf21dbf1cd37500990963a9ad561178b6e30d729ff694c1a5275e80e552952239a5cfc142d0da5a884f7c64e350d7
7
+ data.tar.gz: a1e412ff5dd24c54ad4dfdfb77fd281dfe7b29a8e7f56e761fe7155c5fb6b2409e249474b441e19fce95015784de507fca2482248f4b86296a5259784d68609f
@@ -147,7 +147,7 @@ rb_localtime_r(const time_t *t, struct tm *result)
147
147
  }
148
148
  #define LOCALTIME(tm, result) (tzset(),rb_localtime_r((tm), &(result)))
149
149
 
150
- static struct tm *
150
+ struct tm *
151
151
  rb_gmtime_r(const time_t *t, struct tm *result)
152
152
  {
153
153
  #ifdef HAVE_GMTIME_R
@@ -0,0 +1,462 @@
1
+ #include "../strptime/strptime.h"
2
+ #include "ruby/encoding.h"
3
+ #include <time.h>
4
+
5
+ VALUE rb_cStrftime;
6
+
7
+ #define GetStrftimeval(obj, tobj) ((tobj) = get_strftimeval(obj))
8
+ #define GetNewStrftimeval(obj, tobj) ((tobj) = get_new_strftimeval(obj))
9
+ #define StrfTIME_INIT_P(tobj) ((tobj)->isns)
10
+
11
+ #define LIKELY(x) (__builtin_expect((x), 1))
12
+ #define UNLIKELY(x) (__builtin_expect((x), 0))
13
+
14
+ #define REG_PC (pc)
15
+ #define GET_PC() REG_PC
16
+ #define SET_PC(x) (REG_PC = (x))
17
+ #define GET_CURRENT_INSN() (*GET_PC())
18
+ #define GET_OPERAND(n) (GET_PC()[(n)])
19
+ #define ADD_PC(n) (SET_PC(REG_PC + (n)))
20
+
21
+ #define JUMP(dst) (REG_PC += (dst))
22
+
23
+ #define LABEL(x) INSN_LABEL_##x
24
+ #define ELABEL(x) INSN_ELABEL_##x
25
+ #define LABEL_PTR(x) &&LABEL(x)
26
+
27
+ #define INSN_ENTRY(insn) LABEL(insn) :
28
+
29
+ #define TC_DISPATCH(insn) \
30
+ goto *(void const *)GET_CURRENT_INSN(); \
31
+ ;
32
+ #define END_INSN(insn) TC_DISPATCH(insn);
33
+
34
+ #define INSN_DISPATCH() \
35
+ TC_DISPATCH(__START__) \
36
+ {
37
+
38
+ #define END_INSNS_DISPATCH() \
39
+ rb_bug("strptime: unknown insn: %p", GET_CURRENT_INSN()); \
40
+ } /* end of while loop */
41
+
42
+ #define NEXT_INSN() TC_DISPATCH(__NEXT_INSN__)
43
+
44
+
45
+
46
+ static VALUE
47
+ strftime_exec0(void **pc, VALUE fmt, struct timespec *tsp, int gmtoff, size_t result_length)
48
+ {
49
+ VALUE result;
50
+ struct tm tm;
51
+ char *p;
52
+ if (UNLIKELY(tsp == NULL)) {
53
+ static const void *const insns_address_table[] = {
54
+ NULL, NULL, NULL, NULL,
55
+ NULL, NULL, NULL, LABEL_PTR(H),
56
+ NULL, NULL, NULL, LABEL_PTR(L),
57
+ LABEL_PTR(M), LABEL_PTR(N), NULL, NULL,
58
+ NULL, NULL, LABEL_PTR(S), NULL,
59
+ NULL, NULL, NULL, NULL,
60
+ LABEL_PTR(Y), NULL, NULL, NULL,
61
+ NULL, NULL, LABEL_PTR(_5f), LABEL_PTR(_60),
62
+ NULL, NULL, NULL, LABEL_PTR(d),
63
+ LABEL_PTR(d), NULL, NULL, NULL,
64
+ NULL, NULL, NULL, NULL,
65
+ LABEL_PTR(m), NULL, NULL, NULL,
66
+ NULL, NULL, NULL, NULL,
67
+ NULL, NULL, NULL, NULL,
68
+ LABEL_PTR(y), NULL,
69
+ };
70
+ *pc = (void *)insns_address_table;
71
+ return Qnil;
72
+ }
73
+
74
+ result = rb_str_new(NULL, result_length + 1);
75
+ p = RSTRING_PTR(result);
76
+
77
+ rb_gmtime_r(&tsp->tv_sec, &tm);
78
+
79
+ INSN_DISPATCH();
80
+ INSN_ENTRY(H)
81
+ {
82
+ *p++ = '0' + (tm.tm_hour / 10);
83
+ *p++ = '0' + (tm.tm_hour % 10);
84
+ ADD_PC(1);
85
+ END_INSN(H)
86
+ }
87
+ INSN_ENTRY(L)
88
+ {
89
+ int msec = tsp->tv_nsec / 1000000;
90
+ p[2] = '0' + (msec % 10);
91
+ msec /= 10;
92
+ p[1] = '0' + (msec % 10);
93
+ p[0] = '0' + (msec / 10);
94
+ p += 3;
95
+ ADD_PC(1);
96
+ END_INSN(L)
97
+ }
98
+ INSN_ENTRY(M)
99
+ {
100
+ *p++ = '0' + (tm.tm_min / 10);
101
+ *p++ = '0' + (tm.tm_min % 10);
102
+ ADD_PC(1);
103
+ END_INSN(M)
104
+ }
105
+ INSN_ENTRY(N)
106
+ {
107
+ int len = 9;
108
+ int i;
109
+ int base = 1;
110
+ int subsec = tsp->tv_nsec;
111
+ for (i=0; i < 9-len; i++) {
112
+ base *= 10;
113
+ }
114
+ subsec /= base;
115
+ for (i=0; i < len; i++) {
116
+ p[len-i-1] = '0' + subsec % 10;
117
+ subsec /= 10;
118
+ }
119
+ p += len;
120
+ ADD_PC(1);
121
+ END_INSN(N)
122
+ }
123
+ INSN_ENTRY(S)
124
+ {
125
+ *p++ = '0' + (tm.tm_sec / 10);
126
+ *p++ = '0' + (tm.tm_sec % 10);
127
+ ADD_PC(1);
128
+ END_INSN(S)
129
+ }
130
+ INSN_ENTRY(Y)
131
+ {
132
+ // TODO: Y10K
133
+ int i, y = tm.tm_year;
134
+ y += y < 69 ? 2000 : 1900;
135
+ for (i = 0; i < 4; i++) {
136
+ p[3-i] = '0' + y % 10;
137
+ y /= 10;
138
+ }
139
+ p += 4;
140
+ ADD_PC(1);
141
+ END_INSN(Y)
142
+ }
143
+ INSN_ENTRY(d)
144
+ {
145
+ *p++ = '0' + (tm.tm_mday / 10);
146
+ *p++ = '0' + (tm.tm_mday % 10);
147
+ ADD_PC(1);
148
+ END_INSN(d)
149
+ }
150
+ INSN_ENTRY(m)
151
+ {
152
+ int mon = tm.tm_mon + 1;
153
+ *p++ = '0' + (mon / 10);
154
+ *p++ = '0' + (mon % 10);
155
+ ADD_PC(1);
156
+ END_INSN(m)
157
+ }
158
+ INSN_ENTRY(y)
159
+ {
160
+ int y = tm.tm_year % 100;
161
+ *p++ = '0' + (y / 10);
162
+ *p++ = '0' + (y % 10);
163
+ ADD_PC(1);
164
+ END_INSN(y)
165
+ }
166
+ INSN_ENTRY(_60)
167
+ {
168
+ size_t v = (size_t)GET_OPERAND(1);
169
+ size_t off = v & 0xFFFF;
170
+ size_t len = v >> 16;
171
+ memcpy(p, RSTRING_PTR(fmt) + off, len);
172
+ p += len;
173
+ pc += 2;
174
+ END_INSN(_60)
175
+ }
176
+ INSN_ENTRY(_5f)
177
+ {
178
+ return result;
179
+ END_INSN(_5f)
180
+ }
181
+ END_INSNS_DISPATCH();
182
+
183
+ /* unreachable */
184
+ rb_bug("strftime_exec0: unreachable");
185
+ UNREACHABLE;
186
+ }
187
+
188
+ static void **
189
+ strftime_compile(const char *fmt, size_t flen, size_t *rlenp)
190
+ {
191
+ size_t fi = 0, rlen = 0;
192
+ char c;
193
+ void **isns0, **isns;
194
+ void **insns_address_table;
195
+ void *tmp;
196
+ strftime_exec0((void **)&insns_address_table, Qnil, NULL, 0, 0);
197
+
198
+ if (flen > 65535) {
199
+ rb_raise(rb_eArgError, "too long format string (>65335)");
200
+ }
201
+ isns0 = ALLOC_N(void *, flen + 2);
202
+ isns = isns0;
203
+
204
+ while (fi < flen) {
205
+ switch (fmt[fi]) {
206
+ case '%':
207
+ fi++;
208
+ c = fmt[fi];
209
+ switch (c) {
210
+ case 'H':
211
+ rlen += 2;
212
+ goto accept_format;
213
+ case 'L':
214
+ rlen += 3;
215
+ goto accept_format;
216
+ case 'M':
217
+ rlen += 2;
218
+ goto accept_format;
219
+ case 'N':
220
+ rlen += 9;
221
+ goto accept_format;
222
+ case 'S':
223
+ rlen += 2;
224
+ goto accept_format;
225
+ case 'Y':
226
+ rlen += 4;
227
+ goto accept_format;
228
+ case 'd':
229
+ rlen += 2;
230
+ goto accept_format;
231
+ case 'm':
232
+ rlen += 2;
233
+ goto accept_format;
234
+ case 'y':
235
+ rlen += 2;
236
+ accept_format:
237
+ tmp = insns_address_table[c - 'A'];
238
+ if (tmp) {
239
+ *isns++ = tmp;
240
+ fi++;
241
+ continue;
242
+ }
243
+ default:
244
+ xfree(isns0);
245
+ rb_raise(rb_eArgError, "invalid format");
246
+ break;
247
+ }
248
+ default: {
249
+ const char *p0 = fmt + fi, *p = p0, *pe = fmt + flen;
250
+ size_t v = fi;
251
+ while (p < pe && *p != '%')
252
+ p++;
253
+ v += (p - p0) << 16;
254
+ fi += p - p0;
255
+ rlen += p - p0;
256
+ *isns++ = insns_address_table['`' - 'A'];
257
+ *isns++ = (void *)v;
258
+ } break;
259
+ }
260
+ }
261
+ *isns++ = insns_address_table['_' - 'A'];
262
+ REALLOC_N(isns0, void *, isns - isns0);
263
+ *rlenp = rlen;
264
+ return isns0;
265
+ }
266
+
267
+ struct strftime_object {
268
+ void **isns;
269
+ size_t result_length;
270
+ VALUE fmt;
271
+ };
272
+
273
+ static void
274
+ strftime_mark(void *ptr)
275
+ {
276
+ struct strftime_object *tobj = ptr;
277
+ rb_gc_mark(tobj->fmt);
278
+ }
279
+
280
+ static void
281
+ strftime_free(void *ptr)
282
+ {
283
+ struct strftime_object *tobj = ptr;
284
+ if (tobj->isns) ruby_xfree(tobj->isns);
285
+ }
286
+
287
+ static size_t
288
+ strftime_memsize(const void *tobj)
289
+ {
290
+ return sizeof(struct strftime_object);
291
+ }
292
+
293
+ static const rb_data_type_t strftime_data_type = {
294
+ "strftime",
295
+ {
296
+ strftime_mark, strftime_free, strftime_memsize,
297
+ },
298
+ #ifdef RUBY_TYPED_FREE_IMMEDIATELY
299
+ 0,
300
+ 0,
301
+ RUBY_TYPED_FREE_IMMEDIATELY
302
+ #endif
303
+ };
304
+
305
+ static VALUE
306
+ strftime_s_alloc(VALUE klass)
307
+ {
308
+ VALUE obj;
309
+ struct strftime_object *tobj;
310
+
311
+ obj = TypedData_Make_Struct(klass, struct strftime_object,
312
+ &strftime_data_type, tobj);
313
+
314
+ return obj;
315
+ }
316
+
317
+ static struct strftime_object *
318
+ get_strftimeval(VALUE obj)
319
+ {
320
+ struct strftime_object *tobj;
321
+ TypedData_Get_Struct(obj, struct strftime_object, &strftime_data_type,
322
+ tobj);
323
+ if (!StrfTIME_INIT_P(tobj)) {
324
+ rb_raise(rb_eTypeError, "uninitialized %" PRIsVALUE, rb_obj_class(obj));
325
+ }
326
+ return tobj;
327
+ }
328
+
329
+ static struct strftime_object *
330
+ get_new_strftimeval(VALUE obj)
331
+ {
332
+ struct strftime_object *tobj;
333
+ TypedData_Get_Struct(obj, struct strftime_object, &strftime_data_type,
334
+ tobj);
335
+ if (StrfTIME_INIT_P(tobj)) {
336
+ rb_raise(rb_eTypeError, "already initialized %" PRIsVALUE,
337
+ rb_obj_class(obj));
338
+ }
339
+ return tobj;
340
+ }
341
+
342
+ /*
343
+ * @overload new(format)
344
+ * @param format [String] strftime(3) style format string.
345
+ *
346
+ * returns generator object
347
+ */
348
+ static VALUE
349
+ strftime_init(VALUE self, VALUE fmt)
350
+ {
351
+ struct strftime_object *tobj;
352
+ void **isns;
353
+ size_t rlen;
354
+ StringValueCStr(fmt);
355
+ TypedData_Get_Struct(self, struct strftime_object, &strftime_data_type,
356
+ tobj);
357
+ isns = strftime_compile(RSTRING_PTR(fmt), RSTRING_LEN(fmt), &rlen);
358
+ tobj->isns = isns;
359
+ tobj->fmt = rb_str_new_frozen(fmt);
360
+ tobj->result_length = rlen;
361
+ return self;
362
+ }
363
+
364
+ /* @api private
365
+ * For Ruby VM internal.
366
+ */
367
+ static VALUE
368
+ strftime_init_copy(VALUE copy, VALUE self)
369
+ {
370
+ struct strftime_object *tobj, *tcopy;
371
+
372
+ if (!OBJ_INIT_COPY(copy, self)) return copy;
373
+ GetStrftimeval(self, tobj);
374
+ GetNewStrftimeval(copy, tcopy);
375
+ MEMCPY(tcopy, tobj, struct strftime_object, 1);
376
+
377
+ return copy;
378
+ }
379
+
380
+ /*
381
+ * @overload exec(str)
382
+ * @param str [String] string to parse
383
+ * @return [Time] the time object given string means
384
+ *
385
+ * Return a formatted datetime string
386
+ *
387
+ */
388
+ static VALUE
389
+ strftime_exec(VALUE self, VALUE time)
390
+ {
391
+ struct strftime_object *tobj;
392
+ struct timespec ts = rb_time_timespec(time);
393
+ GetStrftimeval(self, tobj);
394
+
395
+ return strftime_exec0(tobj->isns, tobj->fmt, &ts, 0, tobj->result_length);
396
+ }
397
+
398
+ /*
399
+ * @overload execi(epoch)
400
+ * @param epoch [Integer] Unix epoch
401
+ * @return [String] the formatted datetime string
402
+ *
403
+ * Return a formatted datetime string
404
+ *
405
+ */
406
+ static VALUE
407
+ strftime_execi(VALUE self, VALUE epoch)
408
+ {
409
+ struct strftime_object *tobj;
410
+ struct timespec ts;
411
+ GetStrftimeval(self, tobj);
412
+
413
+ if (RB_INTEGER_TYPE_P(epoch)) {
414
+ ts.tv_sec = NUM2TIMET(epoch);
415
+ ts.tv_nsec = 0;
416
+ } else if (RB_FLOAT_TYPE_P(epoch)) {
417
+ double d = NUM2DBL(epoch);
418
+ ts.tv_sec = (time_t)d;
419
+ ts.tv_nsec = (int)((int64_t)(d * 1000000000) % 1000000000);
420
+ } else if (RB_TYPE_P(epoch, T_RATIONAL)) {
421
+ ts.tv_sec = NUM2TIMET(epoch);
422
+ ts.tv_nsec = NUM2INT(rb_funcall(rb_funcall(epoch, '*', 1, INT2FIX(1000000000)), '%', 1, INT2FIX(1000000000)));
423
+ }
424
+
425
+ return strftime_exec0(tobj->isns, tobj->fmt, &ts, 0, tobj->result_length);
426
+ }
427
+
428
+ /*
429
+ * @overload source
430
+ * @return [String] source format string
431
+ */
432
+ static VALUE
433
+ strftime_source(VALUE self)
434
+ {
435
+ struct strftime_object *tobj;
436
+ GetStrftimeval(self, tobj);
437
+
438
+ return tobj->fmt;
439
+ }
440
+
441
+
442
+ /*
443
+ * Document-class: Strftime
444
+ *
445
+ * Strftime is a faster way to format time string like strftime(3).
446
+ *
447
+ * @example
448
+ * generator = Strftime.new('%Y-%m-%dT%H:%M:%S%z')
449
+ * generator.source #=> "%Y-%m-%dT%H:%M:%S%z"
450
+ * generator.exec(Time.now) #=> 2017-12-25T12:34:56+09:00
451
+ */
452
+ void
453
+ Init_strftime(void)
454
+ {
455
+ rb_cStrftime = rb_define_class("Strftime", rb_cObject);
456
+ rb_define_alloc_func(rb_cStrftime, strftime_s_alloc);
457
+ rb_define_method(rb_cStrftime, "initialize", strftime_init, 1);
458
+ rb_define_method(rb_cStrftime, "initialize_copy", strftime_init_copy, 1);
459
+ rb_define_method(rb_cStrftime, "exec", strftime_exec, 1);
460
+ rb_define_method(rb_cStrftime, "execi", strftime_execi, 1);
461
+ rb_define_method(rb_cStrftime, "source", strftime_source, 0);
462
+ }
@@ -481,7 +481,7 @@ strptime_exec0(void **pc, const char *fmt, const char *str, size_t slen,
481
481
  UNREACHABLE;
482
482
  }
483
483
 
484
- void **
484
+ static void **
485
485
  strptime_compile(const char *fmt, size_t flen)
486
486
  {
487
487
  size_t fi = 0;
@@ -518,7 +518,10 @@ strptime_compile(const char *fmt, size_t flen)
518
518
  fi++;
519
519
  continue;
520
520
  }
521
- default: rb_raise(rb_eArgError, "invalid format"); break;
521
+ default:
522
+ xfree(isns0);
523
+ rb_raise(rb_eArgError, "invalid format");
524
+ break;
522
525
  }
523
526
  case ' ':
524
527
  case '\t':
@@ -562,7 +565,7 @@ static void
562
565
  strptime_free(void *ptr)
563
566
  {
564
567
  struct strptime_object *tobj = ptr;
565
- ruby_xfree(tobj->isns);
568
+ if (tobj->isns) ruby_xfree(tobj->isns);
566
569
  }
567
570
 
568
571
  static size_t
@@ -735,4 +738,5 @@ Init_strptime(void)
735
738
  rb_define_method(rb_cStrptime, "exec", strptime_exec, 1);
736
739
  rb_define_method(rb_cStrptime, "execi", strptime_execi, 1);
737
740
  rb_define_method(rb_cStrptime, "source", strptime_source, 0);
741
+ Init_strftime();
738
742
  }
@@ -12,5 +12,7 @@ void rb_timespec_now(struct timespec *ts);
12
12
  time_t timegm_noleapsecond(struct tm *tm);
13
13
  const char *find_time_t(struct tm *tptr, int utc_p, time_t *tp);
14
14
  void tm_add_offset(struct tm *tm, long diff);
15
+ struct tm *rb_gmtime_r(const time_t *t, struct tm *result);
16
+ void Init_strftime(void);
15
17
 
16
18
  #endif /* STRPTIME_H */
@@ -1,3 +1,3 @@
1
1
  class Strptime
2
- VERSION = "0.1.9"
2
+ VERSION = "0.2.0.beta1"
3
3
  end
@@ -9,8 +9,8 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["NARUSE, Yui"]
10
10
  spec.email = ["naruse@airemix.jp"]
11
11
 
12
- spec.summary = %q{a fast strptime engine.}
13
- spec.description = %q{a fast strptime engine which uses VM.}
12
+ spec.summary = %q{a fast strptime/strftime engine.}
13
+ spec.description = %q{a fast strptime/strftime engine which uses VM.}
14
14
  spec.homepage = "https://github.com/nurse/strptime"
15
15
  spec.license = "BSD-2-Clause"
16
16
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: strptime
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.9
4
+ version: 0.2.0.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - NARUSE, Yui
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-01-11 00:00:00.000000000 Z
11
+ date: 2017-11-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -94,7 +94,7 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
- description: a fast strptime engine which uses VM.
97
+ description: a fast strptime/strftime engine which uses VM.
98
98
  email:
99
99
  - naruse@airemix.jp
100
100
  executables: []
@@ -115,6 +115,7 @@ files:
115
115
  - bin/setup
116
116
  - ext/strptime/extconf.rb
117
117
  - ext/strptime/ruby_time.c
118
+ - ext/strptime/strftime.c
118
119
  - ext/strptime/strptime.c
119
120
  - ext/strptime/strptime.h
120
121
  - lib/strptime.rb
@@ -135,13 +136,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
135
136
  version: '2.0'
136
137
  required_rubygems_version: !ruby/object:Gem::Requirement
137
138
  requirements:
138
- - - ">="
139
+ - - ">"
139
140
  - !ruby/object:Gem::Version
140
- version: '0'
141
+ version: 1.3.1
141
142
  requirements: []
142
143
  rubyforge_project:
143
- rubygems_version: 2.6.8
144
+ rubygems_version: 2.6.11
144
145
  signing_key:
145
146
  specification_version: 4
146
- summary: a fast strptime engine.
147
+ summary: a fast strptime/strftime engine.
147
148
  test_files: []