Fix memory leaks related to DWARF (#6245)
* fix: memory leak rz_analysis_op_fini op->src * fix: memory leak, replaced `rz_type_clone` with `rz_type_clone_shallow` to resolve the memory leak in `rz_type_free(type.callable)` * fix: memory leak in try_create_var_global * fix: memory leak in dwarf * fix: remove redundant memset calls and add documentation for rz_type_clone_shallow * Update librz/arch/op.c Co-authored-by: Giovanni <561184+wargio@users.noreply.github.com> * fix: set free and free_user to NULL in vector copy when item_cpy is not provided --------- Co-authored-by: Giovanni <561184+wargio@users.noreply.github.com>
This commit is contained in:
parent
346bb6375f
commit
71aa8bf1dd
14 changed files with 88 additions and 60 deletions
|
|
@ -886,7 +886,7 @@ static RZ_OWN RzType *type_parse_from_offset_internal(
|
|||
RZ_BORROW RZ_IN RZ_NONNULL RzSetU *visited) {
|
||||
RzType *type = ht_up_find(ctx->analysis->debug_info->type_by_offset, offset, NULL);
|
||||
if (type) {
|
||||
return rz_type_clone(type);
|
||||
return rz_type_clone_shallow(type);
|
||||
}
|
||||
|
||||
if (rz_set_u_contains(visited, offset)) {
|
||||
|
|
@ -1021,7 +1021,7 @@ static RZ_OWN RzType *type_parse_from_offset_internal(
|
|||
break;
|
||||
}
|
||||
|
||||
RzType *copy = type ? rz_type_clone(type) : NULL;
|
||||
RzType *copy = type ? rz_type_clone_shallow(type) : NULL;
|
||||
if (copy && ht_up_insert(ctx->analysis->debug_info->type_by_offset, offset, copy)) {
|
||||
#if RZ_BUILD_DEBUG
|
||||
char *tstring = rz_type_as_string(ctx->analysis->typedb, type);
|
||||
|
|
@ -1513,7 +1513,7 @@ static bool function_children_parse(
|
|||
}
|
||||
if (v.kind == RZ_ANALYSIS_VAR_KIND_FORMAL_PARAMETER) {
|
||||
RzCallableArg *arg = rz_type_callable_arg_new(
|
||||
ctx->analysis->typedb, v.prefer_name, rz_type_clone(v.type));
|
||||
ctx->analysis->typedb, v.prefer_name, rz_type_clone_shallow(v.type));
|
||||
rz_type_callable_arg_add(callable, arg);
|
||||
}
|
||||
RzAnalysisDwarfVariable *ptr = rz_vector_push(&fn->variables, &v);
|
||||
|
|
@ -1632,7 +1632,7 @@ static bool function_from_die(
|
|||
}
|
||||
|
||||
RzCallable *callable = rz_type_callable_new(fcn->prefer_name);
|
||||
callable->ret = fcn->ret_type ? rz_type_clone(fcn->ret_type) : NULL;
|
||||
callable->ret = fcn->ret_type ? rz_type_clone_shallow(fcn->ret_type) : NULL;
|
||||
rz_vector_init(&fcn->variables, sizeof(RzAnalysisDwarfVariable), (RzVectorFree)variable_fini, NULL);
|
||||
function_children_parse(ctx, die, callable, fcn);
|
||||
|
||||
|
|
@ -1690,7 +1690,7 @@ static bool try_create_var_global(
|
|||
RzBinDwarfLine *dw_line = rz_bin_dwarf_line(ctx->dw);
|
||||
RzBinDwarfLineUnit *lu = ctx->unit && dw_line ? rz_pvector_at(dw_line->units, ctx->unit->index) : NULL;
|
||||
ut64 file_index = attr ? rz_bin_dwarf_attr_udata(attr) : UT64_MAX;
|
||||
const char *file = file_index != 0 && lu ? rz_bin_dwarf_file_path(ctx->dw, lu, file_index) : NULL;
|
||||
char *file = file_index != 0 && lu ? rz_bin_dwarf_file_path(ctx->dw, lu, file_index) : NULL;
|
||||
|
||||
attr = rz_bin_dwarf_die_get_attr(die, DW_AT_decl_line);
|
||||
ut32 line = attr ? rz_bin_dwarf_attr_udata(attr) : UT32_MAX;
|
||||
|
|
@ -1701,6 +1701,7 @@ static bool try_create_var_global(
|
|||
result = rz_analysis_var_global_create_with_sourceline(
|
||||
ctx->analysis, v->prefer_name, v->type, v->location->address,
|
||||
file, line, column);
|
||||
free(file);
|
||||
|
||||
v->type = NULL;
|
||||
beach:
|
||||
|
|
@ -1803,26 +1804,26 @@ RZ_API void rz_analysis_dwarf_preprocess_info(
|
|||
b = temp; \
|
||||
} while (0)
|
||||
|
||||
static inline void update_base_type(const RzTypeDB *typedb, RzBaseType *type) {
|
||||
RzBaseType *t = rz_type_db_get_base_type(typedb, type->name);
|
||||
if (t && t == type) {
|
||||
static inline void update_base_type(const RzTypeDB *typedb, RzBaseType *bt) {
|
||||
RzBaseType *db_bt = rz_type_db_get_base_type(typedb, bt->name);
|
||||
if (db_bt && db_bt == bt) {
|
||||
return;
|
||||
}
|
||||
rz_type_db_update_base_type(typedb, rz_base_type_clone(type));
|
||||
rz_type_db_update_base_type(typedb, rz_base_type_clone(bt));
|
||||
}
|
||||
|
||||
static void db_save_renamed(RzTypeDB *db, RzBaseType *b, char *name) {
|
||||
if (!name) {
|
||||
static void db_save_renamed(RzTypeDB *db, RzBaseType *bt, char *new_name) {
|
||||
if (!new_name) {
|
||||
rz_warn_if_reached();
|
||||
return;
|
||||
}
|
||||
RzBaseType *t = rz_type_db_get_base_type(db, b->name);
|
||||
if (t == b) {
|
||||
RzBaseType *db_bt = rz_type_db_get_base_type(db, bt->name);
|
||||
if (db_bt == bt) {
|
||||
return;
|
||||
}
|
||||
free(b->name);
|
||||
b->name = name;
|
||||
rz_type_db_update_base_type(db, b);
|
||||
free(bt->name);
|
||||
bt->name = new_name;
|
||||
rz_type_db_update_base_type(db, bt);
|
||||
}
|
||||
|
||||
static bool store_base_type(void *u, const char *k, const void *v) {
|
||||
|
|
@ -2053,7 +2054,7 @@ static bool RzAnalysisDwarfVariable_as_RzAnalysisVar(RzAnalysis *a, RzAnalysisFu
|
|||
if (!loc) {
|
||||
return false;
|
||||
}
|
||||
var->type = DW_var->type ? rz_type_clone(DW_var->type) : rz_type_new_default(a->typedb);
|
||||
var->type = DW_var->type ? rz_type_clone_shallow(DW_var->type) : rz_type_new_default(a->typedb);
|
||||
var->name = rz_str_dup(DW_var->prefer_name ? DW_var->prefer_name : "");
|
||||
var->kind = DW_var->kind;
|
||||
var->fcn = f;
|
||||
|
|
@ -2120,6 +2121,7 @@ RZ_API RzAnalysisDebugInfo *rz_analysis_debug_info_new() {
|
|||
debug_info->type_by_offset = ht_up_new(NULL, (HtUPFreeValue)rz_type_free);
|
||||
debug_info->callable_by_offset = ht_up_new(NULL, (HtUPFreeValue)rz_type_callable_free);
|
||||
debug_info->base_type_by_offset = ht_up_new(NULL, (HtUPFreeValue)rz_type_base_type_free);
|
||||
// just for by name lookup, we don't store the same base type pointer here, so no need to free value
|
||||
debug_info->base_types_by_name = ht_sp_new(HT_STR_DUP, NULL, (HtSPFreeValue)rz_pvector_free);
|
||||
debug_info->visited = rz_set_u_new();
|
||||
return debug_info;
|
||||
|
|
|
|||
|
|
@ -38,12 +38,10 @@ RZ_API bool rz_analysis_op_fini(RzAnalysisOp *op) {
|
|||
if (!op) {
|
||||
return false;
|
||||
}
|
||||
rz_analysis_value_free(op->src[0]);
|
||||
rz_analysis_value_free(op->src[1]);
|
||||
rz_analysis_value_free(op->src[2]);
|
||||
op->src[0] = NULL;
|
||||
op->src[1] = NULL;
|
||||
op->src[2] = NULL;
|
||||
for (size_t i = 0; i < RZ_ARRAY_SIZE(op->src); ++i) {
|
||||
rz_analysis_value_free(op->src[i]);
|
||||
op->src[i] = NULL;
|
||||
}
|
||||
rz_analysis_value_free(op->dst);
|
||||
op->dst = NULL;
|
||||
rz_list_free(op->access);
|
||||
|
|
|
|||
|
|
@ -1184,8 +1184,9 @@ static void tricore_fillvals(RzAsmTriCoreContext *ctx, RzAnalysis *a, RzAnalysis
|
|||
av->access |= RZ_ANALYSIS_ACC_W;
|
||||
if (op->dst) {
|
||||
rz_warn_if_reached();
|
||||
continue;
|
||||
}
|
||||
if (srci > 0 && av == op->src[srci - 1]) {
|
||||
if (top->access & CS_AC_READ) {
|
||||
av = rz_mem_dup(av, sizeof(RzAnalysisValue));
|
||||
}
|
||||
op->dst = av;
|
||||
|
|
|
|||
|
|
@ -263,7 +263,7 @@ static char *full_file_path(
|
|||
* \param index the index of the file
|
||||
* \return the full path or NULL if the file index is invalid
|
||||
*/
|
||||
RZ_API char *rz_bin_dwarf_file_path(RZ_NONNULL RZ_BORROW RzBinDWARF *dw,
|
||||
RZ_API RZ_OWN char *rz_bin_dwarf_file_path(RZ_NONNULL RZ_BORROW RzBinDWARF *dw,
|
||||
RZ_NONNULL RZ_BORROW RzBinDwarfLineUnit *lu, ut64 index) {
|
||||
rz_return_val_if_fail(dw && lu, NULL);
|
||||
return full_file_path(dw, &lu->hdr, index);
|
||||
|
|
|
|||
|
|
@ -847,12 +847,12 @@ static bool Evaluation_evaluate_one_operation(
|
|||
goto err;
|
||||
}
|
||||
Value clone = { 0 };
|
||||
if (!Value_clone_into(value, &clone) &&
|
||||
if (Value_clone_into(value, &clone) &&
|
||||
Evaluation_push(self, &clone)) {
|
||||
Value_fini(&clone);
|
||||
goto err;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
Value_fini(&clone);
|
||||
goto err;
|
||||
}
|
||||
case OPERATION_KIND_SWAP: {
|
||||
Value a = { 0 };
|
||||
|
|
@ -1092,6 +1092,7 @@ static bool Evaluation_evaluate_one_operation(
|
|||
out->kind = OperationEvaluationResult_COMPLETE;
|
||||
if (val1.type == RzBinDwarfValueType_LOCATION) {
|
||||
MEM_CPY(Location, &out->complete, val1.location);
|
||||
rz_bin_dwarf_location_free(val1.location);
|
||||
} else {
|
||||
out->complete.kind = RzBinDwarfLocationKind_VALUE;
|
||||
MEM_CPY(Value, &out->complete.value, &val1);
|
||||
|
|
@ -1412,6 +1413,7 @@ RZ_API RZ_OWN RzBinDwarfLocation *rz_bin_dwarf_location_from_block(
|
|||
}
|
||||
|
||||
RzBinDwarfEvaluation *eval = NULL;
|
||||
RzBinDwarfEvaluationResult *result = NULL;
|
||||
Location *loc = RZ_NEW0(Location);
|
||||
if (!loc) {
|
||||
return NULL;
|
||||
|
|
@ -1424,23 +1426,27 @@ RZ_API RZ_OWN RzBinDwarfLocation *rz_bin_dwarf_location_from_block(
|
|||
if (rz_bin_dwarf_block_empty(block)) {
|
||||
loc->kind = RzBinDwarfLocationKind_EMPTY;
|
||||
} else {
|
||||
RzBinDwarfEvaluationResult *result = RZ_NEW0(RzBinDwarfEvaluationResult);
|
||||
RET_NULL_IF_FAIL(result);
|
||||
result = RZ_NEW0(RzBinDwarfEvaluationResult);
|
||||
ERR_IF_FAIL(result);
|
||||
eval = rz_bin_dwarf_evaluation_new_from_block(&R, dw, unit, die);
|
||||
ERR_IF_FAIL(eval);
|
||||
if (!(rz_bin_dwarf_evaluation_evaluate(eval, result) &&
|
||||
RzBinDwarfEvaluationResult_to_loc(eval, result, loc))) {
|
||||
goto err;
|
||||
};
|
||||
}
|
||||
}
|
||||
return loc;
|
||||
err:
|
||||
if (eval && eval->state.kind == EVALUATION_STATE_DECODE_ERROR) {
|
||||
loc->kind = RzBinDwarfLocationKind_DECODE_ERROR;
|
||||
if (eval) {
|
||||
if (eval->state.kind == EVALUATION_STATE_DECODE_ERROR) {
|
||||
loc->kind = RzBinDwarfLocationKind_DECODE_ERROR;
|
||||
}
|
||||
} else {
|
||||
rz_bin_dwarf_location_free(loc);
|
||||
loc = NULL;
|
||||
}
|
||||
rz_bin_dwarf_evaluation_free(eval);
|
||||
RzBinDwarfEvaluationResult_free(result);
|
||||
return loc;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1006,8 +1006,8 @@ RZ_IPI void Value_fini(RzBinDwarfValue *self) {
|
|||
}
|
||||
if (self->type == RzBinDwarfValueType_LOCATION) {
|
||||
rz_bin_dwarf_location_free(self->location);
|
||||
self->location = NULL;
|
||||
}
|
||||
memset(self, 0, sizeof(RzBinDwarfValue));
|
||||
}
|
||||
|
||||
RZ_IPI void Value_free(RzBinDwarfValue *self) {
|
||||
|
|
|
|||
|
|
@ -1579,7 +1579,6 @@ static bool analysis_block_cb(RzAnalysisBlock *bb, BlockRecurseCtx *ctx) {
|
|||
}
|
||||
RzAnalysisOp *op = rz_core_analysis_op(core, pos, RZ_ANALYSIS_OP_MASK_ESIL | RZ_ANALYSIS_OP_MASK_VAL | RZ_ANALYSIS_OP_MASK_HINT);
|
||||
if (!op) {
|
||||
// eprintf ("Cannot get op\n");
|
||||
break;
|
||||
}
|
||||
rz_analysis_extract_rarg(core->analysis, op, fcn, reg_set, &ctx->count);
|
||||
|
|
|
|||
|
|
@ -1766,7 +1766,8 @@ static bool bin_dwarf(RzCore *core, RzBinFile *binfile, RzCmdStateOutput *state)
|
|||
RzStrBuf sb = { 0 };
|
||||
rz_strbuf_init(&sb);
|
||||
rz_bin_dwarf_dump(dw, &sb);
|
||||
rz_cons_strcat(rz_strbuf_drain_nofree(&sb));
|
||||
rz_cons_strcat(rz_strbuf_get(&sb));
|
||||
rz_strbuf_fini(&sb);
|
||||
}
|
||||
RzBinDwarfLine *line = rz_bin_dwarf_line(dw);
|
||||
if (line && line->lines) {
|
||||
|
|
|
|||
|
|
@ -1539,7 +1539,7 @@ RZ_API void rz_bin_dwarf_line_free(RZ_OWN RZ_NULLABLE RzBinDwarfLine *li);
|
|||
RZ_API void rz_bin_dwarf_line_units_dump(
|
||||
RZ_NONNULL RZ_BORROW RzBinDwarfLine *line,
|
||||
RZ_NONNULL RZ_BORROW RzStrBuf *sb);
|
||||
RZ_API char *rz_bin_dwarf_file_path(RZ_NONNULL RZ_BORROW RzBinDWARF *dw,
|
||||
RZ_API RZ_OWN char *rz_bin_dwarf_file_path(RZ_NONNULL RZ_BORROW RzBinDWARF *dw,
|
||||
RZ_NONNULL RZ_BORROW RzBinDwarfLineUnit *lu, ut64 index);
|
||||
|
||||
/// dwarf
|
||||
|
|
|
|||
|
|
@ -298,6 +298,7 @@ RZ_API RZ_BORROW RzType *rz_type_db_base_type_unwrap_typedef(RZ_NONNULL const Rz
|
|||
// Compound types
|
||||
|
||||
RZ_API RZ_OWN RzType *rz_type_clone(RZ_BORROW RZ_NONNULL const RzType *type);
|
||||
RZ_API RZ_OWN RzType *rz_type_clone_shallow(RZ_BORROW RZ_NONNULL const RzType *type);
|
||||
RZ_API RZ_BORROW const char *rz_type_identifier(RZ_NONNULL const RzType *type);
|
||||
RZ_API bool rz_types_equal(RZ_NONNULL const RzType *type1, RZ_NONNULL const RzType *type2);
|
||||
RZ_API RZ_OWN char *rz_type_as_string(const RzTypeDB *typedb, RZ_NONNULL const RzType *type);
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ static void RzTypeStructMember_cpy(RzTypeStructMember *dst, RzTypeStructMember *
|
|||
}
|
||||
memcpy(dst, src, sizeof(RzTypeStructMember));
|
||||
dst->name = rz_str_dup(src->name);
|
||||
dst->type = rz_type_clone(src->type);
|
||||
dst->type = rz_type_clone_shallow(src->type);
|
||||
}
|
||||
|
||||
static void RzTypeEnumCase_cpy(RzTypeEnumCase *dst, RzTypeEnumCase *src) {
|
||||
|
|
@ -147,7 +147,7 @@ static void RzTypeUnionMember_cpy(RzTypeUnionMember *dst, RzTypeUnionMember *src
|
|||
}
|
||||
memcpy(dst, src, sizeof(RzTypeUnionMember));
|
||||
dst->name = rz_str_dup(src->name);
|
||||
dst->type = rz_type_clone(src->type);
|
||||
dst->type = rz_type_clone_shallow(src->type);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -162,7 +162,7 @@ RZ_API bool rz_base_type_clone_into(
|
|||
rz_return_val_if_fail(src && dst, false);
|
||||
rz_mem_copy(dst, sizeof(RzBaseType), src, sizeof(RzBaseType));
|
||||
dst->name = rz_str_dup(src->name);
|
||||
dst->type = src->type ? rz_type_clone(src->type) : NULL;
|
||||
dst->type = src->type ? rz_type_clone_shallow(src->type) : NULL;
|
||||
|
||||
switch (src->kind) {
|
||||
case RZ_BASE_TYPE_KIND_ENUM:
|
||||
|
|
@ -191,15 +191,15 @@ RZ_API RZ_OWN RzBaseType *rz_base_type_clone(RZ_NULLABLE RZ_BORROW RzBaseType *b
|
|||
if (!b) {
|
||||
return NULL;
|
||||
}
|
||||
RzBaseType *type = RZ_NEW0(RzBaseType);
|
||||
if (!type) {
|
||||
RzBaseType *bt = RZ_NEW0(RzBaseType);
|
||||
if (!bt) {
|
||||
return NULL;
|
||||
}
|
||||
if (!rz_base_type_clone_into(type, b)) {
|
||||
rz_type_base_type_free(type);
|
||||
if (!rz_base_type_clone_into(bt, b)) {
|
||||
rz_type_base_type_free(bt);
|
||||
return NULL;
|
||||
}
|
||||
return type;
|
||||
return bt;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ RZ_API RZ_OWN RzCallable *rz_type_callable_clone(RZ_BORROW RZ_NONNULL const RzCa
|
|||
if (!newcallable) {
|
||||
return NULL;
|
||||
}
|
||||
newcallable->ret = callable->ret ? rz_type_clone(callable->ret) : NULL;
|
||||
newcallable->ret = callable->ret ? rz_type_clone_shallow(callable->ret) : NULL;
|
||||
newcallable->name = rz_str_dup(callable->name);
|
||||
newcallable->args = rz_pvector_new((RzPVectorFree)rz_type_callable_arg_free);
|
||||
void **it;
|
||||
|
|
@ -90,7 +90,7 @@ RZ_API RZ_OWN RzCallableArg *rz_type_callable_arg_clone(RZ_BORROW RZ_NONNULL con
|
|||
return NULL;
|
||||
}
|
||||
newarg->name = rz_str_dup(arg->name);
|
||||
newarg->type = rz_type_clone(arg->type);
|
||||
newarg->type = rz_type_clone_shallow(arg->type);
|
||||
return newarg;
|
||||
}
|
||||
|
||||
|
|
@ -366,7 +366,7 @@ RZ_API bool rz_type_func_ret_set(RzTypeDB *typedb, const char *name, RZ_BORROW R
|
|||
return false;
|
||||
}
|
||||
rz_type_free(callable->ret);
|
||||
callable->ret = rz_type_clone(type);
|
||||
callable->ret = rz_type_clone_shallow(type);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1161,12 +1161,7 @@ RZ_API RZ_BORROW const char *rz_type_identifier(RZ_NONNULL const RzType *type) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Creates an exact clone of the RzType
|
||||
*
|
||||
* \param type RzType pointer
|
||||
*/
|
||||
RZ_API RZ_OWN RzType *rz_type_clone(RZ_BORROW RZ_NONNULL const RzType *type) {
|
||||
static RZ_OWN RzType *type_clone(RZ_BORROW RZ_NONNULL const RzType *type, const bool clone_callable) {
|
||||
rz_return_val_if_fail(type, NULL);
|
||||
RzType *newtype = RZ_NEW0(RzType);
|
||||
if (!newtype) {
|
||||
|
|
@ -1182,21 +1177,41 @@ RZ_API RZ_OWN RzType *rz_type_clone(RZ_BORROW RZ_NONNULL const RzType *type) {
|
|||
case RZ_TYPE_KIND_ARRAY:
|
||||
newtype->kind = RZ_TYPE_KIND_ARRAY;
|
||||
newtype->array.count = type->array.count;
|
||||
newtype->array.type = rz_type_clone(type->array.type);
|
||||
newtype->array.type = type_clone(type->array.type, clone_callable);
|
||||
break;
|
||||
case RZ_TYPE_KIND_POINTER:
|
||||
newtype->kind = RZ_TYPE_KIND_POINTER;
|
||||
newtype->pointer.is_const = type->pointer.is_const;
|
||||
newtype->pointer.type = rz_type_clone(type->pointer.type);
|
||||
newtype->pointer.type = type_clone(type->pointer.type, clone_callable);
|
||||
break;
|
||||
case RZ_TYPE_KIND_CALLABLE:
|
||||
newtype->kind = RZ_TYPE_KIND_CALLABLE;
|
||||
newtype->callable = rz_type_callable_clone(type->callable);
|
||||
newtype->callable = clone_callable ? rz_type_callable_clone(type->callable) : type->callable;
|
||||
break;
|
||||
}
|
||||
return newtype;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Creates a shallow clone of the RzType, i.e. the callables are shared between the original and the cloned type
|
||||
* \param type RzType pointer
|
||||
* \return clone of the RzType
|
||||
*/
|
||||
RZ_API RZ_OWN RzType *rz_type_clone_shallow(RZ_BORROW RZ_NONNULL const RzType *type) {
|
||||
rz_return_val_if_fail(type, NULL);
|
||||
return type_clone(type, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Creates an exact clone of the RzType
|
||||
*
|
||||
* \param type RzType pointer
|
||||
*/
|
||||
RZ_API RZ_OWN RzType *rz_type_clone(RZ_BORROW RZ_NONNULL const RzType *type) {
|
||||
rz_return_val_if_fail(type, NULL);
|
||||
return type_clone(type, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Checks if two types are identical
|
||||
*
|
||||
|
|
|
|||
|
|
@ -98,8 +98,13 @@ RZ_API bool rz_vector_clone_intof(
|
|||
dst->capacity = src->capacity;
|
||||
dst->len = src->len;
|
||||
dst->elem_size = src->elem_size;
|
||||
dst->free = NULL;
|
||||
dst->free_user = NULL;
|
||||
if (item_cpy) {
|
||||
dst->free = src->free;
|
||||
dst->free_user = src->free_user;
|
||||
} else {
|
||||
dst->free = NULL;
|
||||
dst->free_user = NULL;
|
||||
}
|
||||
if (!dst->len) {
|
||||
dst->a = NULL;
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Reference in a new issue