blob: b82226302ec5559e90fbd96d62e05fdda0060c61 [file] [log] [blame]
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define C_LUCY_VARRAY
#include <string.h>
#include <stdlib.h>
#define LUCY_USE_SHORT_NAMES
#define CHY_USE_SHORT_NAMES
#include "Lucy/Object/VTable.h"
#include "Lucy/Object/VArray.h"
#include "Lucy/Object/Err.h"
#include "Lucy/Util/Memory.h"
#include "Lucy/Util/Freezer.h"
#include "Lucy/Util/SortUtils.h"
#include "Lucy/Store/InStream.h"
#include "Lucy/Store/OutStream.h"
VArray*
VA_new(uint32_t capacity) {
VArray *self = (VArray*)VTable_Make_Obj(VARRAY);
VA_init(self, capacity);
return self;
}
VArray*
VA_init(VArray *self, uint32_t capacity) {
// Init.
self->size = 0;
// Assign.
self->cap = capacity;
// Derive.
self->elems = (Obj**)CALLOCATE(capacity, sizeof(Obj*));
return self;
}
void
VA_destroy(VArray *self) {
if (self->elems) {
Obj **elems = self->elems;
Obj **const limit = elems + self->size;
for (; elems < limit; elems++) {
DECREF(*elems);
}
FREEMEM(self->elems);
}
SUPER_DESTROY(self, VARRAY);
}
VArray*
VA_dump(VArray *self) {
VArray *dump = VA_new(self->size);
uint32_t i, max;
for (i = 0, max = self->size; i < max; i++) {
Obj *elem = VA_Fetch(self, i);
if (elem) { VA_Store(dump, i, Obj_Dump(elem)); }
}
return dump;
}
VArray*
VA_load(VArray *self, Obj *dump) {
VArray *source = (VArray*)CERTIFY(dump, VARRAY);
VArray *loaded = VA_new(source->size);
uint32_t i, max;
UNUSED_VAR(self);
for (i = 0, max = source->size; i < max; i++) {
Obj *elem_dump = VA_Fetch(source, i);
if (elem_dump) {
VA_Store(loaded, i, Obj_Load(elem_dump, elem_dump));
}
}
return loaded;
}
void
VA_serialize(VArray *self, OutStream *outstream) {
uint32_t i;
uint32_t last_valid_tick = 0;
OutStream_Write_C32(outstream, self->size);
for (i = 0; i < self->size; i++) {
Obj *elem = self->elems[i];
if (elem) {
OutStream_Write_C32(outstream, i - last_valid_tick);
FREEZE(elem, outstream);
last_valid_tick = i;
}
}
// Terminate.
OutStream_Write_C32(outstream, self->size - last_valid_tick);
}
VArray*
VA_deserialize(VArray *self, InStream *instream) {
uint32_t tick;
uint32_t size = InStream_Read_C32(instream);
if (self) {
self->size = size;
self->cap = size + 1;
self->elems = (Obj**)CALLOCATE(self->cap, sizeof(Obj*));
}
else { self = VA_new(size); }
for (tick = InStream_Read_C32(instream);
tick < size;
tick += InStream_Read_C32(instream)
) {
Obj *obj = THAW(instream);
self->elems[tick] = obj;
}
self->size = size;
return self;
}
VArray*
VA_clone(VArray *self) {
uint32_t i;
VArray *twin = VA_new(self->size);
// Clone each element.
for (i = 0; i < self->size; i++) {
Obj *elem = self->elems[i];
if (elem) {
twin->elems[i] = Obj_Clone(elem);
}
}
// Ensure that size is the same if NULL elems at end.
twin->size = self->size;
return twin;
}
VArray*
VA_shallow_copy(VArray *self) {
uint32_t i;
VArray *twin;
Obj **elems;
// Dupe, then increment refcounts.
twin = VA_new(self->size);
elems = twin->elems;
memcpy(elems, self->elems, self->size * sizeof(Obj*));
twin->size = self->size;
for (i = 0; i < self->size; i++) {
if (elems[i] != NULL) {
(void)INCREF(elems[i]);
}
}
return twin;
}
void
VA_push(VArray *self, Obj *element) {
if (self->size == self->cap) {
VA_Grow(self, Memory_oversize(self->size + 1, sizeof(Obj*)));
}
self->elems[self->size] = element;
self->size++;
}
void
VA_push_varray(VArray *self, VArray *other) {
uint32_t i;
uint32_t tick = self->size;
uint32_t new_size = self->size + other->size;
if (new_size > self->cap) {
VA_Grow(self, Memory_oversize(new_size, sizeof(Obj*)));
}
for (i = 0; i < other->size; i++, tick++) {
Obj *elem = VA_Fetch(other, i);
if (elem != NULL) {
self->elems[tick] = INCREF(elem);
}
}
self->size = new_size;
}
Obj*
VA_pop(VArray *self) {
if (!self->size) {
return NULL;
}
self->size--;
return self->elems[self->size];
}
void
VA_unshift(VArray *self, Obj *elem) {
if (self->size == self->cap) {
VA_Grow(self, Memory_oversize(self->size + 1, sizeof(Obj*)));
}
memmove(self->elems + 1, self->elems, self->size * sizeof(Obj*));
self->elems[0] = elem;
self->size++;
}
Obj*
VA_shift(VArray *self) {
if (!self->size) {
return NULL;
}
else {
Obj *const return_val = self->elems[0];
self->size--;
if (self->size > 0) {
memmove(self->elems, self->elems + 1,
self->size * sizeof(Obj*));
}
return return_val;
}
}
Obj*
VA_fetch(VArray *self, uint32_t num) {
if (num >= self->size) {
return NULL;
}
return self->elems[num];
}
void
VA_store(VArray *self, uint32_t tick, Obj *elem) {
if (tick >= self->cap) {
VA_Grow(self, Memory_oversize(tick + 1, sizeof(Obj*)));
}
if (tick < self->size) { DECREF(self->elems[tick]); }
else { self->size = tick + 1; }
self->elems[tick] = elem;
}
void
VA_grow(VArray *self, uint32_t capacity) {
if (capacity > self->cap) {
self->elems = (Obj**)REALLOCATE(self->elems, capacity * sizeof(Obj*));
self->cap = capacity;
memset(self->elems + self->size, 0,
(capacity - self->size) * sizeof(Obj*));
}
}
Obj*
VA_delete(VArray *self, uint32_t num) {
Obj *elem = NULL;
if (num < self->size) {
elem = self->elems[num];
self->elems[num] = NULL;
}
return elem;
}
void
VA_excise(VArray *self, uint32_t offset, uint32_t length) {
uint32_t i;
uint32_t num_to_move;
if (self->size <= offset) { return; }
else if (self->size < offset + length) { length = self->size - offset; }
for (i = 0; i < length; i++) {
DECREF(self->elems[offset + i]);
}
num_to_move = self->size - (offset + length);
memmove(self->elems + offset, self->elems + offset + length,
num_to_move * sizeof(Obj*));
self->size -= length;
}
void
VA_clear(VArray *self) {
VA_excise(self, 0, self->size);
}
void
VA_resize(VArray *self, uint32_t size) {
if (size < self->size) {
VA_Excise(self, size, self->size - size);
}
else if (size > self->size) {
VA_Grow(self, size);
}
self->size = size;
}
uint32_t
VA_get_size(VArray *self) {
return self->size;
}
uint32_t
VA_get_capacity(VArray *self) {
return self->cap;
}
static int
S_default_compare(void *context, const void *va, const void *vb) {
Obj *a = *(Obj**)va;
Obj *b = *(Obj**)vb;
UNUSED_VAR(context);
if (a != NULL && b != NULL) { return Obj_Compare_To(a, b); }
else if (a == NULL && b == NULL) { return 0; }
else if (a == NULL) { return 1; } // NULL to the back
else /* b == NULL */ { return -1; } // NULL to the back
}
void
VA_sort(VArray *self, lucy_Sort_compare_t compare, void *context) {
if (!compare) { compare = S_default_compare; }
Sort_quicksort(self->elems, self->size, sizeof(void*), compare, context);
}
bool_t
VA_equals(VArray *self, Obj *other) {
VArray *twin = (VArray*)other;
if (twin == self) { return true; }
if (!Obj_Is_A(other, VARRAY)) { return false; }
if (twin->size != self->size) {
return false;
}
else {
uint32_t i, max;
for (i = 0, max = self->size; i < max; i++) {
Obj *val = self->elems[i];
Obj *other_val = twin->elems[i];
if ((val && !other_val) || (other_val && !val)) { return false; }
if (val && !Obj_Equals(val, other_val)) { return false; }
}
}
return true;
}
VArray*
VA_gather(VArray *self, lucy_VA_gather_test_t test, void *data) {
uint32_t i, max;
VArray *gathered = VA_new(self->size);
for (i = 0, max = self->size; i < max; i++) {
if (test(self, i, data)) {
Obj *elem = self->elems[i];
VA_Push(gathered, elem ? INCREF(elem) : NULL);
}
}
return gathered;
}