mirror of
https://github.com/mikpe/pdp10-tools.git
synced 2026-01-11 23:53:19 +00:00
187 lines
3.7 KiB
C
187 lines
3.7 KiB
C
/*
|
|
* arrlst.c
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "arrlst.h"
|
|
#include "emalloc.h"
|
|
|
|
enum {
|
|
ARRLST_CHUNK_NRELEM = 128,
|
|
};
|
|
|
|
struct arrlst {
|
|
void *head;
|
|
void *tail;
|
|
unsigned int eltsz;
|
|
unsigned int tailpos;
|
|
|
|
/* We only allow a single iterator per arrlst, so we
|
|
allocate the iterator in the arrlst itself. */
|
|
struct {
|
|
void *chunk;
|
|
unsigned int chunkpos;
|
|
unsigned int chunklen;
|
|
} iter;
|
|
};
|
|
|
|
static void **arrlst_chunk_nextp(const void *chunk)
|
|
{
|
|
return (void**)chunk;
|
|
}
|
|
|
|
static unsigned int eltsz_chunk_header_nrelem(unsigned int eltsz)
|
|
{
|
|
unsigned int nrelem;
|
|
|
|
/* compute how may eltsz elements are needed to cover a void* */
|
|
nrelem = sizeof(void*) / eltsz;
|
|
if (nrelem * eltsz < sizeof(void*))
|
|
++nrelem;
|
|
|
|
return nrelem;
|
|
}
|
|
|
|
static unsigned int arrlst_chunk_header_nrelem(const struct arrlst *arrlst)
|
|
{
|
|
return eltsz_chunk_header_nrelem(arrlst->eltsz);
|
|
}
|
|
|
|
static void *arrlst_chunk_element(const struct arrlst *arrlst, void *chunk, unsigned int eltnr)
|
|
{
|
|
return (char*)chunk + eltnr * arrlst->eltsz;
|
|
}
|
|
|
|
static void *arrlst_alloc_chunk(const struct arrlst *arrlst)
|
|
{
|
|
unsigned int nrbytes;
|
|
void *chunk;
|
|
|
|
nrbytes = arrlst->eltsz * ARRLST_CHUNK_NRELEM;
|
|
chunk = emalloc(nrbytes);
|
|
return chunk;
|
|
}
|
|
|
|
struct arrlst *arrlst_alloc(size_t eltsz)
|
|
{
|
|
struct arrlst *arrlst;
|
|
|
|
if (eltsz == 0
|
|
|| eltsz > (unsigned int)-1
|
|
|| eltsz_chunk_header_nrelem(eltsz) >= ARRLST_CHUNK_NRELEM)
|
|
return NULL;
|
|
|
|
arrlst = emalloc(sizeof *arrlst);
|
|
|
|
/* these fields will be adjusted in the first call to append() */
|
|
arrlst->head = NULL;
|
|
arrlst->tail = NULL;
|
|
arrlst->eltsz = eltsz;
|
|
arrlst->tailpos = ARRLST_CHUNK_NRELEM;
|
|
|
|
return arrlst;
|
|
}
|
|
|
|
void arrlst_free(struct arrlst *arrlst)
|
|
{
|
|
void *chunk, *next;
|
|
|
|
chunk = arrlst->head;
|
|
while (chunk) {
|
|
next = *arrlst_chunk_nextp(chunk);
|
|
free(chunk);
|
|
chunk = next;
|
|
}
|
|
|
|
free(arrlst);
|
|
}
|
|
|
|
size_t arrlst_length(const struct arrlst *arrlst)
|
|
{
|
|
const void *chunk;
|
|
size_t length;
|
|
size_t chunk_header_nrelem;
|
|
|
|
chunk = arrlst->head;
|
|
if (!chunk)
|
|
return 0;
|
|
length = 0;
|
|
chunk_header_nrelem = arrlst_chunk_header_nrelem(arrlst);
|
|
for (;;) {
|
|
chunk = *arrlst_chunk_nextp(chunk);
|
|
if (!chunk)
|
|
break;
|
|
length += ARRLST_CHUNK_NRELEM - chunk_header_nrelem;
|
|
}
|
|
return length + arrlst->tailpos - chunk_header_nrelem;
|
|
}
|
|
|
|
void *arrlst_append(struct arrlst *arrlst)
|
|
{
|
|
void *tail;
|
|
void *elt;
|
|
|
|
tail = arrlst->tail;
|
|
|
|
if (arrlst->tailpos >= ARRLST_CHUNK_NRELEM) {
|
|
void *new_tail;
|
|
|
|
new_tail = arrlst_alloc_chunk(arrlst);
|
|
if (!new_tail)
|
|
return NULL;
|
|
|
|
if (tail)
|
|
*arrlst_chunk_nextp(tail) = new_tail;
|
|
else {
|
|
arrlst->head = new_tail;
|
|
arrlst->tail = new_tail;
|
|
}
|
|
|
|
arrlst->tailpos = arrlst_chunk_header_nrelem(arrlst);
|
|
|
|
tail = new_tail;
|
|
}
|
|
|
|
elt = arrlst_chunk_element(arrlst, tail, arrlst->tailpos);
|
|
++arrlst->tailpos;
|
|
|
|
return elt;
|
|
}
|
|
|
|
void arrlst_iter_rewind(struct arrlst *arrlst)
|
|
{
|
|
/* these fields will be adjusted in the first call to next() */
|
|
arrlst->iter.chunk = NULL;
|
|
arrlst->iter.chunklen = 0;
|
|
arrlst->iter.chunkpos = 0;
|
|
}
|
|
|
|
void *arrlst_iter_next(struct arrlst *arrlst)
|
|
{
|
|
void *chunk;
|
|
void *elt;
|
|
|
|
chunk = arrlst->iter.chunk;
|
|
if (arrlst->iter.chunkpos >= arrlst->iter.chunklen) {
|
|
if (!chunk)
|
|
chunk = arrlst->head;
|
|
else
|
|
chunk = *arrlst_chunk_nextp(chunk);
|
|
if (!chunk)
|
|
return NULL;
|
|
arrlst->iter.chunk = chunk;
|
|
|
|
if (*arrlst_chunk_nextp(chunk))
|
|
arrlst->iter.chunklen = ARRLST_CHUNK_NRELEM;
|
|
else
|
|
arrlst->iter.chunklen = arrlst->tailpos;
|
|
|
|
arrlst->iter.chunkpos = arrlst_chunk_header_nrelem(arrlst);
|
|
}
|
|
|
|
elt = arrlst_chunk_element(arrlst, chunk, arrlst->iter.chunkpos);
|
|
++arrlst->iter.chunkpos;
|
|
|
|
return elt;
|
|
}
|