212 lines
5.5 KiB
C
212 lines
5.5 KiB
C
|
|
/* +--------------------------------------------------------------------------+
|
|
** | _ _ _ |
|
|
** | | \ | | (_) |
|
|
** | | \| | ___ _ __ _ |
|
|
** | | . ` |/ _ \| |/ _` | |
|
|
** | | |\ | (_) | | (_| | |
|
|
** | |_| \_|\___/| |\__,_| |
|
|
** | _/ | |
|
|
** | |__/ |
|
|
** +--------------------------------------------------------------------------+
|
|
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
|
** +--------------------------------------------------------------------------+
|
|
** | This file is part of The Noja Interpreter. |
|
|
** | |
|
|
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
|
** | modify it under the terms of the GNU General Public License as published |
|
|
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
|
** | your option) any later version. |
|
|
** | |
|
|
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
|
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
|
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
|
** | Public License for more details. |
|
|
** | |
|
|
** | You should have received a copy of the GNU General Public License along |
|
|
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
|
** +--------------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "bucketlist.h"
|
|
#include "defs.h"
|
|
|
|
#define MIN_BUCKET_SIZE 4096
|
|
|
|
typedef struct Bucket Bucket;
|
|
struct Bucket {
|
|
Bucket* next;
|
|
int size,
|
|
used,
|
|
aidx;
|
|
char body[];
|
|
};
|
|
|
|
struct xBucketList {
|
|
BPAlloc *alloc;
|
|
Bucket *head,
|
|
*tail;
|
|
int size;
|
|
};
|
|
|
|
BucketList *BucketList_New(BPAlloc *alloc)
|
|
{
|
|
assert(alloc != NULL);
|
|
|
|
BucketList *blist = BPAlloc_Malloc(alloc, sizeof(BucketList) + sizeof(Bucket) + MIN_BUCKET_SIZE);
|
|
|
|
if(blist == NULL)
|
|
return NULL;
|
|
|
|
Bucket *head = (Bucket*) (blist + 1);
|
|
head->next = NULL;
|
|
head->size = MIN_BUCKET_SIZE;
|
|
head->used = 0;
|
|
head->aidx = 0;
|
|
|
|
blist->alloc = alloc;
|
|
blist->head = head;
|
|
blist->tail = head;
|
|
blist->size = 0;
|
|
return blist;
|
|
}
|
|
|
|
int BucketList_Size(BucketList *blist)
|
|
{
|
|
return blist->size;
|
|
}
|
|
|
|
static Bucket *make_bucket(BPAlloc *alloc, int size)
|
|
{
|
|
Bucket *new_bucket = BPAlloc_Malloc(alloc, sizeof(Bucket) + size);
|
|
|
|
if(new_bucket == NULL)
|
|
return NULL;
|
|
|
|
// Initialize it.
|
|
new_bucket->next = NULL;
|
|
new_bucket->size = size;
|
|
new_bucket->used = 0;
|
|
new_bucket->aidx = -1;
|
|
return new_bucket;
|
|
}
|
|
|
|
static void append_bucket(BucketList *blist, Bucket *new_bucket)
|
|
{
|
|
new_bucket->aidx = blist->size;
|
|
blist->tail->next = new_bucket;
|
|
blist->tail = new_bucket;
|
|
}
|
|
|
|
_Bool BucketList_Append(BucketList *blist, const void *data, int size)
|
|
{
|
|
assert(blist != NULL);
|
|
assert(size >= 0);
|
|
|
|
int not_copied_yet = size;
|
|
|
|
while(not_copied_yet > 0)
|
|
{
|
|
// Copy until there's nothing left
|
|
// or until the current bucket is
|
|
// full. If the bucket is already
|
|
// full, add another one.
|
|
|
|
int left_in_bucket = blist->tail->size - blist->tail->used;
|
|
|
|
if(left_in_bucket == 0)
|
|
{
|
|
Bucket *new_bucket = make_bucket(blist->alloc, MIN_BUCKET_SIZE);
|
|
|
|
if(new_bucket == NULL)
|
|
return 0;
|
|
|
|
append_bucket(blist, new_bucket);
|
|
}
|
|
|
|
// Decide how much to copy.
|
|
int copying = MIN(not_copied_yet, left_in_bucket);
|
|
|
|
// Copy into the bucket.
|
|
{
|
|
char *dst = blist->tail->body + blist->tail->used;
|
|
|
|
if(data == NULL)
|
|
memset(dst, 0, copying);
|
|
else
|
|
memcpy(dst, data + size - not_copied_yet, copying);
|
|
|
|
blist->tail->used += copying;
|
|
}
|
|
|
|
not_copied_yet -= copying;
|
|
}
|
|
|
|
blist->size += size;
|
|
return 1;
|
|
}
|
|
|
|
void *BucketList_Append2(BucketList *blist, const void *data, int size)
|
|
{
|
|
assert(blist != NULL);
|
|
assert(size >= 0);
|
|
|
|
// If the data doesn't fit inside the
|
|
// current bucket, add another one with
|
|
// enough space.
|
|
if(blist->tail->used + size > blist->tail->size)
|
|
{
|
|
int bucket_size = MAX(MIN_BUCKET_SIZE, size);
|
|
|
|
Bucket *new_bucket = make_bucket(blist->alloc, bucket_size);
|
|
|
|
if(new_bucket == NULL)
|
|
return 0;
|
|
|
|
append_bucket(blist, new_bucket);
|
|
}
|
|
|
|
void *addr = blist->tail->body + blist->tail->used;
|
|
|
|
// Do the copying.
|
|
if(data == NULL)
|
|
memset(addr, 0, size);
|
|
else
|
|
memcpy(addr, data, size);
|
|
|
|
blist->tail->used += size;
|
|
blist->size += size;
|
|
return addr;
|
|
}
|
|
|
|
void BucketList_Copy(BucketList *blist, void *dest, int len)
|
|
{
|
|
assert(blist != NULL);
|
|
assert(dest != NULL);
|
|
|
|
if(len < 0)
|
|
len = blist->size;
|
|
|
|
int copied = 0;
|
|
|
|
Bucket *bucket = blist->head;
|
|
|
|
while(bucket && copied < len)
|
|
{
|
|
int copying = MIN(len - copied, bucket->used);
|
|
assert(copying >= 0);
|
|
|
|
memcpy((char*) dest + copied, bucket->body, copying);
|
|
|
|
copied += copying;
|
|
bucket = bucket->next;
|
|
}
|
|
}
|
|
|
|
BPAlloc *BucketList_GetAlloc(BucketList *blist)
|
|
{
|
|
return blist->alloc;
|
|
} |