Index: array.c
===================================================================
RCS file: /cvs/ruby/src/ruby/array.c,v
retrieving revision 1.179
diff -U2 -p -r1.179 array.c
--- array.c	12 Sep 2005 15:23:54 -0000	1.179
+++ array.c	22 Sep 2005 03:55:44 -0000
@@ -24,4 +24,24 @@ static ID id_cmp;
 #define ARY_DEFAULT_SIZE 16
 
+static inline long
+lfree_len(VALUE ary)
+{
+    if (!FL_TEST(ary, ELTS_LFREE)) return 0;
+    return RARRAY(ary)->ptr[-1];
+}
+
+static inline long
+rfree_len(VALUE ary)
+{
+    if (!FL_TEST(ary, ELTS_RFREE)) return 0;
+    return RARRAY(ary)->ptr[RARRAY(ary)->len];
+}
+
+static inline long
+ary_capa(VALUE ary)
+{
+    return RARRAY(ary)->len + rfree_len(ary);
+}
+
 void
 rb_mem_clear(register VALUE *mem, register long size)
@@ -53,18 +73,56 @@ rb_ary_modify_check(VALUE ary)
 
 static void
-rb_ary_modify(VALUE ary)
+rb_ary_unshare(VALUE ary, int freeit)
 {
+    VALUE shared = RARRAY(ary)->shared;
+    VALUE next = shared;
+    VALUE cur;
     VALUE *ptr;
+    VALUE *freeptr = 0;
 
-    rb_ary_modify_check(ary);
     if (FL_TEST(ary, ELTS_SHARED)) {
-	ptr = ALLOC_N(VALUE, RARRAY(ary)->len);
+	if (freeit) {
+	    RARRAY(ary)->ptr = 0;
+	}
+	else {
+	    ptr = ALLOC_N(VALUE, RARRAY(ary)->len);
+	    MEMCPY(ptr, RARRAY(ary)->ptr, VALUE, RARRAY(ary)->len);
+	    RARRAY(ary)->ptr = ptr;
+	}
 	FL_UNSET(ary, ELTS_SHARED);
-	RARRAY(ary)->aux.capa = RARRAY(ary)->len;
-	MEMCPY(ptr, RARRAY(ary)->ptr, VALUE, RARRAY(ary)->len);
-	RARRAY(ary)->ptr = ptr;
+	RARRAY(ary)->shared = 0;
+	do {
+	    cur = next;
+	    next = RARRAY(cur)->shared;
+	} while (next != ary);
+	RARRAY(cur)->shared = (cur == shared ? 0 : shared);
+    }
+    else if (RARRAY(ary)->ptr) {
+	if (freeit) {
+	    freeptr = RARRAY(ary)->ptr - lfree_len(ary);
+	    FL_UNSET(ary, ELTS_LFREE|ELTS_RFREE);
+	    RARRAY(ary)->ptr = 0;
+	}
+	if (shared) {
+	    while ((RARRAY(ary)->shared = 0), (shared = RARRAY(ary = shared)->shared)) {
+		ptr = ALLOC_N(VALUE, RARRAY(ary)->len);
+		MEMCPY(ptr, RARRAY(ary)->ptr, VALUE, RARRAY(ary)->len);
+		FL_UNSET(ary, ELTS_SHARED);
+		RARRAY(ary)->ptr = ptr;
+	    }
+	}
+	xfree(freeptr);
     }
 }
 
+static void
+rb_ary_modify(VALUE ary)
+{
+    VALUE *ptr;
+
+    rb_ary_modify_check(ary);
+    rb_ary_unshare(ary, 0);
+}
+
 VALUE
 rb_ary_freeze(VALUE ary)
@@ -97,5 +155,5 @@ ary_alloc(VALUE klass)
     ary->len = 0;
     ary->ptr = 0;
-    ary->aux.capa = 0;
+    ary->shared = 0;
 
     return (VALUE)ary;
@@ -117,5 +175,4 @@ ary_new(VALUE klass, long len)
     ary = ary_alloc(klass);
     RARRAY(ary)->ptr = ALLOC_N(VALUE, len);
-    RARRAY(ary)->aux.capa = len;
 
     return ary;
@@ -194,7 +251,13 @@ rb_values_new2(long n, const VALUE *elts
 
     val = ary_new(rb_cValues, n);
-    if (n > 0 && elts) {
-	RARRAY(val)->len = n;
-	MEMCPY(RARRAY(val)->ptr, elts, VALUE, n);
+    if (n > 0) {
+	if (elts) {
+	    RARRAY(val)->len = n;
+	    MEMCPY(RARRAY(val)->ptr, elts, VALUE, n);
+	}
+	else {
+	    RARRAY(val)->ptr[0] = n;
+	    FL_SET(val, ELTS_RFREE);
+	}
     }
 
@@ -203,21 +266,17 @@ rb_values_new2(long n, const VALUE *elts
 
 static VALUE
-ary_make_shared(VALUE ary)
+ary_share(VALUE val, VALUE ary)
 {
-    if (!FL_TEST(ary, ELTS_SHARED)) {
-	NEWOBJ(shared, struct RArray);
-	OBJSETUP(shared, rb_cArray, T_ARRAY);
-
-	shared->len = RARRAY(ary)->len;
-	shared->ptr = RARRAY(ary)->ptr;
-	shared->aux.capa = RARRAY(ary)->aux.capa;
-	RARRAY(ary)->aux.shared = (VALUE)shared;
-	FL_SET(ary, ELTS_SHARED);
-	OBJ_FREEZE(shared);
-	return (VALUE)shared;
+    RARRAY(val)->ptr = RARRAY(ary)->ptr;
+    RARRAY(val)->len = RARRAY(ary)->len;
+    if (RARRAY(ary)->shared) {
+        RARRAY(val)->shared = RARRAY(ary)->shared;
     }
     else {
-	return RARRAY(ary)->aux.shared;
+	RARRAY(val)->shared = ary;
     }
+    RARRAY(ary)->shared = val;
+    FL_SET(val, ELTS_SHARED);
+    return val;
 }
 
@@ -225,12 +284,5 @@ static VALUE
 ary_shared_array(VALUE klass, VALUE ary)
 {
-    VALUE val = ary_alloc(klass);
-
-    ary_make_shared(ary);
-    RARRAY(val)->ptr = RARRAY(ary)->ptr;
-    RARRAY(val)->len = RARRAY(ary)->len;
-    RARRAY(val)->aux.shared = RARRAY(ary)->aux.shared;
-    FL_SET(val, ELTS_SHARED);
-    return val;
+    return ary_share(ary_alloc(klass), ary);
 }
 
@@ -247,4 +299,10 @@ rb_ary_from_values(VALUE val)
 }
 
+void
+rb_ary_free(VALUE obj)
+{
+    rb_ary_unshare(obj, 1);
+}
+
 VALUE
 rb_assoc_new(VALUE car, VALUE cdr)
@@ -316,6 +374,15 @@ rb_ary_initialize(int argc, VALUE *argv,
     long len;
     VALUE size, val;
+    long capa;
 
     if (rb_scan_args(argc, argv, "02", &size, &val) == 0) {
+	if (!FL_TEST(ary, ELTS_SHARED)) {
+	    rb_ary_modify(ary);
+	    capa = ary_capa(ary);
+	    if (capa) {
+		FL_SET(ary, ELTS_RFREE);
+		RARRAY(ary)->ptr[0] = capa;
+	    }
+	}
 	RARRAY(ary)->len = 0;
 	if (rb_block_given_p()) {
@@ -341,7 +408,11 @@ rb_ary_initialize(int argc, VALUE *argv,
     }
     rb_ary_modify(ary);
-    if (len > RARRAY(ary)->aux.capa) {
-	REALLOC_N(RARRAY(ary)->ptr, VALUE, len);
-	RARRAY(ary)->aux.capa = len;
+    capa = ary_capa(ary);
+    if (len > capa) {
+	long lfree = lfree_len(ary);
+	RARRAY(ary)->ptr -= lfree;
+	REALLOC_N(RARRAY(ary)->ptr, VALUE, len + lfree);
+	RARRAY(ary)->ptr += lfree;
+	capa = len;
     }
     if (rb_block_given_p()) {
@@ -360,4 +431,11 @@ rb_ary_initialize(int argc, VALUE *argv,
 	RARRAY(ary)->len = len;
     }
+    if (capa > len) {
+	FL_SET(ary, ELTS_RFREE);
+	RARRAY(ary)->ptr[len] = capa-len;
+    }
+    else {
+	FL_UNSET(ary, ELTS_RFREE);
+    }
 
     return ary;
@@ -382,5 +460,5 @@ rb_ary_s_create(int argc, VALUE *argv, V
 	MEMCPY(RARRAY(ary)->ptr, argv, VALUE, argc);
     }
-    RARRAY(ary)->len = RARRAY(ary)->aux.capa = argc;
+    RARRAY(ary)->len = argc;
 
     return ary;
@@ -390,4 +468,5 @@ void
 rb_ary_store(VALUE ary, long idx, VALUE val)
 {
+    long capa;
     if (idx < 0) {
 	idx += RARRAY(ary)->len;
@@ -399,6 +478,8 @@ rb_ary_store(VALUE ary, long idx, VALUE 
 
     rb_ary_modify(ary);
-    if (idx >= RARRAY(ary)->aux.capa) {
-	long new_capa = RARRAY(ary)->aux.capa / 2;
+    capa = ary_capa(ary);
+    if (idx >= capa) {
+	long new_capa = capa / 2;
+	long lfree = lfree_len(ary);
 
 	if (new_capa < ARY_DEFAULT_SIZE) {
@@ -409,6 +490,8 @@ rb_ary_store(VALUE ary, long idx, VALUE 
 	    rb_raise(rb_eArgError, "index too big");
 	}
-	REALLOC_N(RARRAY(ary)->ptr, VALUE, new_capa);
-	RARRAY(ary)->aux.capa = new_capa;
+	RARRAY(ary)->ptr -= lfree;
+	REALLOC_N(RARRAY(ary)->ptr, VALUE, new_capa + lfree);
+	RARRAY(ary)->ptr += lfree;
+	capa = new_capa;
     }
     if (idx > RARRAY(ary)->len) {
@@ -418,5 +501,13 @@ rb_ary_store(VALUE ary, long idx, VALUE 
 
     if (idx >= RARRAY(ary)->len) {
-	RARRAY(ary)->len = idx + 1;
+	long len = idx + 1;
+	RARRAY(ary)->len = len;
+	if (capa > len) {
+	    FL_SET(ary, ELTS_RFREE);
+	    RARRAY(ary)->ptr[len] = capa-len;
+	}
+	else {
+	    FL_UNSET(ary, ELTS_RFREE);
+	}
     }
     RARRAY(ary)->ptr[idx] = val;
@@ -496,13 +587,29 @@ VALUE
 rb_ary_pop(VALUE ary)
 {
-    rb_ary_modify_check(ary);
+    long capa;
+    VALUE item;
+    if (!FL_TEST(ary, ELTS_SHARED)) {
+        rb_ary_modify(ary);
+    }
+    else {
+        rb_ary_modify_check(ary);
+    }
     if (RARRAY(ary)->len == 0) return Qnil;
+    capa = ary_capa(ary);
     if (!FL_TEST(ary, ELTS_SHARED) &&
-	    RARRAY(ary)->len * 2 < RARRAY(ary)->aux.capa &&
-	    RARRAY(ary)->aux.capa > ARY_DEFAULT_SIZE) {
-	RARRAY(ary)->aux.capa = RARRAY(ary)->len * 2;
-	REALLOC_N(RARRAY(ary)->ptr, VALUE, RARRAY(ary)->aux.capa);
+	    RARRAY(ary)->len * 2 < capa &&
+	    capa > ARY_DEFAULT_SIZE) {
+	long lfree = lfree_len(ary);
+	capa = RARRAY(ary)->len * 2;
+	RARRAY(ary)->ptr -= lfree;
+	REALLOC_N(RARRAY(ary)->ptr, VALUE, capa + lfree);
+	RARRAY(ary)->ptr += lfree;
+    }
+    item = RARRAY(ary)->ptr[--RARRAY(ary)->len];
+    if (!FL_TEST(ary, ELTS_SHARED)) {
+	RARRAY(ary)->ptr[RARRAY(ary)->len] = capa-RARRAY(ary)->len;
+	FL_SET(ary, ELTS_RFREE);
     }
-    return RARRAY(ary)->ptr[--RARRAY(ary)->len];
+    return item;
 }
 
@@ -524,4 +631,5 @@ rb_ary_pop_m(int argc, VALUE *argv, VALU
 {
     VALUE result;
+    long n;
 
     if (argc == 0) {
@@ -529,8 +637,12 @@ rb_ary_pop_m(int argc, VALUE *argv, VALU
     }
 
-    rb_ary_modify_check(ary);
-
     result = ary_shared_last(argc, argv, ary);
-    RARRAY(ary)->len -= RARRAY(result)->len;
+    rb_ary_modify(ary);
+    n = RARRAY(result)->len;
+    if (n) {
+	long rfree = rfree_len(ary);
+	RARRAY(ary)->ptr[RARRAY(ary)->len -= n] = rfree + n;
+	FL_SET(ary, ELTS_RFREE);
+    }
     return result;
 }
@@ -541,8 +653,20 @@ rb_ary_shift(VALUE ary)
     VALUE top;
 
-    rb_ary_modify_check(ary);
     if (RARRAY(ary)->len == 0) return Qnil;
     top = RARRAY(ary)->ptr[0];
-    ary_make_shared(ary);
+    if (!FL_TEST(ary, ELTS_SHARED)) {
+	rb_ary_modify(ary);
+	if (FL_TEST(ary, ELTS_LFREE)) {
+	    RARRAY(ary)->ptr[0] = RARRAY(ary)->ptr[-1] + 1;
+	}
+	else {
+	    FL_SET(ary, ELTS_LFREE);
+	    RARRAY(ary)->ptr[0] = 1;
+	}
+    }
+    else {
+	rb_ary_modify_check(ary);
+    }
+
     RARRAY(ary)->ptr++;		/* shift ptr */
     RARRAY(ary)->len--;
@@ -578,34 +702,15 @@ rb_ary_shift_m(int argc, VALUE *argv, VA
     }
 
-    rb_ary_modify_check(ary);
-
     result = ary_shared_first(argc, argv, ary);
-    n = RARRAY(result)->len;
-    RARRAY(ary)->ptr += n;
-    RARRAY(ary)->len -= n;
-
-    return result;
-}
-
-VALUE
-rb_ary_unshift(VALUE ary, VALUE item)
-{
     rb_ary_modify(ary);
-    if (RARRAY(ary)->len == RARRAY(ary)->aux.capa) {
-	long capa_inc = RARRAY(ary)->aux.capa / 2;
-	if (capa_inc < ARY_DEFAULT_SIZE) {
-	    capa_inc = ARY_DEFAULT_SIZE;
-	}
-	RARRAY(ary)->aux.capa += capa_inc;
-	REALLOC_N(RARRAY(ary)->ptr, VALUE, RARRAY(ary)->aux.capa);
+    n = RARRAY(result)->len;
+    if (n) {
+	long lfree = lfree_len(ary);
+	(RARRAY(ary)->ptr += n)[-1] = lfree + n;
+	RARRAY(ary)->len -= n;
+	FL_SET(ary, ELTS_LFREE);
     }
 
-    /* sliding items */
-    MEMMOVE(RARRAY(ary)->ptr + 1, RARRAY(ary)->ptr, VALUE, RARRAY(ary)->len);
-
-    RARRAY(ary)->len++;
-    RARRAY(ary)->ptr[0] = item;
-
-    return ary;
+    return result;
 }
 
@@ -625,18 +730,50 @@ static VALUE
 rb_ary_unshift_m(int argc, VALUE *argv, VALUE ary)
 {
-    long len = RARRAY(ary)->len;
-
-    if (argc == 0) return ary;
-
-    /* make rooms by setting the last item */
-    rb_ary_store(ary, len + argc - 1, Qnil);
-
-    /* sliding items */
-    MEMMOVE(RARRAY(ary)->ptr + argc, RARRAY(ary)->ptr, VALUE, len);
-    MEMCPY(RARRAY(ary)->ptr, argv, VALUE, argc);
+    long lfree = lfree_len(ary);
     
+    rb_ary_modify_check(ary);
+    if (lfree<argc) {
+	long len = RARRAY(ary)->len;
+	long rfree = rfree_len(ary) / 2;
+	long free2 = argc+(RARRAY(ary)->len+argc)/2;
+	VALUE *ptr;
+	if (free2 < ARY_DEFAULT_SIZE) {
+	    free2 = ARY_DEFAULT_SIZE;
+	}
+	ptr = ALLOC_N(VALUE, len + free2) + (free2 - rfree);
+	MEMMOVE(ptr, RARRAY(ary)->ptr, VALUE, len);
+	rb_ary_free(ary);
+	RARRAY(ary)->ptr = ptr;
+	if (rfree) {
+	    RARRAY(ary)->ptr[len] = rfree;
+	    FL_SET(ary, ELTS_RFREE);
+	}
+	else {
+	    FL_UNSET(ary, ELTS_RFREE);
+	}
+	lfree = free2-rfree;
+	FL_SET(ary, ELTS_LFREE);
+    }
+
+    RARRAY(ary)->ptr -= argc;
+    RARRAY(ary)->len += argc;
+    lfree -= argc;
+    if (lfree) {
+	RARRAY(ary)->ptr[-1] = lfree;
+    }
+    else {
+	FL_UNSET(ary, ELTS_LFREE);
+    }
+    MEMCPY(RARRAY(ary)->ptr, argv, VALUE, argc);
+
     return ary;
 }
 
+VALUE
+rb_ary_unshift(VALUE ary, VALUE item)
+{
+    return rb_ary_unshift_m(1, &item, ary);
+}
+
 /* faster version - use this if you don't need to treat negative offset */
 static inline VALUE
@@ -662,5 +799,5 @@ static VALUE
 rb_ary_subseq(VALUE ary, long beg, long len)
 {
-    VALUE klass, ary2, shared;
+    VALUE klass, ary2;
     VALUE *ptr;
 
@@ -676,11 +813,7 @@ rb_ary_subseq(VALUE ary, long beg, long 
     if (len == 0) return ary_new(klass, 0);
 
-    shared = ary_make_shared(ary);
-    ptr = RARRAY(ary)->ptr;
-    ary2 = ary_alloc(klass);
-    RARRAY(ary2)->ptr = ptr + beg;
+    ary2 = ary_shared_array(klass, ary);
+    RARRAY(ary2)->ptr += beg;
     RARRAY(ary2)->len = len;
-    RARRAY(ary2)->aux.shared = shared;
-    FL_SET(ary2, ELTS_SHARED);
 
     return ary2;
@@ -969,4 +1102,9 @@ rb_ary_splice(VALUE ary, long beg, long 
 {
     long rlen;
+    long alen;
+    long beg0;
+    long diff;
+    VALUE *ptr;
+    long alen0 = RARRAY(ary)->len;
 
     if (len < 0) rb_raise(rb_eIndexError, "negative length (%ld)", len);
@@ -978,7 +1116,4 @@ rb_ary_splice(VALUE ary, long beg, long 
 	}
     }
-    if (beg + len > RARRAY(ary)->len) {
-	len = RARRAY(ary)->len - beg;
-    }
 
     if (rpl == Qundef) {
@@ -989,39 +1124,108 @@ rb_ary_splice(VALUE ary, long beg, long 
 	rlen = RARRAY(rpl)->len;
     }
+
     rb_ary_modify(ary);
 
-    if (beg >= RARRAY(ary)->len) {
-	len = beg + rlen;
-	if (len >= RARRAY(ary)->aux.capa) {
-	    REALLOC_N(RARRAY(ary)->ptr, VALUE, len);
-	    RARRAY(ary)->aux.capa = len;
-	}
-	rb_mem_clear(RARRAY(ary)->ptr + RARRAY(ary)->len, beg - RARRAY(ary)->len);
-	if (rlen > 0) {
-	    MEMCPY(RARRAY(ary)->ptr + beg, RARRAY(rpl)->ptr, VALUE, rlen);
-	}
-	RARRAY(ary)->len = len;
-    }
-    else {
-	long alen;
+    beg0 = beg;
+    ptr = RARRAY(ary)->ptr;
 
-	if (beg + len > RARRAY(ary)->len) {
-	    len = RARRAY(ary)->len - beg;
-	}
+    if (beg + len > RARRAY(ary)->len) {
+	len = RARRAY(ary)->len - beg;
+	if (len<0) beg0 = beg + len;
+    }
 
-	alen = RARRAY(ary)->len + rlen - len;
-	if (alen >= RARRAY(ary)->aux.capa) {
-	    REALLOC_N(RARRAY(ary)->ptr, VALUE, alen);
-	    RARRAY(ary)->aux.capa = alen;
-	}
+    diff = rlen - len;
+    alen = alen0 + diff;
 
-	if (len != rlen) {
-	    MEMMOVE(RARRAY(ary)->ptr + beg + rlen, RARRAY(ary)->ptr + beg + len,
-		    VALUE, RARRAY(ary)->len - (beg + len));
-	    RARRAY(ary)->len = alen;
+    if (diff) {
+	long lfree = FL_TEST(ary, ELTS_LFREE) ? ptr[-1] : 0;
+	long rfree = FL_TEST(ary, ELTS_RFREE) ? ptr[alen0] : 0;
+	if (beg >= alen / 2) {
+	    rfree -= diff;
+	    if (rfree<0) {
+		long free2 = alen / 2;
+		if (lfree > free2 / 2) {
+		    lfree = free2 / 2;
+		}
+		rfree = free2 - lfree;
+		ptr = ALLOC_N(VALUE, alen + free2) + lfree;
+		MEMCPY(ptr, RARRAY(ary)->ptr, VALUE, beg0);
+		MEMCPY(ptr + beg + rlen, RARRAY(ary)->ptr + beg + len,
+		       VALUE, alen0 - (beg + len));
+		if (rlen > 0) {
+		    MEMMOVE(ptr + beg, RARRAY(rpl)->ptr, VALUE, rlen);
+		}
+		rb_ary_free(ary);
+		if (lfree) {
+		    ptr[-1] = lfree;
+		    FL_SET(ary, ELTS_LFREE);
+		}
+		else {
+		    FL_UNSET(ary, ELTS_LFREE);
+		}
+		RARRAY(ary)->ptr = ptr;
+	    }
+	    else {
+		MEMMOVE(ptr + beg + rlen, ptr + beg + len,
+			VALUE, alen0 - (beg + len));
+		if (rlen > 0) {
+		    MEMMOVE(ptr + beg, RARRAY(rpl)->ptr, VALUE, rlen);
+		}
+	    }
+	    if (rfree) {
+		ptr[alen] = rfree;
+		FL_SET(ary, ELTS_RFREE);
+	    }
+	    else {
+		FL_UNSET(ary, ELTS_RFREE);
+	    }
 	}
-	if (rlen > 0) {
-	    MEMMOVE(RARRAY(ary)->ptr + beg, RARRAY(rpl)->ptr, VALUE, rlen);
+	else {
+	    ptr -= diff;
+	    lfree -= diff;
+	    if (lfree < 0) {
+		long free2 = alen / 2;
+		if (rfree > free2 / 2) {
+		    rfree = free2 / 2;
+		}
+		lfree = free2 - rfree;
+		ptr = ALLOC_N(VALUE, alen + free2) + lfree;
+		MEMCPY(ptr, RARRAY(ary)->ptr, VALUE, beg0);
+		MEMCPY(ptr + beg + rlen, RARRAY(ary)->ptr + beg + len,
+		       VALUE, alen0 - (beg + len));
+		if (rlen > 0) {
+		    MEMMOVE(ptr + beg, RARRAY(rpl)->ptr, VALUE, rlen);
+		}
+		rb_ary_free(ary);
+		if (rfree) {
+		    ptr[alen] = rfree;
+		    FL_SET(ary, ELTS_RFREE);
+		}
+		else {
+		    FL_UNSET(ary, ELTS_RFREE);
+		}
+	    }
+	    else {
+		MEMMOVE(ptr, ptr + diff, VALUE, beg0);
+		if (rlen > 0) {
+		    MEMMOVE(ptr + beg, RARRAY(rpl)->ptr, VALUE, rlen);
+		}
+	    }
+	    if (lfree) {
+		ptr[-1] = lfree;
+		FL_SET(ary, ELTS_LFREE);
+	    }
+	    else {
+		FL_UNSET(ary, ELTS_LFREE);
+	    }
+	    RARRAY(ary)->ptr = ptr;
 	}
+	RARRAY(ary)->len = alen;
+    }
+    else if (rlen > 0) {
+	MEMMOVE(ptr + beg, RARRAY(rpl)->ptr, VALUE, rlen);
+    }
+    if (len < 0) {
+	rb_mem_clear(ptr + beg0, -len);
     }
 }
@@ -1731,4 +1935,5 @@ rb_ary_delete(VALUE ary, VALUE item)
 {
     long i1, i2;
+    long capa;
 
     for (i1 = i2 = 0; i1 < RARRAY(ary)->len; i1++) {
@@ -1749,10 +1954,21 @@ rb_ary_delete(VALUE ary, VALUE item)
 
     rb_ary_modify(ary);
+    capa = ary_capa(ary);
     if (RARRAY(ary)->len > i2) {
 	RARRAY(ary)->len = i2;
-	if (i2 * 2 < RARRAY(ary)->aux.capa &&
-	    RARRAY(ary)->aux.capa > ARY_DEFAULT_SIZE) {
-	    REALLOC_N(RARRAY(ary)->ptr, VALUE, i2 * 2);
-	    RARRAY(ary)->aux.capa = i2 * 2;
+	if (i2 * 2 < capa &&
+	    capa > ARY_DEFAULT_SIZE) {
+	    long lfree = lfree_len(ary);
+	    RARRAY(ary)->ptr -= lfree;
+	    REALLOC_N(RARRAY(ary)->ptr, VALUE, i2 * 2 + lfree);
+	    RARRAY(ary)->ptr += lfree;
+	    capa = i2 * 2;
+	}
+	if (capa>i2) {
+	    RARRAY(ary)->ptr[i2] = capa-i2;
+	    FL_SET(ary, ELTS_RFREE);
+	}
+	else {
+	    FL_UNSET(ary, ELTS_RFREE);
 	}
     }
@@ -1764,6 +1980,8 @@ VALUE
 rb_ary_delete_at(VALUE ary, long pos)
 {
-    long i, len = RARRAY(ary)->len;
+    long len = RARRAY(ary)->len;
     VALUE del;
+    long rfree;
+    VALUE *ptr;
 
     if (pos >= len) return Qnil;
@@ -1774,9 +1992,26 @@ rb_ary_delete_at(VALUE ary, long pos)
 
     rb_ary_modify(ary);
-    del = RARRAY(ary)->ptr[pos];
-    for (i = pos + 1; i < len; i++, pos++) {
-	RARRAY(ary)->ptr[pos] = RARRAY(ary)->ptr[i];
+    ptr = RARRAY(ary)->ptr;
+    del = ptr[pos];
+    if (pos >= len / 2) {
+	VALUE *eptr = ptr + len;
+	long rfree = FL_TEST(ary, ELTS_RFREE) ? *eptr : 0;
+	--len;
+	--eptr;
+	ptr += pos;
+	MEMMOVE(ptr, ptr + 1, VALUE, len - pos);
+	RARRAY(ary)->len = len;
+	*eptr = rfree + 1;
+	FL_SET(ary, ELTS_RFREE);
+    }
+    else {
+	long lfree = FL_TEST(ary, ELTS_LFREE) ? ptr[-1] : 0;
+	--len;
+	MEMMOVE(ptr + 1, ptr, VALUE, pos);
+	*ptr = lfree + 1;
+	RARRAY(ary)->len = len;
+	RARRAY(ary)->ptr = ptr + 1;
+	FL_SET(ary, ELTS_LFREE);
     }
-    RARRAY(ary)->len = pos;
 
     return del;
@@ -1867,7 +2102,9 @@ rb_ary_reject_bang(VALUE ary)
 {
     long i1, i2;
+    long rfree;
 
     RETURN_ENUMERATOR(ary, 0, 0);
     rb_ary_modify(ary);
+    rfree = rfree_len(ary);
     for (i1 = i2 = 0; i1 < RARRAY(ary)->len; i1++) {
 	VALUE v = RARRAY(ary)->ptr[i1];
@@ -1879,6 +2116,10 @@ rb_ary_reject_bang(VALUE ary)
     }
     if (RARRAY(ary)->len == i2) return Qnil;
-    if (i2 < RARRAY(ary)->len)
+    if (i2 < RARRAY(ary)->len) {
+	rfree += RARRAY(ary)->len-i2;
+	RARRAY(ary)->ptr[i2] = rfree;
+	FL_SET(ary, ELTS_RFREE);
 	RARRAY(ary)->len = i2;
+    }
 
     return ary;
@@ -2032,16 +2273,9 @@ static VALUE
 rb_ary_replace(VALUE copy, VALUE orig)
 {
-    VALUE shared;
-
     rb_ary_modify(copy);
     orig = to_ary(orig);
     if (copy == orig) return copy;
-    shared = ary_make_shared(orig);
-    if (RARRAY(copy)->ptr && !FL_TEST(copy, ELTS_SHARED))
-	free(RARRAY(copy)->ptr);
-    RARRAY(copy)->ptr = RARRAY(orig)->ptr;
-    RARRAY(copy)->len = RARRAY(orig)->len;
-    RARRAY(copy)->aux.shared = shared;
-    FL_SET(copy, ELTS_SHARED);
+    rb_ary_free(copy);
+    ary_share(copy, orig);
 
     return copy;
@@ -2061,10 +2295,18 @@ VALUE
 rb_ary_clear(VALUE ary)
 {
+    long capa;
+
     rb_ary_modify(ary);
+    capa = ary_capa(ary);
     RARRAY(ary)->len = 0;
-    if (ARY_DEFAULT_SIZE * 2 < RARRAY(ary)->aux.capa) {
-	REALLOC_N(RARRAY(ary)->ptr, VALUE, ARY_DEFAULT_SIZE * 2);
-	RARRAY(ary)->aux.capa = ARY_DEFAULT_SIZE * 2;
+    if (ARY_DEFAULT_SIZE * 2 < capa) {
+	long lfree = lfree_len(ary);
+	RARRAY(ary)->ptr -= lfree;
+	REALLOC_N(RARRAY(ary)->ptr, VALUE, ARY_DEFAULT_SIZE * 2 + lfree);
+	RARRAY(ary)->ptr += lfree;
+	capa = ARY_DEFAULT_SIZE * 2;
     }
+    RARRAY(ary)->ptr[0] = capa;
+    FL_SET(ary, ELTS_RFREE);
     return ary;
 }
@@ -2132,11 +2374,22 @@ rb_ary_fill(int argc, VALUE *argv, VALUE
     end = beg + len;
     if (end > RARRAY(ary)->len) {
-	if (end >= RARRAY(ary)->aux.capa) {
-	    REALLOC_N(RARRAY(ary)->ptr, VALUE, end);
-	    RARRAY(ary)->aux.capa = end;
+	long capa = ary_capa(ary);
+	if (end >= capa) {
+	    long lfree = lfree_len(ary);
+	    RARRAY(ary)->ptr -= lfree;
+	    REALLOC_N(RARRAY(ary)->ptr, VALUE, end + lfree);
+	    RARRAY(ary)->ptr += lfree;
+	    capa = end;
 	}
 	if (beg > RARRAY(ary)->len) {
 	    rb_mem_clear(RARRAY(ary)->ptr + RARRAY(ary)->len, end - RARRAY(ary)->len);
 	}
+	if (capa > end) {
+	    RARRAY(ary)->ptr[end] = capa-end;
+	    FL_SET(ary, ELTS_RFREE);
+	}
+	else {
+	    FL_UNSET(ary, ELTS_RFREE);
+	}
 	RARRAY(ary)->len = end;
     }
@@ -2612,4 +2865,5 @@ rb_ary_uniq_bang(VALUE ary)
     VALUE hash, v, vv;
     long i, j;
+    long capa;
 
     hash = ary_make_hash(ary, 0);
@@ -2618,5 +2872,5 @@ rb_ary_uniq_bang(VALUE ary)
 	return Qnil;
     }
-    for (i=j=0; i<RARRAY(ary)->len; i++) {
+    for (i = j = 0; i<RARRAY(ary)->len; i++) {
 	v = vv = rb_ary_elt(ary, i);
 	if (st_delete(RHASH(hash)->tbl, (st_data_t*)&vv, 0)) {
@@ -2624,5 +2878,13 @@ rb_ary_uniq_bang(VALUE ary)
 	}
     }
+    capa = ary_capa(ary);
     RARRAY(ary)->len = j;
+    if (capa > j) {
+	RARRAY(ary)->ptr[j] = capa - j;
+	FL_SET(ary, ELTS_RFREE);
+    }
+    else {
+	FL_UNSET(ary, ELTS_RFREE);
+    }
 
     return ary;
@@ -2662,4 +2924,5 @@ rb_ary_compact_bang(VALUE ary)
 {
     VALUE *p, *t, *end;
+    long lfree;
 
     rb_ary_modify(ary);
@@ -2674,6 +2937,10 @@ rb_ary_compact_bang(VALUE ary)
 	return Qnil;
     }
-    RARRAY(ary)->len = RARRAY(ary)->aux.capa = (p - RARRAY(ary)->ptr);
-    REALLOC_N(RARRAY(ary)->ptr, VALUE, RARRAY(ary)->len);
+    RARRAY(ary)->len = (p - RARRAY(ary)->ptr);
+    lfree = lfree_len(ary);
+    RARRAY(ary)->ptr -= lfree;
+    REALLOC_N(RARRAY(ary)->ptr, VALUE, RARRAY(ary)->len + lfree);
+    RARRAY(ary)->ptr += lfree;
+    FL_UNSET(ary, ELTS_RFREE);
 
     return ary;
Index: gc.c
===================================================================
RCS file: /cvs/ruby/src/ruby/gc.c,v
retrieving revision 1.211
diff -U2 -p -r1.211 gc.c
--- gc.c	17 Sep 2005 16:10:53 -0000	1.211
+++ gc.c	22 Sep 2005 02:07:34 -0000
@@ -873,9 +873,5 @@ gc_mark_children(VALUE ptr, int lev)
 
       case T_ARRAY:
-	if (FL_TEST(obj, ELTS_SHARED)) {
-	    ptr = obj->as.array.aux.shared;
-	    goto again;
-	}
-	else {
+	{
 	    long i, len = obj->as.array.len;
 	    VALUE *ptr = obj->as.array.ptr;
@@ -1114,7 +1110,5 @@ obj_free(VALUE obj)
 	break;
       case T_ARRAY:
-	if (RANY(obj)->as.array.ptr && !FL_TEST(obj, ELTS_SHARED)) {
-	    RUBY_CRITICAL(free(RANY(obj)->as.array.ptr));
-	}
+	rb_ary_free(obj);
 	break;
       case T_HASH:
Index: intern.h
===================================================================
RCS file: /cvs/ruby/src/ruby/intern.h,v
retrieving revision 1.182
diff -U2 -p -r1.182 intern.h
--- intern.h	14 Sep 2005 08:30:15 -0000	1.182
+++ intern.h	22 Sep 2005 02:12:44 -0000
@@ -41,4 +41,5 @@ VALUE rb_values_new2(long, const VALUE *
 VALUE rb_values_from_ary(VALUE);
 VALUE rb_ary_from_values(VALUE);
+void rb_ary_free(VALUE);
 VALUE rb_ary_freeze(VALUE);
 VALUE rb_ary_aref(int, VALUE*, VALUE);
Index: ruby.h
===================================================================
RCS file: /cvs/ruby/src/ruby/ruby.h,v
retrieving revision 1.123
diff -U2 -p -r1.123 ruby.h
--- ruby.h	16 Sep 2005 13:46:03 -0000	1.123
+++ ruby.h	22 Sep 2005 02:07:34 -0000
@@ -347,5 +347,8 @@ struct RFloat {
 };
 
+/* SHARED implies not (LFREE or RFREE) */
 #define ELTS_SHARED FL_USER2
+#define ELTS_LFREE FL_USER3
+#define ELTS_RFREE FL_USER4
 
 struct RString {
@@ -362,8 +365,5 @@ struct RArray {
     struct RBasic basic;
     long len;
-    union {
-	long capa;
-	VALUE shared;
-    } aux;
+    VALUE shared;
     VALUE *ptr;
 };
