On Fri, Dec 16, 2005 at 08:17:40AM +0900, Ron Jeffries wrote:
> On Thu, 15 Dec 2005 14:50:59 +0900, "Daniel Sheppard" <daniels / pronto.com.au>
> wrote:
> 
> >I might be wrong - but I'm pretty sure that substrings in ruby are
> >created with copy-on-write. That is, when you take a substring, a new
> >block of memory isn't allocated to the new String, it references the
> >same block of memory as the original string - the allocation of a new
> >block of memory only occurs when one of the strings is modified.
> 
> I asked about this a week or two ago, as part of my Extended Set Theory thing,
> and the impression I got from the answers then was that sub /arrays/ are copy on
> write but that sub /strings/ were not. 
> 
> I wonder how one might confirm this, other than by asking matz or reading the
> compiler and libraries ...

Just the following should do:

-----------------------------------------------------------------------------
VALUE
rb_str_substr(str, beg, len)
    VALUE str;
    long beg, len;
{
    VALUE str2;

    if (len < 0) return Qnil;
    if (beg > RSTRING(str)->len) return Qnil;
    if (beg < 0) {
	beg += RSTRING(str)->len;
	if (beg < 0) return Qnil;
    }
    if (beg + len > RSTRING(str)->len) {
	len = RSTRING(str)->len - beg;
    }
    if (len < 0) {
	len = 0;
    }
    if (len == 0) {
	str2 = rb_str_new5(str,0,0);
    }
    else if (len > sizeof(struct RString)/2 &&
	beg + len == RSTRING(str)->len && !FL_TEST(str, STR_ASSOC)) {
/*      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        */
	str2 = rb_str_new3(rb_str_new4(str));
	RSTRING(str2)->ptr += RSTRING(str2)->len - len;
	RSTRING(str2)->len = len;
    }
    else {
	str2 = rb_str_new5(str, RSTRING(str)->ptr+beg, len);
    }
    OBJ_INFECT(str2, str);

    return str2;
}
-----------------------------------------------------------------------------



... (optionally) ...




-----------------------------------------------------------------------------
static VALUE
str_new3(klass, str)
    VALUE klass, str;
{
    VALUE str2 = str_alloc(klass);

    RSTRING(str2)->len = RSTRING(str)->len;
    RSTRING(str2)->ptr = RSTRING(str)->ptr;
    RSTRING(str2)->aux.shared = str;
    FL_SET(str2, ELTS_SHARED);
    OBJ_INFECT(str2, str);

    return str2;
}

VALUE
rb_str_new3(str)
    VALUE str;
{
    return str_new3(rb_obj_class(str), str);
}

static VALUE
str_new4(klass, str)
    VALUE klass, str;
{
    VALUE str2 = str_alloc(klass);

    RSTRING(str2)->len = RSTRING(str)->len;
    RSTRING(str2)->ptr = RSTRING(str)->ptr;
    if (FL_TEST(str, ELTS_SHARED)) {
	FL_SET(str2, ELTS_SHARED);
	RSTRING(str2)->aux.shared = RSTRING(str)->aux.shared;
    }
    else {
	FL_SET(str, ELTS_SHARED);
	RSTRING(str)->aux.shared = str2;
    }

    return str2;
}

VALUE
rb_str_new4(orig)
    VALUE orig;
{
    VALUE klass, str;

    if (OBJ_FROZEN(orig)) return orig;
    klass = rb_obj_class(orig);
    if (FL_TEST(orig, ELTS_SHARED) && (str = RSTRING(orig)->aux.shared) && klass == RBASIC(str)->klass) {
	long ofs;
	ofs = RSTRING(str)->len - RSTRING(orig)->len;
	if (ofs > 0) {
	    str = str_new3(klass, str);
	    RSTRING(str)->ptr += ofs;
	    RSTRING(str)->len -= ofs;
	}
    }
    else if (FL_TEST(orig, STR_ASSOC)) {
	str = str_new(klass, RSTRING(orig)->ptr, RSTRING(orig)->len);
    }
    else {
	str = str_new4(klass, orig);
    }
    OBJ_INFECT(str, orig);
    OBJ_FREEZE(str);
    return str;
}

VALUE
rb_str_new5(obj, ptr, len)
    VALUE obj;
    const char *ptr;
    long len;
{
    return str_new(rb_obj_class(obj), ptr, len);
}
-----------------------------------------------------------------------------

-- 
Mauricio Fernandez