Use an object pool for stack nodes, to reduce allocations
Also, fix some leaks in the case where memory allocation failed during parsing
This commit is contained in:
parent
a302ee822a
commit
c96c4a08e6
8 changed files with 196 additions and 103 deletions
|
|
@ -10,6 +10,7 @@
|
|||
#define MAX_SUCCESSOR_COUNT 8
|
||||
#define INITIAL_HEAD_CAPACITY 3
|
||||
#define STARTING_TREE_CAPACITY 10
|
||||
#define MAX_NODE_POOL_SIZE 50
|
||||
|
||||
typedef struct StackNode {
|
||||
StackEntry entry;
|
||||
|
|
@ -24,6 +25,7 @@ struct Stack {
|
|||
int head_capacity;
|
||||
Vector pop_results;
|
||||
Vector pop_paths;
|
||||
Vector node_pool;
|
||||
void *tree_selection_payload;
|
||||
TreeSelectionFunction tree_selection_function;
|
||||
};
|
||||
|
|
@ -50,20 +52,26 @@ Stack *ts_stack_new() {
|
|||
|
||||
self->head_count = 1;
|
||||
self->head_capacity = INITIAL_HEAD_CAPACITY;
|
||||
self->heads = NULL;
|
||||
self->pop_results = vector_new(sizeof(StackPopResult));
|
||||
self->pop_paths = vector_new(sizeof(PopPath));
|
||||
self->node_pool = vector_new(sizeof(StackNode *));
|
||||
self->tree_selection_payload = NULL;
|
||||
self->tree_selection_function = ts_stack__default_tree_selection;
|
||||
|
||||
self->heads = ts_calloc(INITIAL_HEAD_CAPACITY, sizeof(StackNode *));
|
||||
if (!self->heads)
|
||||
goto error;
|
||||
|
||||
self->pop_results = vector_new(sizeof(StackPopResult), 4);
|
||||
if (!vector_valid(&self->pop_results))
|
||||
if (!vector_grow(&self->pop_results, 4))
|
||||
goto error;
|
||||
|
||||
self->pop_paths = vector_new(sizeof(PopPath), 4);
|
||||
if (!vector_valid(&self->pop_paths))
|
||||
if (!vector_grow(&self->pop_paths, 4))
|
||||
goto error;
|
||||
|
||||
if (!vector_grow(&self->node_pool, 20))
|
||||
goto error;
|
||||
|
||||
self->tree_selection_payload = NULL;
|
||||
self->tree_selection_function = ts_stack__default_tree_selection;
|
||||
return self;
|
||||
|
||||
error:
|
||||
|
|
@ -74,6 +82,8 @@ error:
|
|||
vector_delete(&self->pop_results);
|
||||
if (self->pop_paths.contents)
|
||||
vector_delete(&self->pop_paths);
|
||||
if (self->node_pool.contents)
|
||||
vector_delete(&self->node_pool);
|
||||
ts_free(self);
|
||||
}
|
||||
return NULL;
|
||||
|
|
@ -127,40 +137,50 @@ static void stack_node_retain(StackNode *self) {
|
|||
self->ref_count++;
|
||||
}
|
||||
|
||||
static bool stack_node_release(StackNode *self) {
|
||||
if (!self)
|
||||
static bool stack_node_release(Stack *self, StackNode *node) {
|
||||
if (!node)
|
||||
return false;
|
||||
assert(self->ref_count != 0);
|
||||
self->ref_count--;
|
||||
if (self->ref_count == 0) {
|
||||
for (int i = 0; i < self->successor_count; i++)
|
||||
stack_node_release(self->successors[i]);
|
||||
ts_tree_release(self->entry.tree);
|
||||
ts_free(self);
|
||||
assert(node->ref_count != 0);
|
||||
node->ref_count--;
|
||||
if (node->ref_count == 0) {
|
||||
for (int i = 0; i < node->successor_count; i++)
|
||||
stack_node_release(self, node->successors[i]);
|
||||
ts_tree_release(node->entry.tree);
|
||||
|
||||
if (self->node_pool.size >= MAX_NODE_POOL_SIZE)
|
||||
ts_free(node);
|
||||
else
|
||||
vector_push(&self->node_pool, &node);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static StackNode *stack_node_new(StackNode *next, TSStateId state, TSTree *tree) {
|
||||
static StackNode *stack_node_new(Stack *self, StackNode *next, TSStateId state, TSTree *tree) {
|
||||
assert(tree->ref_count > 0);
|
||||
StackNode *self = ts_malloc(sizeof(StackNode));
|
||||
if (!self)
|
||||
return NULL;
|
||||
StackNode *node;
|
||||
if (self->node_pool.size == 0) {
|
||||
node = ts_malloc(sizeof(StackNode));
|
||||
if (!node)
|
||||
return NULL;
|
||||
} else {
|
||||
node = *(StackNode **)vector_pop(&self->node_pool);
|
||||
}
|
||||
|
||||
ts_tree_retain(tree);
|
||||
stack_node_retain(next);
|
||||
TSLength position = ts_tree_total_size(tree);
|
||||
if (next)
|
||||
position = ts_length_add(next->entry.position, position);
|
||||
*self = (StackNode){
|
||||
*node = (StackNode){
|
||||
.ref_count = 1,
|
||||
.successor_count = 1,
|
||||
.successors = { next, NULL, NULL },
|
||||
.entry = {.state = state, .tree = tree, .position = position },
|
||||
};
|
||||
return self;
|
||||
return node;
|
||||
}
|
||||
|
||||
static void ts_stack__add_alternative_tree(Stack *self, StackNode *node,
|
||||
|
|
@ -225,7 +245,7 @@ static int ts_stack__find_head(Stack *self, StackNode *node) {
|
|||
}
|
||||
|
||||
void ts_stack_remove_head(Stack *self, int head_index) {
|
||||
stack_node_release(self->heads[head_index]);
|
||||
stack_node_release(self, self->heads[head_index]);
|
||||
for (int i = head_index; i < self->head_count - 1; i++)
|
||||
self->heads[i] = self->heads[i + 1];
|
||||
self->head_count--;
|
||||
|
|
@ -262,11 +282,11 @@ StackPushResult ts_stack_push(Stack *self, int head_index, TSStateId state,
|
|||
if (ts_stack__merge_head(self, head_index, state, tree, position))
|
||||
return StackPushResultMerged;
|
||||
|
||||
StackNode *new_head = stack_node_new(self->heads[head_index], state, tree);
|
||||
StackNode *new_head = stack_node_new(self, self->heads[head_index], state, tree);
|
||||
if (!new_head)
|
||||
return StackPushResultFailed;
|
||||
|
||||
stack_node_release(self->heads[head_index]);
|
||||
stack_node_release(self, self->heads[head_index]);
|
||||
self->heads[head_index] = new_head;
|
||||
return StackPushResultContinued;
|
||||
}
|
||||
|
|
@ -292,11 +312,11 @@ Vector ts_stack_pop(Stack *self, int head_index, int child_count,
|
|||
PopPath initial_path = {
|
||||
.goal_tree_count = child_count,
|
||||
.node = previous_head,
|
||||
.trees = vector_new(sizeof(TSTree *), capacity),
|
||||
.trees = vector_new(sizeof(TSTree *)),
|
||||
.is_shared = false,
|
||||
};
|
||||
|
||||
if (!vector_valid(&initial_path.trees))
|
||||
if (!vector_grow(&initial_path.trees, capacity))
|
||||
goto error;
|
||||
|
||||
if (!vector_push(&self->pop_paths, &initial_path))
|
||||
|
|
@ -382,11 +402,12 @@ Vector ts_stack_pop(Stack *self, int head_index, int child_count,
|
|||
goto error;
|
||||
}
|
||||
|
||||
stack_node_release(previous_head);
|
||||
stack_node_release(self, previous_head);
|
||||
return self->pop_results;
|
||||
|
||||
error:
|
||||
return vector_new(0, 0);
|
||||
vector_delete(&initial_path.trees);
|
||||
return vector_new(0);
|
||||
}
|
||||
|
||||
void ts_stack_shrink(Stack *self, int head_index, int count) {
|
||||
|
|
@ -398,13 +419,13 @@ void ts_stack_shrink(Stack *self, int head_index, int count) {
|
|||
new_head = new_head->successors[0];
|
||||
}
|
||||
stack_node_retain(new_head);
|
||||
stack_node_release(head);
|
||||
stack_node_release(self, head);
|
||||
self->heads[head_index] = new_head;
|
||||
}
|
||||
|
||||
void ts_stack_clear(Stack *self) {
|
||||
for (int i = 0; i < self->head_count; i++)
|
||||
stack_node_release(self->heads[i]);
|
||||
stack_node_release(self, self->heads[i]);
|
||||
self->head_count = 1;
|
||||
self->heads[0] = NULL;
|
||||
}
|
||||
|
|
@ -416,9 +437,17 @@ void ts_stack_set_tree_selection_callback(Stack *self, void *payload,
|
|||
}
|
||||
|
||||
void ts_stack_delete(Stack *self) {
|
||||
vector_delete(&self->pop_results);
|
||||
vector_delete(&self->pop_paths);
|
||||
if (self->pop_paths.contents)
|
||||
vector_delete(&self->pop_results);
|
||||
if (self->pop_paths.contents)
|
||||
vector_delete(&self->pop_paths);
|
||||
ts_stack_clear(self);
|
||||
for (size_t i = 0; i < self->node_pool.size; i++) {
|
||||
StackNode **node = vector_get(&self->node_pool, i);
|
||||
ts_free(*node);
|
||||
}
|
||||
if (self->node_pool.contents)
|
||||
vector_delete(&self->node_pool);
|
||||
ts_free(self->heads);
|
||||
ts_free(self);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue