patch 8.1.0798: changing a blob while iterating over it works strangely
Problem: Changing a blob while iterating over it works strangely. Solution: Make a copy of the Blob before iterating.
This commit is contained in:
22
src/blob.c
22
src/blob.c
@ -57,6 +57,28 @@ rettv_blob_set(typval_T *rettv, blob_T *b)
|
|||||||
++b->bv_refcount;
|
++b->bv_refcount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
blob_copy(typval_T *from, typval_T *to)
|
||||||
|
{
|
||||||
|
int ret = OK;
|
||||||
|
|
||||||
|
to->v_type = VAR_BLOB;
|
||||||
|
if (from->vval.v_blob == NULL)
|
||||||
|
to->vval.v_blob = NULL;
|
||||||
|
else if (rettv_blob_alloc(to) == FAIL)
|
||||||
|
ret = FAIL;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int len = from->vval.v_blob->bv_ga.ga_len;
|
||||||
|
|
||||||
|
if (len > 0)
|
||||||
|
to->vval.v_blob->bv_ga.ga_data =
|
||||||
|
vim_memsave(from->vval.v_blob->bv_ga.ga_data, len);
|
||||||
|
to->vval.v_blob->bv_ga.ga_len = len;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
blob_free(blob_T *b)
|
blob_free(blob_T *b)
|
||||||
{
|
{
|
||||||
|
|||||||
34
src/eval.c
34
src/eval.c
@ -2587,7 +2587,6 @@ eval_for_line(
|
|||||||
char_u *expr;
|
char_u *expr;
|
||||||
typval_T tv;
|
typval_T tv;
|
||||||
list_T *l;
|
list_T *l;
|
||||||
blob_T *b;
|
|
||||||
|
|
||||||
*errp = TRUE; /* default: there is an error */
|
*errp = TRUE; /* default: there is an error */
|
||||||
|
|
||||||
@ -2632,16 +2631,17 @@ eval_for_line(
|
|||||||
}
|
}
|
||||||
else if (tv.v_type == VAR_BLOB)
|
else if (tv.v_type == VAR_BLOB)
|
||||||
{
|
{
|
||||||
b = tv.vval.v_blob;
|
fi->fi_bi = 0;
|
||||||
if (b == NULL)
|
if (tv.vval.v_blob != NULL)
|
||||||
clear_tv(&tv);
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
// No need to increment the refcount, it's already set for
|
typval_T btv;
|
||||||
// the blob being used in "tv".
|
|
||||||
fi->fi_blob = b;
|
// Make a copy, so that the iteration still works when the
|
||||||
fi->fi_bi = 0;
|
// blob is changed.
|
||||||
|
blob_copy(&tv, &btv);
|
||||||
|
fi->fi_blob = btv.vval.v_blob;
|
||||||
}
|
}
|
||||||
|
clear_tv(&tv);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -8076,7 +8076,7 @@ tv_check_lock(int lock, char_u *name, int use_gettext)
|
|||||||
/*
|
/*
|
||||||
* Copy the values from typval_T "from" to typval_T "to".
|
* Copy the values from typval_T "from" to typval_T "to".
|
||||||
* When needed allocates string or increases reference count.
|
* When needed allocates string or increases reference count.
|
||||||
* Does not make a copy of a list or dict but copies the reference!
|
* Does not make a copy of a list, blob or dict but copies the reference!
|
||||||
* It is OK for "from" and "to" to point to the same item. This is used to
|
* It is OK for "from" and "to" to point to the same item. This is used to
|
||||||
* make a copy later.
|
* make a copy later.
|
||||||
*/
|
*/
|
||||||
@ -8216,19 +8216,7 @@ item_copy(
|
|||||||
ret = FAIL;
|
ret = FAIL;
|
||||||
break;
|
break;
|
||||||
case VAR_BLOB:
|
case VAR_BLOB:
|
||||||
to->v_type = VAR_BLOB;
|
ret = blob_copy(from, to);
|
||||||
if (from->vval.v_blob == NULL)
|
|
||||||
to->vval.v_blob = NULL;
|
|
||||||
else if (rettv_blob_alloc(to) == FAIL)
|
|
||||||
ret = FAIL;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int len = from->vval.v_blob->bv_ga.ga_len;
|
|
||||||
|
|
||||||
to->vval.v_blob->bv_ga.ga_data =
|
|
||||||
vim_memsave(from->vval.v_blob->bv_ga.ga_data, len);
|
|
||||||
to->vval.v_blob->bv_ga.ga_len = len;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case VAR_DICT:
|
case VAR_DICT:
|
||||||
to->v_type = VAR_DICT;
|
to->v_type = VAR_DICT;
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
blob_T *blob_alloc(void);
|
blob_T *blob_alloc(void);
|
||||||
int rettv_blob_alloc(typval_T *rettv);
|
int rettv_blob_alloc(typval_T *rettv);
|
||||||
void rettv_blob_set(typval_T *rettv, blob_T *b);
|
void rettv_blob_set(typval_T *rettv, blob_T *b);
|
||||||
|
int blob_copy(typval_T *from, typval_T *to);
|
||||||
void blob_free(blob_T *b);
|
void blob_free(blob_T *b);
|
||||||
void blob_unref(blob_T *b);
|
void blob_unref(blob_T *b);
|
||||||
long blob_len(blob_T *b);
|
long blob_len(blob_T *b);
|
||||||
|
|||||||
@ -154,6 +154,7 @@ func Test_blob_for_loop()
|
|||||||
call assert_equal(i, byte)
|
call assert_equal(i, byte)
|
||||||
let i += 1
|
let i += 1
|
||||||
endfor
|
endfor
|
||||||
|
call assert_equal(4, i)
|
||||||
|
|
||||||
let blob = 0z00
|
let blob = 0z00
|
||||||
call remove(blob, 0)
|
call remove(blob, 0)
|
||||||
@ -161,6 +162,19 @@ func Test_blob_for_loop()
|
|||||||
for byte in blob
|
for byte in blob
|
||||||
call assert_error('loop over empty blob')
|
call assert_error('loop over empty blob')
|
||||||
endfor
|
endfor
|
||||||
|
|
||||||
|
let blob = 0z0001020304
|
||||||
|
let i = 0
|
||||||
|
for byte in blob
|
||||||
|
call assert_equal(i, byte)
|
||||||
|
if i == 1
|
||||||
|
call remove(blob, 0)
|
||||||
|
elseif i == 3
|
||||||
|
call remove(blob, 3)
|
||||||
|
endif
|
||||||
|
let i += 1
|
||||||
|
endfor
|
||||||
|
call assert_equal(5, i)
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_blob_concatenate()
|
func Test_blob_concatenate()
|
||||||
|
|||||||
@ -791,6 +791,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
798,
|
||||||
/**/
|
/**/
|
||||||
797,
|
797,
|
||||||
/**/
|
/**/
|
||||||
|
|||||||
Reference in New Issue
Block a user