summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--generic/tclListObj.c56
-rw-r--r--tests/listRep.test241
2 files changed, 269 insertions, 28 deletions
diff --git a/generic/tclListObj.c b/generic/tclListObj.c
index 40e69db..a76760c 100644
--- a/generic/tclListObj.c
+++ b/generic/tclListObj.c
@@ -1447,7 +1447,7 @@ ListRepRange(
/* Take the opportunity to garbage collect */
/* TODO - we probably do not need the preserveSrcRep here unlike later */
if (!preserveSrcRep) {
- /* T:listrep-1.{4,5,8,9},2.{4,5,6,7} */
+ /* T:listrep-1.{4,5,8,9},2.{4,5,6,7},3.{15,16,17,18} */
ListRepFreeUnreferenced(srcRepPtr);
}
@@ -1510,6 +1510,7 @@ ListRepRange(
if (!preserveSrcRep && srcRepPtr->spanPtr
&& srcRepPtr->spanPtr->refCount <= 1) {
/* If span is not shared reuse it */
+ /* T:listrep-3.{16,18} */
srcRepPtr->spanPtr->spanStart = spanStart;
srcRepPtr->spanPtr->spanLength = rangeLen;
*rangeRepPtr = *srcRepPtr;
@@ -1526,7 +1527,7 @@ ListRepRange(
* is mandated.
*/
if (!preserveSrcRep) {
- /* T:listrep-2.{5,7} */
+ /* T:listrep-2.{5,7},3.{16,18} */
ListRepFreeUnreferenced(rangeRepPtr);
}
} else if (preserveSrcRep || ListRepIsShared(srcRepPtr)) {
@@ -1558,7 +1559,7 @@ ListRepRange(
/* Free leading elements outside range */
if (rangeStart != 0) {
- /* T:listrep-1.4 */
+ /* T:listrep-1.4,3.15 */
ObjArrayDecrRefs(srcElems, 0, rangeStart);
}
/* Ditto for trailing */
@@ -1566,6 +1567,7 @@ ListRepRange(
/* Assert: Because numSrcElems > rangeEnd earlier */
LIST_ASSERT(numAfterRangeEnd >= 0);
if (numAfterRangeEnd != 0) {
+ /* T:listrep-3.17 */
ObjArrayDecrRefs(srcElems, rangeEnd + 1, numAfterRangeEnd);
}
memmove(&srcRepPtr->storePtr->slots[0],
@@ -1576,10 +1578,13 @@ ListRepRange(
srcRepPtr->storePtr->numUsed = rangeLen;
srcRepPtr->storePtr->flags = 0;
if (srcRepPtr->spanPtr) {
- ListSpanDecrRefs(srcRepPtr->spanPtr);
- srcRepPtr->spanPtr = NULL;
+ /* In case the source has a span, update it for consistency */
+ /* T:listrep-3.{15,17} */
+ srcRepPtr->spanPtr->spanStart = srcRepPtr->storePtr->firstUsed;
+ srcRepPtr->spanPtr->spanLength = srcRepPtr->storePtr->numUsed;
}
- *rangeRepPtr = *srcRepPtr; /* Note no ref count increments */
+ rangeRepPtr->storePtr = srcRepPtr->storePtr;
+ rangeRepPtr->spanPtr = NULL;
}
/* TODO - call freezombies here if !preserveSrcRep? */
@@ -2144,14 +2149,14 @@ Tcl_ListObjReplace(
}
if (first == 0) {
/* Delete from front, so return tail. */
- /* T:listrep-1.{4,5},2.{4,5} */
+ /* T:listrep-1.{4,5},2.{4,5},3.{15,16} */
ListRep tailRep;
ListRepRange(&listRep, numToDelete, origListLen-1, 0, &tailRep);
ListObjReplaceRepAndInvalidate(listObj, &tailRep);
return TCL_OK;
} else if ((first+numToDelete) >= origListLen) {
/* Delete from tail, so return head */
- /* T:listrep-1.{8,9},2.{6,7} */
+ /* T:listrep-1.{8,9},2.{6,7},3.{17,18} */
ListRep headRep;
ListRepRange(&listRep, 0, first-1, 0, &headRep);
ListObjReplaceRepAndInvalidate(listObj, &headRep);
@@ -2230,7 +2235,7 @@ Tcl_ListObjReplace(
* free space.
*/
if (numFreeSlots < lenChange && !ListRepIsShared(&listRep)) {
- /* T:listrep-1.{1,3,14,18,21},3.{3,10,11,14} */
+ /* T:listrep-1.{1,3,14,18,21},3.{3,10,11,14,27,32,41} */
ListStore *newStorePtr =
ListStoreReallocate(listRep.storePtr, origListLen + lenChange);
if (newStorePtr == NULL) {
@@ -2318,12 +2323,11 @@ Tcl_ListObjReplace(
* for objects to be inserted in case there is overlap. T:listobj-11.1
*/
if (numToInsert) {
- /* T:listrep-1.{1,3,12,13,14,15,16,17,18,19,20,21} */
- /* T:listrep-3.{2,3,7,8,9,10,11,12,13,14} */
+ /* T:listrep-1.{1,3,12:21},3.{2,3,7:14,23:41} */
ObjArrayIncrRefs(insertObjs, 0, numToInsert);
}
if (numToDelete) {
- /* T:listrep-1.{6,7,12,13,14,15,16,17,18,19,20,21} */
+ /* T:listrep-1.{6,7,12:21},3.{19:41} */
ObjArrayDecrRefs(listObjs, first, numToDelete);
}
@@ -2344,7 +2348,7 @@ Tcl_ListObjReplace(
*/
if (lenChange == 0) {
- /* T:listrep-1.{12,15,19}. Exact fit */
+ /* T:listrep-1.{12,15,19},3.{23,28,33}. Exact fit */
leadShift = 0;
tailShift = 0;
} else if (lenChange < 0) {
@@ -2354,12 +2358,12 @@ Tcl_ListObjReplace(
*/
if (leadSegmentLen > tailSegmentLen) {
/* Tail segment smaller. Insert after lead, move tail down */
- /* T:listrep-1.{7,17,20} */
+ /* T:listrep-1.{7,17,20},3.{21,2229,35} */
leadShift = 0;
tailShift = lenChange;
} else {
/* Lead segment smaller. Insert before tail, move lead up */
- /* T:listrep-1.{6,13,16} */
+ /* T:listrep-1.{6,13,16},3.{19,20,24,34} */
leadShift = -lenChange;
tailShift = 0;
}
@@ -2380,6 +2384,7 @@ Tcl_ListObjReplace(
if (leadSpace >= lenChange
&& (leadSegmentLen < tailSegmentLen || tailSpace < lenChange)) {
/* Move only lead to the front to make more room */
+ /* T:listrep-3.25,36,38, */
leadShift = -lenChange;
tailShift = 0;
/*
@@ -2396,11 +2401,11 @@ Tcl_ListObjReplace(
leadShift -= extraShift;
tailShift = -extraShift; /* Move tail to the front as well */
}
- } /* else T:listrep-3.{7,12} */
+ } /* else T:listrep-3.{7,12,25,38} */
LIST_ASSERT(leadShift >= 0 || leadSpace >= -leadShift);
} else if (tailSpace >= lenChange) {
/* Move only tail segment to the back to make more room. */
- /* T:listrep-3.{8,10,11,14} */
+ /* T:listrep-3.{8,10,11,14,26,27,30,32,37,39,41} */
leadShift = 0;
tailShift = lenChange;
/*
@@ -2409,7 +2414,7 @@ Tcl_ListObjReplace(
if (finalFreeSpace > 1 && (leadSpace == 0 || leadSegmentLen == 0)) {
ListSizeT postShiftTailSpace = tailSpace - lenChange;
if (postShiftTailSpace > (finalFreeSpace/2)) {
- /* T:listrep-1.{1,3,14,18,21},3.{2,3} */
+ /* T:listrep-1.{1,3,14,18,21},3.{2,3,26,27} */
ListSizeT extraShift = postShiftTailSpace - (finalFreeSpace / 2);
tailShift += extraShift;
leadShift = extraShift; /* Move head to the back as well */
@@ -2421,7 +2426,7 @@ Tcl_ListObjReplace(
* Both lead and tail need to be shifted to make room.
* Divide remaining free space equally between front and back.
*/
- /* T:listrep-3.{9,13} */
+ /* T:listrep-3.{9,13,31,40} */
LIST_ASSERT(leadSpace < lenChange);
LIST_ASSERT(tailSpace < lenChange);
@@ -2454,27 +2459,27 @@ Tcl_ListObjReplace(
if (leadShift > 0) {
/* Will happen when we have to make room at bottom */
if (tailShift != 0 && tailSegmentLen != 0) {
- /* T:listrep-1.{1,3,14,18},3.{2,3} */
+ /* T:listrep-1.{1,3,14,18},3.{2,3,26,27} */
ListSizeT tailStart = leadSegmentLen + numToDelete;
memmove(&listObjs[tailStart + tailShift],
&listObjs[tailStart],
tailSegmentLen * sizeof(Tcl_Obj *));
}
if (leadSegmentLen != 0) {
- /* T:listrep-1.{3,6,16,18,21} */
+ /* T:listrep-1.{3,6,16,18,21},3.{19,20,34} */
memmove(&listObjs[leadShift],
&listObjs[0],
leadSegmentLen * sizeof(Tcl_Obj *));
}
} else {
if (leadShift != 0 && leadSegmentLen != 0) {
- /* T:listrep-3.{7,9,12,13} */
+ /* T:listrep-3.{7,9,12,13,31,36,38,40} */
memmove(&listObjs[leadShift],
&listObjs[0],
leadSegmentLen * sizeof(Tcl_Obj *));
}
if (tailShift != 0 && tailSegmentLen != 0) {
- /* T:listrep-1.{7,17},3.{8,9,10,11,13,14} */
+ /* T:listrep-1.{7,17},3.{8:11,13,14,21,22,35,37,39:41} */
ListSizeT tailStart = leadSegmentLen + numToDelete;
memmove(&listObjs[tailStart + tailShift],
&listObjs[tailStart],
@@ -2483,8 +2488,7 @@ Tcl_ListObjReplace(
}
if (numToInsert) {
/* Do NOT use ObjArrayCopy here since we have already incr'ed ref counts */
- /* T:listrep-1.{1,3,12,13,14,15,16,17,18,19,20,21} */
- /* T:listrep-3.{2,3,7,8,9,10,11,12,13,14} */
+ /* T:listrep-1.{1,3,12:21},3.{2,3,7:14,23:41} */
memmove(&listObjs[leadSegmentLen + leadShift],
insertObjs,
numToInsert * sizeof(Tcl_Obj *));
@@ -2496,7 +2500,7 @@ Tcl_ListObjReplace(
if (listRep.spanPtr && listRep.spanPtr->refCount <= 1) {
/* An unshared span record, re-use it, even if not required */
- /* T:listrep-3.{2,3,7,8,9,10,11,12,13,14} */
+ /* T:listrep-3.{2,3,7:14},3.{19:41} */
listRep.spanPtr->spanStart = listRep.storePtr->firstUsed;
listRep.spanPtr->spanLength = listRep.storePtr->numUsed;
} else {
diff --git a/tests/listRep.test b/tests/listRep.test
index 5e79e7e..bf5b343 100644
--- a/tests/listRep.test
+++ b/tests/listRep.test
@@ -557,7 +557,7 @@ test listrep-2.18 {
test listrep-3.1 {
Inserts in front of unshared spanned list with room in front should just
- use up the free spots
+ shrink the lead space
} -constraints testlistrep -body {
set l [linsert [freeSpaceBoth] $zero -2 -1]
validate $l
@@ -680,7 +680,244 @@ test listrep-3.14 {
list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
} -result [list {0 1 2 3 4 5 6 8 9 10 7} 1 10 1]
-# TBD - tests on shared spanned lists
+test listrep-3.15 {
+ Deletes from front of small unshared span list results in elements
+ moved up front and span removal
+} -constraints testlistrep -body {
+ set l [lreplace [freeSpaceBoth] $zero $zero]
+ validate $l
+ list $l [leadSpace $l] [tailSpace $l] [hasSpan $l]
+} -result [list {1 2 3 4 5 6 7} 0 7 0]
+
+test listrep-3.16 {
+ Deletes from front of large unshared span list results in another
+ span
+} -constraints testlistrep -body {
+ set l [lreplace [freeSpaceBoth 1000 10 10] $zero $one]
+ validate $l
+ list $l [leadSpace $l] [tailSpace $l] [hasSpan $l 12 998]
+} -result [list [irange 2 999] 12 10 1]
+
+test listrep-3.17 {
+ Deletes from back of small unshared span list results in new store
+ without span
+} -constraints testlistrep -body {
+ set l [lreplace [freeSpaceBoth] $end $end]
+ validate $l
+ list $l [leadSpace $l] [tailSpace $l] [hasSpan $l]
+} -result [list {0 1 2 3 4 5 6} 0 7 0]
+
+test listrep-3.18 {
+ Deletes from back of large unshared span list results in another
+ span
+} -constraints testlistrep -body {
+ set l [lreplace [freeSpaceBoth 1000 10 10] $end-1 $end]
+ validate $l
+ list $l [leadSpace $l] [tailSpace $l] [hasSpan $l 10 998]
+} -result [list [irange 0 997] 10 12 1]
+
+test listrep-3.19 {
+ Deletes from front half of small unshared span list results in
+ movement of smaller front segment
+} -constraints testlistrep -body {
+ set l [lreplace [freeSpaceBoth] $one $two]
+ validate $l
+ list $l [leadSpace $l] [tailSpace $l] [hasSpan $l 5 6]
+} -result [list {0 3 4 5 6 7} 5 3 1]
+
+test listrep-3.20 {
+ Deletes from front half of large unshared span list results in
+ movement of smaller front segment
+} -constraints testlistrep -body {
+ set l [lreplace [freeSpaceBoth 1000 10 10] $one $two]
+ validate $l
+ list $l [leadSpace $l] [tailSpace $l] [hasSpan $l 12 998]
+} -result [list [list 0 {*}[irange 3 999]] 12 10 1]
+
+test listrep-3.21 {
+ Deletes from back half of small unshared span list results in
+ movement of smaller back segment
+} -constraints testlistrep -body {
+ set l [lreplace [freeSpaceBoth] $end-2 $end-1]
+ validate $l
+ list $l [leadSpace $l] [tailSpace $l] [hasSpan $l 3 6]
+} -result [list {0 1 2 3 4 7} 3 5 1]
+
+test listrep-3.22 {
+ Deletes from back half of small unshared span list results in
+ movement of smaller back segment
+} -constraints testlistrep -body {
+ set l [lreplace [freeSpaceBoth 1000 10 10] $end-2 $end-1]
+ validate $l
+ list $l [leadSpace $l] [tailSpace $l] [hasSpan $l 10 998]
+} -result [list [list {*}[irange 0 996] 999] 10 12 1]
+
+test listrep-3.23 {
+ Replacement of elements at front with same number elements in unshared
+ spanned list is in-place
+} -body {
+ set l [lreplace [freeSpaceBoth] $zero $one 10 11]
+ list $l [leadSpace $l] [tailSpace $l]
+} -result [list {10 11 2 3 4 5 6 7} 3 3]
+
+test listrep-3.24 {
+ Replacement of elements at front with fewer elements in unshared
+ spanned list expands leading space
+} -body {
+ set l [lreplace [freeSpaceBoth] $zero $four 10]
+ list $l [leadSpace $l] [tailSpace $l]
+} -result [list {10 5 6 7} 7 3]
+
+test listrep-3.25 {
+ Replacement of elements at front with more elements in unshared
+ spanned list with sufficient leading space shrinks leading space
+} -body {
+ set l [lreplace [freeSpaceBoth] $zero $one 10 11 12]
+ list $l [leadSpace $l] [tailSpace $l]
+} -result [list {10 11 12 2 3 4 5 6 7} 2 3]
+
+test listrep-3.26 {
+ Replacement of elements at front with more elements in unshared
+ spanned list with insufficient leading space but sufficient total
+ free space
+} -constraints testlistrep -body {
+ set l [lreplace [freeSpaceBoth 8 1 10] $zero $one 10 11 12 13]
+ validate $l
+ list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
+} -result [list {10 11 12 13 2 3 4 5 6 7} 5 4 1]
+
+test listrep-3.27 {
+ Replacement of elements at front in unshared spanned list with insufficient
+ total freespace should reallocate with equal free space
+} -constraints testlistrep -body {
+ set l [lreplace [freeSpaceBoth 8 1 1] $zero $one 10 11 12 13 14]
+ validate $l
+ list $l [leadSpace $l] [tailSpace $l] [repStoreRefCount $l]
+} -result [list {10 11 12 13 14 2 3 4 5 6 7} 6 5 1]
+
+test listrep-3.28 {
+ Replacement of elements at back with same number of elements in unshared
+ spanned list is in-place
+} -body {
+ set l [lreplace [freeSpaceBoth] $end-1 $end 10 11]
+ list $l [leadSpace $l] [tailSpace $l]
+} -result [list {0 1 2 3 4 5 10 11} 3 3]
+
+test listrep-3.29 {
+ Replacement of elements at back with fewer elements in unshared
+ spanned list expands tail space
+} -body {
+ set l [lreplace [freeSpaceBoth] $end-2 $end 10]
+ list $l [leadSpace $l] [tailSpace $l]
+} -result [list {0 1 2 3 4 10} 3 5]
+
+test listrep-3.30 {
+ Replacement of elements at back with more elements in unshared
+ spanned list with sufficient tail space shrinks tailspace
+} -body {
+ set l [lreplace [freeSpaceBoth] $end-1 $end 10 11 12]
+ list $l [leadSpace $l] [tailSpace $l]
+} -result [list {0 1 2 3 4 5 10 11 12} 3 2]
+
+test listrep-3.31 {
+ Replacement of elements at back with more elements in unshared spanned list
+ with insufficient tail space but enough total free space moves up the span
+} -body {
+ set l [lreplace [freeSpaceBoth 8 2 2] $end-1 $end 10 11 12 13 14]
+ list $l [leadSpace $l] [tailSpace $l]
+} -result [list {0 1 2 3 4 5 10 11 12 13 14} 0 1]
+
+test listrep-3.32 {
+ Replacement of elements at back with more elements in unshared spanned list
+ with insufficient total space reallocates with more room in the tail because
+ of realloc()
+} -body {
+ set l [lreplace [freeSpaceBoth 8 1 1] $end-1 $end 10 11 12 13 14]
+ list $l [leadSpace $l] [tailSpace $l]
+} -result [list {0 1 2 3 4 5 10 11 12 13 14} 1 10]
+
+test listrep-3.33 {
+ Replacement of elements in the middle in an unshared spanned list with
+ the same number of elements
+} -body {
+ set l [lreplace [freeSpaceBoth] $two $four 10 11 12]
+ list $l [leadSpace $l] [tailSpace $l]
+} -result [list {0 1 10 11 12 5 6 7} 3 3]
+
+test listrep-3.34 {
+ Replacement of elements in an unshared spanned list with fewer elements
+ in the front half moves the front (smaller) segment
+} -body {
+ set l [lreplace [freeSpaceBoth] $two $four 10 11]
+ list $l [leadSpace $l] [tailSpace $l]
+} -result [list {0 1 10 11 5 6 7} 4 3]
+
+test listrep-3.35 {
+ Replacement of elements in an unshared spanned list with fewer elements
+ in the back half moves the tail (smaller) segment
+} -body {
+ set l [lreplace [freeSpaceBoth] $end-2 $end-1 10]
+ list $l [leadSpace $l] [tailSpace $l]
+} -result [list {0 1 2 3 4 10 7} 3 4]
+
+test listrep-3.36 {
+ Replacement of elements in an unshared spanned list with more elements
+ when both front and back have room should move the smaller segment
+ (front case)
+} -body {
+ set l [lreplace [freeSpaceBoth] $one $two 8 9 10]
+ list $l [leadSpace $l] [tailSpace $l]
+} -result [list {0 8 9 10 3 4 5 6 7} 2 3]
+
+test listrep-3.37 {
+ Replacement of elements in an unshared spanned list with more elements
+ when both front and back have room should move the smaller segment
+ (back case)
+} -body {
+ set l [lreplace [freeSpaceBoth] $end-2 $end-1 8 9 10]
+ list $l [leadSpace $l] [tailSpace $l]
+} -result [list {0 1 2 3 4 8 9 10 7} 3 2]
+
+test listrep-3.38 {
+ Replacement of elements in an unshared spanned list with more elements
+ when only front has room
+} -body {
+ set l [lreplace [freeSpaceBoth 8 3 1] $end-1 $end-1 8 9 10]
+ list $l [leadSpace $l] [tailSpace $l]
+} -result [list {0 1 2 3 4 5 8 9 10 7} 1 1]
+
+test listrep-3.39 {
+ Replacement of elements in an unshared spanned list with more elements
+ when only back has room
+} -body {
+ set l [lreplace [freeSpaceBoth 8 1 3] $one $one 8 9 10]
+ list $l [leadSpace $l] [tailSpace $l]
+} -result [list {0 8 9 10 2 3 4 5 6 7} 1 1]
+
+test listrep-3.40 {
+ Replacement of elements in an unshared spanned list with more elements
+ when neither send has enough room by itself
+} -body {
+ set l [lreplace [freeSpaceBoth] $one $one 8 9 10 11 12]
+ list $l [leadSpace $l] [tailSpace $l]
+} -result [list {0 8 9 10 11 12 2 3 4 5 6 7} 1 1]
+
+test listrep-3.41 {
+ Replacement of elements in an unshared spanned list with more elements
+ when there is not enough free space results in new allocation. The back
+ end has more space because of realloc()
+} -body {
+ set l [lreplace [freeSpaceBoth 8 1 1] $one $one 8 9 10 11 12]
+ list $l [leadSpace $l] [tailSpace $l]
+} -result [list {0 8 9 10 11 12 2 3 4 5 6 7} 1 11]
+
+
+
+
+#
+# 4.* - tests on shared spanned lists
+
+
# TBD - tests when tcl-obj is shared but listrep is not (lappend, lset etc.)
# TBD - range and subrange tests
# - spanned and unspanned