From: Nobuyoshi Nakada Date: 2008-01-28T15:18:41+09:00 Subject: [ruby-dev:33443] Re: Ruby1.9 String バイト列へのインデックス アクセス なかだです。 At Sun, 20 Jan 2008 03:58:32 +0900, Yukihiro Matsumoto wrote in [ruby-dev:33196]: > |s = "abcde" > |s.byte(3) # => 100 > |s.byte(3, 101) > |s # => "abcee" > > 難点は「s.byte(3,101)」がいまいち「代入感」がないところでしょ > うか。文法を拡張して「s.byte(3)=101」を許すようにするかな。 これはだいぶ前から考えているんですが、かなり難しいというか面倒そ うです。 > |あるいはBytesクラスを用意してString#byteで作成できるといいかもしれません。 > > この場合はBytesクラスはメモリ領域をStringと共有するのでしょ > うか。それはそれで面白いと思いますが。 > > |s = "abcde" > |s.byte # "abcde"のバイト列オブジェクト > |s.byte[3] # => 100 > |s.byte[3] = 101 # 破壊的更新 > |s # => "abcee" > | > |インデックスにはRangeも指定できるといいです。 > > その場合は右辺に文字列(またはBytes)を指定するわけ? > > そういえば、自分で言い出しといてなんだけど複数形な名前のクラ > スってのはいかがなものか。 String::ByteIndexerとか。 Index: string.c =================================================================== --- string.c (revision 15284) +++ string.c (working copy) @@ -2405,27 +2405,33 @@ rb_str_aref_m(int argc, VALUE *argv, VAL static void -rb_str_splice_0(VALUE str, long beg, long len, VALUE val) +rb_str_splice_ptr(VALUE str, long beg, long len, const char *vptr, long vlen) { rb_str_modify(str); - if (len < RSTRING_LEN(val)) { + if (len < vlen) { /* expand string */ - RESIZE_CAPA(str, RSTRING_LEN(str) + RSTRING_LEN(val) - len + 1); + RESIZE_CAPA(str, RSTRING_LEN(str) + vlen - len + 1); } - if (RSTRING_LEN(val) != len) { - memmove(RSTRING_PTR(str) + beg + RSTRING_LEN(val), + if (vlen != len) { + memmove(RSTRING_PTR(str) + beg + vlen, RSTRING_PTR(str) + beg + len, RSTRING_LEN(str) - (beg + len)); } - if (RSTRING_LEN(val) < beg && len < 0) { + if (vlen < beg && len < 0) { MEMZERO(RSTRING_PTR(str) + RSTRING_LEN(str), char, -len); } - if (RSTRING_LEN(val) > 0) { - memmove(RSTRING_PTR(str)+beg, RSTRING_PTR(val), RSTRING_LEN(val)); + if (vlen > 0) { + memmove(RSTRING_PTR(str)+beg, vptr, vlen); } - STR_SET_LEN(str, RSTRING_LEN(str) + RSTRING_LEN(val) - len); + STR_SET_LEN(str, RSTRING_LEN(str) + vlen - len); if (RSTRING_PTR(str)) { RSTRING_PTR(str)[RSTRING_LEN(str)] = '\0'; } +} + +static void +rb_str_splice_0(VALUE str, long beg, long len, VALUE val) +{ + rb_str_splice_ptr(str, beg, len, RSTRING_PTR(val), RSTRING_LEN(val)); OBJ_INFECT(str, val); } @@ -4666,16 +4672,195 @@ rb_str_each_line(int argc, VALUE *argv, +static VALUE rb_str_each_byte(VALUE str); + +static VALUE rb_cByteIndexer; + +struct byte_indexer { + VALUE str; + long offset, length; +}; + +static void +bidx_mark(void *ptr) +{ + struct byte_indexer *bp = ptr; + rb_gc_mark(bp->str); +} + +static struct byte_indexer * +bidx_ptr(VALUE self) +{ + struct byte_indexer *ptr; + + Data_Get_Struct(self, struct byte_indexer, ptr); + if (RDATA(self)->dmark != bidx_mark) { + rb_raise(rb_eArgError, "ByteIndexer expected"); + } + return ptr; +} + +static VALUE +rb_byte_indexer(VALUE str, long offset, long length) +{ + struct byte_indexer *ptr; + VALUE indexer = Data_Make_Struct(rb_cByteIndexer, struct byte_indexer, + bidx_mark, -1, ptr); + + ptr->str = str; + ptr->offset = offset; + ptr->length = length; + return indexer; +} + +static VALUE +bidx_aref(int argc, VALUE *argv, VALUE self) +{ + struct byte_indexer *ptr = bidx_ptr(self); + VALUE str = ptr->str; + long beg, len, offset = ptr->offset, length = ptr->length; + + if (length < 0) { + length += RSTRING_LEN(str) + 1; + } + if (offset + length > RSTRING_LEN(str)) { + length = RSTRING_LEN(str) - offset; + } + if (argc == 2) { + beg = NUM2LONG(argv[0]) + offset; + len = NUM2LONG(argv[1]); + range: + if (len < 0) return Qnil; + if (!length) { + len = 0; + } + if (beg < 0) { + if (len > -beg) len = -beg; + beg += length; + if (beg < 0) return Qnil; + } + else if (beg > length) { + return Qnil; + } + return rb_byte_indexer(str, beg, len); + } + else if (argc != 1) { + rb_raise(rb_eArgError, "wrong number arguments (%d for 1)", argc); + } + if (!FIXNUM_P(argv[0])) { + switch (rb_range_beg_len(argv[0], &beg, &len, length, 0)) { + case Qfalse: + break; + case Qnil: + return Qnil; + default: + goto range; + } + } + beg = FIX2INT(argv[0]); + if (beg >= length) return Qnil; + return INT2FIX(RSTRING_PTR(str)[beg + offset]); +} + +static VALUE +byte_array(VALUE ary) +{ + long i; + VALUE str = rb_str_tmp_new(RARRAY_LEN(ary)); + + for (i = 0; i < RARRAY_LEN(ary); ++i) { + VALUE n = RARRAY_PTR(ary)[i]; + RSTRING_PTR(str)[i] = NUM2CHR(n); + } + return str; +} + +static VALUE +bidx_aset(int argc, VALUE *argv, VALUE self) +{ + struct byte_indexer *ptr = bidx_ptr(self); + VALUE str = ptr->str, val, ary; + long beg, len, slen; + + rb_str_modify(str); + slen = RSTRING_LEN(str); + switch (argc) { + case 3: + beg = NUM2LONG(argv[0]); + len = NUM2LONG(argv[1]); + if (len < 0) rb_raise(rb_eIndexError, "negative length %ld", len); + if (beg < 0 ? (beg += slen) < 0 : beg > slen) { + rb_raise(rb_eIndexError, "index %ld out of string", beg); + } + val = argv[2]; + break; + case 2: + if (!rb_range_beg_len(argv[0], &beg, &len, slen, 0)) { + beg = NUM2LONG(argv[0]); + len = 1; + } + val = argv[1]; + break; + default: + rb_raise(rb_eArgError, "wrong number of arguments (%d for 2)", argc); + } + if (!NIL_P(ary = rb_check_array_type(val))) { + rb_str_splice_0(str, beg, len, byte_array(ary)); + } + else { + char byte = NUM2CHR(val); + rb_str_splice_ptr(str, beg, len, &byte, 1); + } + return argv[argc - 1]; +} + +static VALUE +bidx_each(VALUE self) +{ + struct byte_indexer *ptr = bidx_ptr(self); + VALUE str = ptr->str; + long i, l; + + if (!rb_block_given_p()) return self; + for (l = (i=ptr->offset) + ptr->length; i anEnumerator + * str.bytes => anIndexer * str.bytes {|fixnum| block } => str - * + * * Returns an enumerator that gives each byte in the string. If a block is * given, it iterates over each byte in the string. - * + * * "hello".bytes.to_a #=> [104, 101, 108, 108, 111] + * + * str = "hello" + * str.bytes[0] ^= 0x20 + * str #=> "Hello" */ +static VALUE +rb_str_bytes(VALUE str) +{ + if (!rb_block_given_p()) return rb_byte_indexer(str, 0, -1); + return rb_str_each_byte(str); +} + /* * Document-method: each_byte @@ -6077,5 +6262,5 @@ Init_String(void) rb_define_method(rb_cString, "split", rb_str_split_m, -1); rb_define_method(rb_cString, "lines", rb_str_each_line, -1); - rb_define_method(rb_cString, "bytes", rb_str_each_byte, 0); + rb_define_method(rb_cString, "bytes", rb_str_bytes, 0); rb_define_method(rb_cString, "chars", rb_str_each_char, 0); rb_define_method(rb_cString, "reverse", rb_str_reverse, 0); @@ -6183,3 +6368,5 @@ Init_String(void) rb_define_method(rb_cSymbol, "encoding", sym_encoding, 0); + + Init_ByteIndexer(); } -- --- 僕の前にBugはない。 --- 僕の後ろにBugはできる。 中田 伸悦