strptime 0.1.9 → 0.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
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: []