source: EcnlProtoTool/trunk/mruby-1.2.0/mrbgems/mruby-string-ext/src/string.c@ 270

Last change on this file since 270 was 270, checked in by coas-nagasima, 7 years ago

mruby版ECNLプロトタイピング・ツールを追加

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
  • Property svn:mime-type set to text/x-csrc
File size: 14.5 KB
Line 
1#include <string.h>
2#include "mruby.h"
3#include "mruby/array.h"
4#include "mruby/class.h"
5#include "mruby/string.h"
6#include "mruby/range.h"
7
8static mrb_value
9mrb_str_getbyte(mrb_state *mrb, mrb_value str)
10{
11 mrb_int pos;
12 mrb_get_args(mrb, "i", &pos);
13
14 if (pos < 0)
15 pos += RSTRING_LEN(str);
16 if (pos < 0 || RSTRING_LEN(str) <= pos)
17 return mrb_nil_value();
18
19 return mrb_fixnum_value((unsigned char)RSTRING_PTR(str)[pos]);
20}
21
22static mrb_value
23mrb_str_setbyte(mrb_state *mrb, mrb_value str)
24{
25 mrb_int pos, byte;
26 long len = RSTRING_LEN(str);
27
28 mrb_get_args(mrb, "ii", &pos, &byte);
29
30 if (pos < -len || len <= pos)
31 mrb_raisef(mrb, E_INDEX_ERROR, "index %S is out of array", mrb_fixnum_value(pos));
32 if (pos < 0)
33 pos += len;
34
35 mrb_str_modify(mrb, mrb_str_ptr(str));
36 byte &= 0xff;
37 RSTRING_PTR(str)[pos] = byte;
38 return mrb_fixnum_value((unsigned char)byte);
39}
40
41static mrb_value
42mrb_str_byteslice(mrb_state *mrb, mrb_value str)
43{
44 mrb_value a1;
45 mrb_int len;
46 int argc;
47
48 argc = mrb_get_args(mrb, "o|i", &a1, &len);
49 if (argc == 2) {
50 return mrb_str_substr(mrb, str, mrb_fixnum(a1), len);
51 }
52 switch (mrb_type(a1)) {
53 case MRB_TT_RANGE:
54 {
55 mrb_int beg;
56
57 len = RSTRING_LEN(str);
58 if (mrb_range_beg_len(mrb, a1, &beg, &len, len)) {
59 return mrb_str_substr(mrb, str, beg, len);
60 }
61 return mrb_nil_value();
62 }
63 case MRB_TT_FLOAT:
64 a1 = mrb_fixnum_value((mrb_int)mrb_float(a1));
65 /* fall through */
66 case MRB_TT_FIXNUM:
67 return mrb_str_substr(mrb, str, mrb_fixnum(a1), 1);
68 default:
69 mrb_raise(mrb, E_TYPE_ERROR, "wrong type of argument");
70 }
71 /* not reached */
72 return mrb_nil_value();
73}
74
75/*
76 * call-seq:
77 * str.swapcase! -> str or nil
78 *
79 * Equivalent to <code>String#swapcase</code>, but modifies the receiver in
80 * place, returning <i>str</i>, or <code>nil</code> if no changes were made.
81 * Note: case conversion is effective only in ASCII region.
82 */
83static mrb_value
84mrb_str_swapcase_bang(mrb_state *mrb, mrb_value str)
85{
86 char *p, *pend;
87 int modify = 0;
88 struct RString *s = mrb_str_ptr(str);
89
90 mrb_str_modify(mrb, s);
91 p = RSTRING_PTR(str);
92 pend = p + RSTRING_LEN(str);
93 while (p < pend) {
94 if (ISUPPER(*p)) {
95 *p = TOLOWER(*p);
96 modify = 1;
97 }
98 else if (ISLOWER(*p)) {
99 *p = TOUPPER(*p);
100 modify = 1;
101 }
102 p++;
103 }
104
105 if (modify) return str;
106 return mrb_nil_value();
107}
108
109/*
110 * call-seq:
111 * str.swapcase -> new_str
112 *
113 * Returns a copy of <i>str</i> with uppercase alphabetic characters converted
114 * to lowercase and lowercase characters converted to uppercase.
115 * Note: case conversion is effective only in ASCII region.
116 *
117 * "Hello".swapcase #=> "hELLO"
118 * "cYbEr_PuNk11".swapcase #=> "CyBeR_pUnK11"
119 */
120static mrb_value
121mrb_str_swapcase(mrb_state *mrb, mrb_value self)
122{
123 mrb_value str;
124
125 str = mrb_str_dup(mrb, self);
126 mrb_str_swapcase_bang(mrb, str);
127 return str;
128}
129
130/*
131 * call-seq:
132 * str << integer -> str
133 * str.concat(integer) -> str
134 * str << obj -> str
135 * str.concat(obj) -> str
136 *
137 * Append---Concatenates the given object to <i>str</i>. If the object is a
138 * <code>Integer</code>, it is considered as a codepoint, and is converted
139 * to a character before concatenation.
140 *
141 * a = "hello "
142 * a << "world" #=> "hello world"
143 * a.concat(33) #=> "hello world!"
144 */
145static mrb_value
146mrb_str_concat2(mrb_state *mrb, mrb_value self)
147{
148 mrb_value str;
149 mrb_get_args(mrb, "S", &str);
150 mrb_str_concat(mrb, self, str);
151 return self;
152}
153
154/*
155 * call-seq:
156 * str.start_with?([prefixes]+) -> true or false
157 *
158 * Returns true if +str+ starts with one of the +prefixes+ given.
159 *
160 * "hello".start_with?("hell") #=> true
161 *
162 * # returns true if one of the prefixes matches.
163 * "hello".start_with?("heaven", "hell") #=> true
164 * "hello".start_with?("heaven", "paradise") #=> false
165 * "h".start_with?("heaven", "hell") #=> false
166 */
167static mrb_value
168mrb_str_start_with(mrb_state *mrb, mrb_value self)
169{
170 mrb_value *argv, sub;
171 mrb_int argc, i;
172 mrb_get_args(mrb, "*", &argv, &argc);
173
174 for (i = 0; i < argc; i++) {
175 size_t len_l, len_r;
176 int ai = mrb_gc_arena_save(mrb);
177 sub = mrb_string_type(mrb, argv[i]);
178 mrb_gc_arena_restore(mrb, ai);
179 len_l = RSTRING_LEN(self);
180 len_r = RSTRING_LEN(sub);
181 if (len_l >= len_r) {
182 if (memcmp(RSTRING_PTR(self), RSTRING_PTR(sub), len_r) == 0) {
183 return mrb_true_value();
184 }
185 }
186 }
187 return mrb_false_value();
188}
189
190/*
191 * call-seq:
192 * str.end_with?([suffixes]+) -> true or false
193 *
194 * Returns true if +str+ ends with one of the +suffixes+ given.
195 */
196static mrb_value
197mrb_str_end_with(mrb_state *mrb, mrb_value self)
198{
199 mrb_value *argv, sub;
200 mrb_int argc, i;
201 mrb_get_args(mrb, "*", &argv, &argc);
202
203 for (i = 0; i < argc; i++) {
204 size_t len_l, len_r;
205 int ai = mrb_gc_arena_save(mrb);
206 sub = mrb_string_type(mrb, argv[i]);
207 mrb_gc_arena_restore(mrb, ai);
208 len_l = RSTRING_LEN(self);
209 len_r = RSTRING_LEN(sub);
210 if (len_l >= len_r) {
211 if (memcmp(RSTRING_PTR(self) + (len_l - len_r),
212 RSTRING_PTR(sub),
213 len_r) == 0) {
214 return mrb_true_value();
215 }
216 }
217 }
218 return mrb_false_value();
219}
220
221static mrb_value
222mrb_str_hex(mrb_state *mrb, mrb_value self)
223{
224 return mrb_str_to_inum(mrb, self, 16, FALSE);
225}
226
227static mrb_value
228mrb_str_oct(mrb_state *mrb, mrb_value self)
229{
230 return mrb_str_to_inum(mrb, self, 8, FALSE);
231}
232
233/*
234 * call-seq:
235 * string.chr -> string
236 *
237 * Returns a one-character string at the beginning of the string.
238 *
239 * a = "abcde"
240 * a.chr #=> "a"
241 */
242static mrb_value
243mrb_str_chr(mrb_state *mrb, mrb_value self)
244{
245 return mrb_str_substr(mrb, self, 0, 1);
246}
247
248static mrb_value
249mrb_fixnum_chr(mrb_state *mrb, mrb_value num)
250{
251 mrb_int cp = mrb_fixnum(num);
252#ifdef MRB_UTF8_STRING
253 char utf8[4];
254 mrb_int len;
255
256 if (cp < 0 || 0x10FFFF < cp) {
257 mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", num);
258 }
259 if (cp < 0x80) {
260 utf8[0] = (char)cp;
261 len = 1;
262 }
263 else if (cp < 0x800) {
264 utf8[0] = (char)(0xC0 | (cp >> 6));
265 utf8[1] = (char)(0x80 | (cp & 0x3F));
266 len = 2;
267 }
268 else if (cp < 0x10000) {
269 utf8[0] = (char)(0xE0 | (cp >> 12));
270 utf8[1] = (char)(0x80 | ((cp >> 6) & 0x3F));
271 utf8[2] = (char)(0x80 | ( cp & 0x3F));
272 len = 3;
273 }
274 else {
275 utf8[0] = (char)(0xF0 | (cp >> 18));
276 utf8[1] = (char)(0x80 | ((cp >> 12) & 0x3F));
277 utf8[2] = (char)(0x80 | ((cp >> 6) & 0x3F));
278 utf8[3] = (char)(0x80 | ( cp & 0x3F));
279 len = 4;
280 }
281 return mrb_str_new(mrb, utf8, len);
282#else
283 char c;
284
285 if (cp < 0 || 0xff < cp) {
286 mrb_raisef(mrb, E_RANGE_ERROR, "%S out of char range", num);
287 }
288 c = (char)cp;
289 return mrb_str_new(mrb, &c, 1);
290#endif
291}
292
293/*
294 * call-seq:
295 * string.lines -> array of string
296 *
297 * Returns strings per line;
298 *
299 * a = "abc\ndef"
300 * a.lines #=> ["abc\n", "def"]
301 */
302static mrb_value
303mrb_str_lines(mrb_state *mrb, mrb_value self)
304{
305 mrb_value result;
306 mrb_value blk;
307 int ai;
308 mrb_int len;
309 mrb_value arg;
310 char *p = RSTRING_PTR(self), *t;
311 char *e = p + RSTRING_LEN(self);
312
313 mrb_get_args(mrb, "&", &blk);
314
315 result = mrb_ary_new(mrb);
316
317 if (!mrb_nil_p(blk)) {
318 while (p < e) {
319 t = p;
320 while (p < e && *p != '\n') p++;
321 if (*p == '\n') p++;
322 len = (mrb_int) (p - t);
323 arg = mrb_str_new(mrb, t, len);
324 mrb_yield_argv(mrb, blk, 1, &arg);
325 }
326 return self;
327 }
328 while (p < e) {
329 ai = mrb_gc_arena_save(mrb);
330 t = p;
331 while (p < e && *p != '\n') p++;
332 if (*p == '\n') p++;
333 len = (mrb_int) (p - t);
334 mrb_ary_push(mrb, result, mrb_str_new(mrb, t, len));
335 mrb_gc_arena_restore(mrb, ai);
336 }
337 return result;
338}
339
340/*
341 * call-seq:
342 * string.succ -> string
343 *
344 * Returns next sequence of the string;
345 *
346 * a = "abc"
347 * a.succ #=> "abd"
348 */
349static mrb_value
350mrb_str_succ_bang(mrb_state *mrb, mrb_value self)
351{
352 mrb_value result;
353 unsigned char *p, *e, *b, *t;
354 const char *prepend;
355 struct RString *s = mrb_str_ptr(self);
356 size_t l;
357
358 if (RSTRING_LEN(self) == 0)
359 return self;
360
361 mrb_str_modify(mrb, s);
362 l = RSTRING_LEN(self);
363 b = p = (unsigned char*) RSTRING_PTR(self);
364 t = e = p + l;
365 *(e--) = 0;
366
367 // find trailing ascii/number
368 while (e >= b) {
369 if (ISALNUM(*e))
370 break;
371 e--;
372 }
373 if (e < b) {
374 e = p + l - 1;
375 result = mrb_str_new_lit(mrb, "");
376 } else {
377 // find leading letter of the ascii/number
378 b = e;
379 while (b > p) {
380 if (!ISALNUM(*b) || (ISALNUM(*b) && *b != '9' && *b != 'z' && *b != 'Z'))
381 break;
382 b--;
383 }
384 if (!ISALNUM(*b))
385 b++;
386 result = mrb_str_new(mrb, (char*) p, b - p);
387 }
388
389 while (e >= b) {
390 if (!ISALNUM(*e)) {
391 if (*e == 0xff) {
392 mrb_str_cat_lit(mrb, result, "\x01");
393 (*e) = 0;
394 } else
395 (*e)++;
396 break;
397 }
398 prepend = NULL;
399 if (*e == '9') {
400 if (e == b) prepend = "1";
401 *e = '0';
402 } else if (*e == 'z') {
403 if (e == b) prepend = "a";
404 *e = 'a';
405 } else if (*e == 'Z') {
406 if (e == b) prepend = "A";
407 *e = 'A';
408 } else {
409 (*e)++;
410 break;
411 }
412 if (prepend) mrb_str_cat_cstr(mrb, result, prepend);
413 e--;
414 }
415 result = mrb_str_cat(mrb, result, (char*) b, t - b);
416 l = RSTRING_LEN(result);
417 mrb_str_resize(mrb, self, l);
418 memcpy(RSTRING_PTR(self), RSTRING_PTR(result), l);
419 return self;
420}
421
422static mrb_value
423mrb_str_succ(mrb_state *mrb, mrb_value self)
424{
425 mrb_value str;
426
427 str = mrb_str_dup(mrb, self);
428 mrb_str_succ_bang(mrb, str);
429 return str;
430}
431
432/*
433 * call-seq:
434 * str.prepend(other_str) -> str
435 *
436 * Prepend---Prepend the given string to <i>str</i>.
437 *
438 * a = "world"
439 * a.prepend("hello ") #=> "hello world"
440 * a #=> "hello world"
441 */
442static mrb_value
443mrb_str_prepend(mrb_state *mrb, mrb_value self)
444{
445 struct RString *s1 = mrb_str_ptr(self), *s2, *temp_s;
446 mrb_int len;
447 mrb_value other, temp_str;
448
449 mrb_get_args(mrb, "S", &other);
450
451 mrb_str_modify(mrb, s1);
452 if (!mrb_string_p(other)) {
453 other = mrb_str_to_str(mrb, other);
454 }
455 s2 = mrb_str_ptr(other);
456 len = RSTR_LEN(s1) + RSTR_LEN(s2);
457 temp_str = mrb_str_new(mrb, NULL, RSTR_LEN(s1));
458 temp_s = mrb_str_ptr(temp_str);
459 memcpy(RSTR_PTR(temp_s), RSTR_PTR(s1), RSTR_LEN(s1));
460 if (RSTRING_CAPA(self) < len) {
461 mrb_str_resize(mrb, self, len);
462 }
463 memcpy(RSTR_PTR(s1), RSTR_PTR(s2), RSTR_LEN(s2));
464 memcpy(RSTR_PTR(s1) + RSTR_LEN(s2), RSTR_PTR(temp_s), RSTR_LEN(temp_s));
465 RSTR_SET_LEN(s1, len);
466 RSTR_PTR(s1)[len] = '\0';
467 return self;
468}
469
470#ifdef MRB_UTF8_STRING
471static const char utf8len_codepage_zero[256] =
472{
473 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
474 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
475 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
476 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
477 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
478 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
479 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
480 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0,
481};
482
483static mrb_int
484utf8code(unsigned char* p)
485{
486 mrb_int len;
487
488 if (p[0] < 0x80)
489 return p[0];
490
491 len = utf8len_codepage_zero[p[0]];
492 if (len > 1 && (p[1] & 0xc0) == 0x80) {
493 if (len == 2)
494 return ((p[0] & 0x1f) << 6) + (p[1] & 0x3f);
495 if ((p[2] & 0xc0) == 0x80) {
496 if (len == 3)
497 return ((p[0] & 0x0f) << 12) + ((p[1] & 0x3f) << 6)
498 + (p[2] & 0x3f);
499 if ((p[3] & 0xc0) == 0x80) {
500 if (len == 4)
501 return ((p[0] & 0x07) << 18) + ((p[1] & 0x3f) << 12)
502 + ((p[2] & 0x3f) << 6) + (p[3] & 0x3f);
503 if ((p[4] & 0xc0) == 0x80) {
504 if (len == 5)
505 return ((p[0] & 0x03) << 24) + ((p[1] & 0x3f) << 18)
506 + ((p[2] & 0x3f) << 12) + ((p[3] & 0x3f) << 6)
507 + (p[4] & 0x3f);
508 if ((p[5] & 0xc0) == 0x80 && len == 6)
509 return ((p[0] & 0x01) << 30) + ((p[1] & 0x3f) << 24)
510 + ((p[2] & 0x3f) << 18) + ((p[3] & 0x3f) << 12)
511 + ((p[4] & 0x3f) << 6) + (p[5] & 0x3f);
512 }
513 }
514 }
515 }
516 return p[0];
517}
518
519static mrb_value
520mrb_str_ord(mrb_state* mrb, mrb_value str)
521{
522 if (RSTRING_LEN(str) == 0)
523 mrb_raise(mrb, E_ARGUMENT_ERROR, "empty string");
524 return mrb_fixnum_value(utf8code((unsigned char*) RSTRING_PTR(str)));
525}
526#else
527static mrb_value
528mrb_str_ord(mrb_state* mrb, mrb_value str)
529{
530 if (RSTRING_LEN(str) == 0)
531 mrb_raise(mrb, E_ARGUMENT_ERROR, "empty string");
532 return mrb_fixnum_value(RSTRING_PTR(str)[0]);
533}
534#endif
535
536void
537mrb_mruby_string_ext_gem_init(mrb_state* mrb)
538{
539 struct RClass * s = mrb->string_class;
540
541 mrb_define_method(mrb, s, "dump", mrb_str_dump, MRB_ARGS_NONE());
542 mrb_define_method(mrb, s, "getbyte", mrb_str_getbyte, MRB_ARGS_REQ(1));
543 mrb_define_method(mrb, s, "setbyte", mrb_str_setbyte, MRB_ARGS_REQ(2));
544 mrb_define_method(mrb, s, "byteslice", mrb_str_byteslice, MRB_ARGS_REQ(1)|MRB_ARGS_OPT(1));
545 mrb_define_method(mrb, s, "swapcase!", mrb_str_swapcase_bang, MRB_ARGS_NONE());
546 mrb_define_method(mrb, s, "swapcase", mrb_str_swapcase, MRB_ARGS_NONE());
547 mrb_define_method(mrb, s, "concat", mrb_str_concat2, MRB_ARGS_REQ(1));
548 mrb_define_method(mrb, s, "<<", mrb_str_concat2, MRB_ARGS_REQ(1));
549 mrb_define_method(mrb, s, "start_with?", mrb_str_start_with, MRB_ARGS_REST());
550 mrb_define_method(mrb, s, "end_with?", mrb_str_end_with, MRB_ARGS_REST());
551 mrb_define_method(mrb, s, "hex", mrb_str_hex, MRB_ARGS_NONE());
552 mrb_define_method(mrb, s, "oct", mrb_str_oct, MRB_ARGS_NONE());
553 mrb_define_method(mrb, s, "chr", mrb_str_chr, MRB_ARGS_NONE());
554 mrb_define_method(mrb, s, "lines", mrb_str_lines, MRB_ARGS_NONE());
555 mrb_define_method(mrb, s, "succ", mrb_str_succ, MRB_ARGS_NONE());
556 mrb_define_method(mrb, s, "succ!", mrb_str_succ_bang, MRB_ARGS_NONE());
557 mrb_define_method(mrb, s, "prepend", mrb_str_prepend, MRB_ARGS_REQ(1));
558 mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "next"), mrb_intern_lit(mrb, "succ"));
559 mrb_alias_method(mrb, s, mrb_intern_lit(mrb, "next!"), mrb_intern_lit(mrb, "succ!"));
560 mrb_define_method(mrb, s, "ord", mrb_str_ord, MRB_ARGS_NONE());
561
562 mrb_define_method(mrb, mrb->fixnum_class, "chr", mrb_fixnum_chr, MRB_ARGS_NONE());
563}
564
565void
566mrb_mruby_string_ext_gem_final(mrb_state* mrb)
567{
568}
Note: See TracBrowser for help on using the repository browser.