#include "obj.h"
#include "out.h"
#include "pmalloc.h"
#include "pvector.h"
#include "valgrind_internal.h"
struct pvector_context {
PMEMobjpool *pop;
struct pvector *vec;
size_t nvalues;
size_t iter;
};
struct pvector_context *
pvector_new(PMEMobjpool *pop, struct pvector *vec)
{
struct pvector_context *ctx = Malloc(sizeof(*ctx));
if (ctx == NULL) {
ERR("!failed to create pvector context");
return NULL;
}
ctx->nvalues = 0;
ctx->pop = pop;
ctx->vec = vec;
ctx->iter = 0;
size_t narrays;
for (narrays = 0; narrays < PVECTOR_MAX_ARRAYS; ++narrays) {
if (vec->arrays[narrays] == 0)
break;
if (narrays != PVECTOR_MAX_ARRAYS - 1 &&
vec->arrays[narrays + 1] != 0)
ctx->nvalues += 1ULL << (narrays + PVECTOR_INIT_SHIFT);
}
if (narrays != 0) {
size_t last_array = narrays - 1;
size_t arr_size = 1ULL << (last_array + PVECTOR_INIT_SHIFT);
uint64_t *arrp = OBJ_OFF_TO_PTR(pop, vec->arrays[last_array]);
size_t nvalues;
for (nvalues = 0; nvalues < arr_size; ++nvalues) {
if (arrp[nvalues] == 0)
break;
}
if (nvalues == 0 && last_array != 0 )
pfree(pop, &vec->arrays[last_array]);
else
ctx->nvalues += nvalues;
}
return ctx;
}
void
pvector_delete(struct pvector_context *ctx)
{
Free(ctx);
}
void
pvector_reinit(struct pvector_context *ctx)
{
VALGRIND_ANNOTATE_NEW_MEMORY(ctx, sizeof(*ctx));
for (size_t n = 1; n < PVECTOR_MAX_ARRAYS; ++n) {
if (ctx->vec->arrays[n] == 0)
break;
size_t arr_size = 1ULL << (n + PVECTOR_INIT_SHIFT);
uint64_t *arrp = OBJ_OFF_TO_PTR(ctx->pop, ctx->vec->arrays[n]);
VALGRIND_ANNOTATE_NEW_MEMORY(arrp, sizeof(*arrp) * arr_size);
}
}
struct array_spec {
size_t idx;
size_t pos;
};
static struct array_spec
pvector_get_array_spec(uint64_t idx)
{
struct array_spec s;
uint64_t pos = idx + PVECTOR_INIT_SIZE;
unsigned hbit = util_mssb_index64(pos);
s.idx = (size_t)(hbit - PVECTOR_INIT_SHIFT);
s.pos = pos ^ (1ULL << hbit);
return s;
}
static int
pvector_array_constr(void *ctx, void *ptr, size_t usable_size, void *arg)
{
PMEMobjpool *pop = ctx;
VALGRIND_ADD_TO_TX(ptr, usable_size);
pmemops_memset_persist(&pop->p_ops, ptr, 0, usable_size);
return 0;
}
uint64_t *
pvector_push_back(struct pvector_context *ctx)
{
uint64_t idx = ctx->nvalues;
struct array_spec s = pvector_get_array_spec(idx);
if (s.idx >= PVECTOR_MAX_ARRAYS) {
ERR("Exceeded maximum number of entries in persistent vector");
return NULL;
}
PMEMobjpool *pop = ctx->pop;
if (ctx->vec->arrays[s.idx] == 0) {
if (s.idx == 0) {
ASSERTeq(util_is_zeroed(ctx->vec,
sizeof(*ctx->vec)), 1);
ctx->vec->arrays[0] = OBJ_PTR_TO_OFF(pop,
&ctx->vec->embedded);
pmemops_persist(&pop->p_ops, &ctx->vec->arrays[0],
sizeof(ctx->vec->arrays[0]));
} else {
size_t arr_size = sizeof(uint64_t) *
(1ULL << (s.idx + PVECTOR_INIT_SHIFT));
if (pmalloc_construct(pop,
&ctx->vec->arrays[s.idx],
arr_size, pvector_array_constr, NULL,
0, OBJ_INTERNAL_OBJECT_MASK, 0) != 0)
return NULL;
}
}
ctx->nvalues++;
uint64_t *arrp = OBJ_OFF_TO_PTR(pop, ctx->vec->arrays[s.idx]);
return &arrp[s.pos];
}
uint64_t
pvector_pop_back(struct pvector_context *ctx, entry_op_callback cb)
{
if (ctx->nvalues == 0)
return 0;
uint64_t idx = ctx->nvalues - 1;
struct array_spec s = pvector_get_array_spec(idx);
uint64_t *arrp = OBJ_OFF_TO_PTR(ctx->pop, ctx->vec->arrays[s.idx]);
uint64_t ret = arrp[s.pos];
if (cb)
cb(ctx->pop, &arrp[s.pos]);
if (s.pos == 0 && s.idx != 0 ) {
#ifdef USE_VG_PMEMCHECK
if (On_valgrind) {
size_t usable_size = palloc_usable_size(&ctx->pop->heap,
ctx->vec->arrays[s.idx]);
VALGRIND_REMOVE_FROM_TX(arrp, usable_size);
}
#endif
pfree(ctx->pop, &ctx->vec->arrays[s.idx]);
}
ctx->nvalues--;
return ret;
}
uint64_t
pvector_nvalues(struct pvector_context *ctx)
{
return ctx->nvalues;
}
static uint64_t
pvector_get(PMEMobjpool *pop, struct pvector *vec, uint64_t idx)
{
struct array_spec s = pvector_get_array_spec(idx);
uint64_t *arrp = OBJ_OFF_TO_PTR(pop, vec->arrays[s.idx]);
return arrp[s.pos];
}
uint64_t
pvector_first(struct pvector_context *ctx)
{
if (ctx->nvalues == 0)
return 0;
ctx->iter = 0;
return pvector_get(ctx->pop, ctx->vec, ctx->iter);
}
uint64_t
pvector_last(struct pvector_context *ctx)
{
if (ctx->nvalues == 0)
return 0;
ctx->iter = ctx->nvalues - 1;
return pvector_get(ctx->pop, ctx->vec, ctx->iter);
}
uint64_t
pvector_prev(struct pvector_context *ctx)
{
if (ctx->iter == 0)
return 0;
ctx->iter--;
return pvector_get(ctx->pop, ctx->vec, ctx->iter);
}
uint64_t
pvector_next(struct pvector_context *ctx)
{
if (ctx->iter == ctx->nvalues - 1)
return 0;
ctx->iter++;
return pvector_get(ctx->pop, ctx->vec, ctx->iter);
}