cleanups and more test cases
This commit is contained in:
@@ -0,0 +1,247 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | 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 <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include "defs.h"
|
||||
#include "bpalloc.h"
|
||||
|
||||
#if USING_VALGRIND
|
||||
#include <valgrind/memcheck.h>
|
||||
#endif
|
||||
|
||||
#define CHUNK_SIZE 4096
|
||||
#define PADDING 8
|
||||
|
||||
typedef struct BPAllocChunk BPAllocChunk;
|
||||
struct BPAllocChunk {
|
||||
BPAllocChunk *prev;
|
||||
char body[];
|
||||
};
|
||||
|
||||
struct xBPAlloc {
|
||||
int flags;
|
||||
void *userp;
|
||||
void *(*fn_malloc)(void *userp, int size);
|
||||
void (*fn_free )(void *userp, void *addr);
|
||||
int minsize, size, used;
|
||||
BPAllocChunk *tail;
|
||||
};
|
||||
|
||||
enum {
|
||||
FG_STATIC = 1,
|
||||
};
|
||||
|
||||
static void *default_fn_malloc(void *userp, int size);
|
||||
static void default_fn_free (void *userp, void *addr);
|
||||
|
||||
BPAlloc *BPAlloc_Init(int chunk_size)
|
||||
{
|
||||
return BPAlloc_Init2(-1, chunk_size, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
BPAlloc *BPAlloc_Init2(int first_size, int chunk_size,
|
||||
void *userp,
|
||||
void *(*fn_malloc)(void *userp, int size),
|
||||
void (*fn_free )(void *userp, void *addr))
|
||||
{
|
||||
if(chunk_size < 0)
|
||||
chunk_size = CHUNK_SIZE;
|
||||
|
||||
if(first_size < 0)
|
||||
first_size = chunk_size;
|
||||
|
||||
if(fn_malloc == NULL)
|
||||
{
|
||||
userp = NULL;
|
||||
fn_malloc = default_fn_malloc;
|
||||
fn_free = default_fn_free;
|
||||
}
|
||||
|
||||
void *temp = fn_malloc(userp, sizeof(BPAlloc) + sizeof(BPAllocChunk) + first_size + PADDING);
|
||||
|
||||
if(temp == NULL)
|
||||
return NULL;
|
||||
|
||||
BPAlloc *alloc = temp;
|
||||
|
||||
BPAllocChunk *chunk = (BPAllocChunk*) (alloc + 1);
|
||||
chunk->prev = NULL;
|
||||
|
||||
alloc->flags = 0;
|
||||
alloc->used = 0;
|
||||
alloc->size = first_size;
|
||||
alloc->tail = chunk;
|
||||
alloc->minsize = chunk_size;
|
||||
alloc->userp = userp;
|
||||
alloc->fn_malloc = fn_malloc;
|
||||
alloc->fn_free = fn_free;
|
||||
|
||||
#if USING_VALGRIND
|
||||
VALGRIND_CREATE_MEMPOOL(alloc, PADDING, 0);
|
||||
#endif
|
||||
|
||||
return alloc;
|
||||
}
|
||||
|
||||
BPAlloc *BPAlloc_Init3(void *mem, int mem_size, int chunk_size,
|
||||
void *userp,
|
||||
void *(*fn_malloc)(void *userp, int size),
|
||||
void (*fn_free )(void *userp, void *addr))
|
||||
{
|
||||
assert(mem != NULL);
|
||||
assert(mem_size >= 0);
|
||||
|
||||
if(chunk_size < 0)
|
||||
chunk_size = CHUNK_SIZE;
|
||||
|
||||
if(fn_malloc == NULL)
|
||||
{
|
||||
userp = NULL;
|
||||
fn_malloc = default_fn_malloc;
|
||||
fn_free = default_fn_free;
|
||||
}
|
||||
|
||||
int required = sizeof(BPAlloc)
|
||||
+ sizeof(BPAllocChunk)
|
||||
+ PADDING;
|
||||
|
||||
if(mem_size < required)
|
||||
// Not enough memory was provided.
|
||||
return NULL;
|
||||
|
||||
BPAlloc *alloc = mem;
|
||||
|
||||
BPAllocChunk *chunk = (BPAllocChunk*) (alloc + 1);
|
||||
chunk->prev = NULL;
|
||||
|
||||
alloc->flags = FG_STATIC;
|
||||
alloc->used = 0;
|
||||
alloc->size = mem_size - sizeof(BPAlloc) - sizeof(BPAllocChunk);
|
||||
alloc->tail = chunk;
|
||||
alloc->minsize = chunk_size;
|
||||
alloc->userp = userp;
|
||||
alloc->fn_malloc = fn_malloc;
|
||||
alloc->fn_free = fn_free;
|
||||
|
||||
#if USING_VALGRIND
|
||||
VALGRIND_CREATE_MEMPOOL(alloc, PADDING, 0);
|
||||
#endif
|
||||
|
||||
return alloc;
|
||||
}
|
||||
|
||||
void BPAlloc_Free(BPAlloc *alloc)
|
||||
{
|
||||
assert(alloc != NULL);
|
||||
|
||||
#if USING_VALGRIND
|
||||
VALGRIND_DESTROY_MEMPOOL(alloc);
|
||||
#endif
|
||||
|
||||
BPAllocChunk *chunk = alloc->tail;
|
||||
|
||||
while(chunk->prev)
|
||||
{
|
||||
BPAllocChunk *prev = chunk->prev;
|
||||
|
||||
if(alloc->fn_free)
|
||||
alloc->fn_free(alloc->userp, chunk);
|
||||
|
||||
chunk = prev;
|
||||
}
|
||||
|
||||
if(!(alloc->flags & FG_STATIC))
|
||||
if(alloc->fn_free)
|
||||
alloc->fn_free(alloc->userp, alloc);
|
||||
}
|
||||
|
||||
void *BPAlloc_Malloc(BPAlloc *alloc, int req_size)
|
||||
{
|
||||
assert(alloc != NULL);
|
||||
assert(req_size >= 0);
|
||||
|
||||
alloc->used += PADDING;
|
||||
|
||||
if(alloc->used & 7)
|
||||
alloc->used = (alloc->used & ~7) + 8;
|
||||
|
||||
if(alloc->used + req_size > alloc->size)
|
||||
{
|
||||
// If the chunk size is lower than the
|
||||
// requested size, then set the chunk
|
||||
// size to the requested size.
|
||||
int chunk_size = MAX(alloc->minsize, req_size + PADDING);
|
||||
|
||||
assert(alloc->fn_malloc != NULL);
|
||||
BPAllocChunk *chunk = alloc->fn_malloc(alloc->userp, sizeof(BPAllocChunk) + chunk_size);
|
||||
|
||||
if(chunk == NULL)
|
||||
return NULL;
|
||||
|
||||
chunk->prev = alloc->tail;
|
||||
alloc->tail = chunk;
|
||||
alloc->size = chunk_size;
|
||||
alloc->used = PADDING;
|
||||
|
||||
if(alloc->used & 7)
|
||||
alloc->used = (alloc->used & ~7) + 8;
|
||||
}
|
||||
|
||||
void *addr = alloc->tail->body + alloc->used;
|
||||
|
||||
assert(((intptr_t) addr) % 8 == 0);
|
||||
|
||||
alloc->used += req_size;
|
||||
|
||||
#if USING_VALGRIND
|
||||
VALGRIND_MEMPOOL_ALLOC(alloc, addr, req_size);
|
||||
// VALGRIND_MAKE_MEM_NOACCESS((char*) addr - PADDING, PADDING);
|
||||
// VALGRIND_MAKE_MEM_NOACCESS((char*) addr + req_size, PADDING);
|
||||
#endif
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
static void *default_fn_malloc(void *userp, int size)
|
||||
{
|
||||
assert(userp == NULL);
|
||||
assert(size >= 0);
|
||||
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
static void default_fn_free(void *userp, void *addr)
|
||||
{
|
||||
assert(userp == NULL);
|
||||
|
||||
free(addr);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | 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/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef BPALLOC_H
|
||||
#define BPALLOC_H
|
||||
typedef struct xBPAlloc BPAlloc;
|
||||
BPAlloc* BPAlloc_Init(int chunk_size);
|
||||
BPAlloc* BPAlloc_Init2(int first_size, int chunk_size, void *userp, void *(*fn_malloc)(void *userp, int size), void (*fn_free )(void *userp, void *addr));
|
||||
BPAlloc* BPAlloc_Init3(void *mem, int mem_size, int chunk_size, void *userp, void *(*fn_malloc)(void *userp, int size), void (*fn_free )(void *userp, void *addr));
|
||||
void BPAlloc_Free(BPAlloc *alloc);
|
||||
void* BPAlloc_Malloc(BPAlloc *alloc, int req_size);
|
||||
#endif
|
||||
@@ -0,0 +1,212 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | 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;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | 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/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef BUCKETLIST_H
|
||||
#define BUCKETLIST_H
|
||||
#include "bpalloc.h"
|
||||
typedef struct xBucketList BucketList;
|
||||
BucketList *BucketList_New(BPAlloc *alloc);
|
||||
int BucketList_Size(BucketList *blist);
|
||||
void BucketList_Copy(BucketList *blist, void *dest, int len);
|
||||
_Bool BucketList_Append (BucketList *blist, const void *data, int size);
|
||||
void *BucketList_Append2(BucketList *blist, const void *data, int size);
|
||||
BPAlloc *BucketList_GetAlloc(BucketList *blist);
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | 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>
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(x, y) ((x) > (y) ? (x) : (y))
|
||||
#endif
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
||||
#endif
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL ((void*) 0)
|
||||
#endif
|
||||
|
||||
#define UNREACHABLE assert(0);
|
||||
|
||||
#define membersizeof(type, member) (sizeof(((type*) 0)->member))
|
||||
@@ -0,0 +1,118 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include "error.h"
|
||||
|
||||
void Error_Init(Error *err)
|
||||
{
|
||||
memset(err, 0, sizeof (Error));
|
||||
}
|
||||
|
||||
void Error_Init2(Error *err, void (*on_report)(Error *err))
|
||||
{
|
||||
memset(err, 0, sizeof (Error));
|
||||
err->on_report = on_report;
|
||||
}
|
||||
|
||||
void Error_Free(Error *err)
|
||||
{
|
||||
if(err->message2 != err->message)
|
||||
free(err->message);
|
||||
memset(err, 0, sizeof (Error));
|
||||
}
|
||||
|
||||
void _Error_Report(Error *err, _Bool internal,
|
||||
const char *file, const char *func, int line,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
_Error_Report2(err, internal, file, func, line, fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
void _Error_Report2(Error *err, _Bool internal,
|
||||
const char *file, const char *func, int line,
|
||||
const char *fmt, va_list va)
|
||||
{
|
||||
assert(err);
|
||||
assert(file);
|
||||
assert(func);
|
||||
assert(line > 0);
|
||||
assert(fmt);
|
||||
assert(err->occurred == 0);
|
||||
|
||||
err->occurred = 1;
|
||||
err->internal = internal;
|
||||
err->file = file;
|
||||
err->func = func;
|
||||
err->line = line;
|
||||
|
||||
va_list va2;
|
||||
va_copy(va2, va);
|
||||
|
||||
int p = vsnprintf(err->message2, sizeof(err->message2), fmt, va);
|
||||
|
||||
assert(p > -1);
|
||||
|
||||
if((unsigned int) p > sizeof(err->message2)-1)
|
||||
{
|
||||
char *temp = malloc(p+1);
|
||||
|
||||
if(temp == NULL)
|
||||
{
|
||||
err->truncated = 1;
|
||||
err->message = err->message2;
|
||||
err->length = sizeof(err->message2)-1;
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(temp, p+1, fmt, va2);
|
||||
err->truncated = 0;
|
||||
err->message = temp;
|
||||
err->length = p;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
err->truncated = 0;
|
||||
err->message = err->message2;
|
||||
err->length = p;
|
||||
}
|
||||
|
||||
va_end(va2);
|
||||
|
||||
if(err->on_report)
|
||||
err->on_report(err);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | 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/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef ERROR_H
|
||||
#define ERROR_H
|
||||
#include <stdarg.h>
|
||||
|
||||
typedef struct Error Error;
|
||||
|
||||
struct Error {
|
||||
void (*on_report)(Error *err);
|
||||
_Bool occurred,
|
||||
internal,
|
||||
truncated;
|
||||
int length;
|
||||
char* message;
|
||||
char message2[256];
|
||||
const char *file,
|
||||
*func;
|
||||
int line;
|
||||
};
|
||||
|
||||
void Error_Init(Error *err);
|
||||
void Error_Init2(Error *err, void (*on_report)(Error *err));
|
||||
void Error_Free(Error *err);
|
||||
#define Error_Report(err, internal, fmt, ...) _Error_Report(err, internal, __FILE__, __func__, __LINE__, fmt, ## __VA_ARGS__)
|
||||
void _Error_Report (Error *err, _Bool internal, const char *file, const char *func, int line, const char *fmt, ...);
|
||||
void _Error_Report2(Error *err, _Bool internal, const char *file, const char *func, int line, const char *fmt, va_list va);
|
||||
#endif
|
||||
@@ -0,0 +1,49 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | 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 <stdint.h>
|
||||
#include "hash.h"
|
||||
|
||||
int hashbytes(unsigned char *str, int len)
|
||||
{
|
||||
int x = 0; // Temp?
|
||||
|
||||
x ^= *str << 7;
|
||||
|
||||
for(int i = 0; i < len; i += 1)
|
||||
x = (1000003UL * x) ^ *str++;
|
||||
|
||||
x ^= len;
|
||||
|
||||
if(x == -1)
|
||||
x = -2;
|
||||
|
||||
return x;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | 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/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef HASH_H
|
||||
#define HASH_H
|
||||
int hashbytes(unsigned char *str, int len);
|
||||
#endif
|
||||
@@ -0,0 +1,99 @@
|
||||
#include <string.h>
|
||||
#include "labellist.h"
|
||||
|
||||
typedef struct LabelInfo LabelInfo;
|
||||
struct LabelInfo {
|
||||
const char *name;
|
||||
size_t name_len;
|
||||
Promise *promise;
|
||||
LabelInfo *next;
|
||||
};
|
||||
|
||||
struct LabelList {
|
||||
BPAlloc *alloc;
|
||||
LabelInfo *head;
|
||||
};
|
||||
|
||||
LabelList *LabelList_New(BPAlloc *alloc)
|
||||
{
|
||||
LabelList *list = BPAlloc_Malloc(alloc, sizeof(LabelList));
|
||||
if(list != NULL) {
|
||||
list->head = NULL;
|
||||
list->alloc = alloc;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
void LabelList_Free(LabelList *list)
|
||||
{
|
||||
LabelInfo *info = list->head;
|
||||
while(info != NULL) {
|
||||
Promise_Free(info->promise);
|
||||
info = info->next;
|
||||
}
|
||||
list->head = NULL;
|
||||
}
|
||||
|
||||
bool LabelList_SetLabel(LabelList *list, const char *name, size_t name_len, long long int value)
|
||||
{
|
||||
Promise *promise = LabelList_GetLabel(list, name, name_len);
|
||||
if(promise == NULL)
|
||||
return false;
|
||||
Promise_Resolve(promise, &value, sizeof(value));
|
||||
return true;
|
||||
}
|
||||
|
||||
Promise *LabelList_GetLabel(LabelList *list, const char *name, size_t name_len)
|
||||
{
|
||||
// Find the label with the given name.
|
||||
{
|
||||
LabelInfo *info = list->head;
|
||||
while(info != NULL) {
|
||||
|
||||
if(name_len == info->name_len
|
||||
&& strncmp(name, info->name, name_len) == 0)
|
||||
return info->promise;
|
||||
|
||||
info = info->next;
|
||||
}
|
||||
}
|
||||
|
||||
// No such label. Create a new one.
|
||||
LabelInfo *new_info;
|
||||
{
|
||||
BPAlloc *alloc = list->alloc;
|
||||
Promise *promise = Promise_New(alloc, sizeof(long long int));
|
||||
if(promise == NULL)\
|
||||
return NULL;
|
||||
|
||||
new_info = BPAlloc_Malloc(alloc, sizeof(LabelInfo));
|
||||
if(new_info == NULL) {
|
||||
Promise_Free(promise);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
new_info->next = NULL;
|
||||
new_info->name = name;
|
||||
new_info->name_len = name_len;
|
||||
new_info->promise = promise;
|
||||
}
|
||||
|
||||
// Add it to the list
|
||||
new_info->next = list->head;
|
||||
list->head = new_info;
|
||||
|
||||
return new_info->promise;
|
||||
}
|
||||
|
||||
size_t LabelList_GetUnresolvedCount(LabelList *list)
|
||||
{
|
||||
size_t count = 0;
|
||||
|
||||
LabelInfo *info = list->head;
|
||||
while(info != NULL) {
|
||||
count += !Promise_hasResolved(info->promise);
|
||||
info = info->next;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#ifndef LABELLIST_H
|
||||
#define LABELLIST_H
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include "bpalloc.h"
|
||||
#include "promise.h"
|
||||
typedef struct LabelList LabelList;
|
||||
LabelList *LabelList_New(BPAlloc *alloc);
|
||||
void LabelList_Free(LabelList *list);
|
||||
bool LabelList_SetLabel(LabelList *list, const char *name, size_t name_len, long long int value);
|
||||
Promise *LabelList_GetLabel(LabelList *list, const char *name, size_t name_len);
|
||||
size_t LabelList_GetUnresolvedCount(LabelList *list);
|
||||
#endif /* LABELLIST_H */
|
||||
@@ -0,0 +1,142 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | 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 <string.h>
|
||||
#include "promise.h"
|
||||
#include "defs.h"
|
||||
|
||||
typedef struct Gap Gap;
|
||||
struct Gap {
|
||||
Gap *next;
|
||||
void *dest;
|
||||
void *userp;
|
||||
void (*callback)(void*);
|
||||
};
|
||||
|
||||
struct xPromise {
|
||||
BPAlloc *alloc;
|
||||
_Bool set;
|
||||
Gap *gaps;
|
||||
int size;
|
||||
char body[];
|
||||
};
|
||||
|
||||
Promise *Promise_New(BPAlloc *alloc, int size)
|
||||
{
|
||||
assert(alloc != NULL);
|
||||
assert(size >= 0);
|
||||
|
||||
Promise *promise = BPAlloc_Malloc(alloc, sizeof(Promise) + size);
|
||||
|
||||
if(promise == NULL)
|
||||
return NULL;
|
||||
|
||||
promise->alloc = alloc;
|
||||
promise->set = 0;
|
||||
promise->gaps = NULL;
|
||||
promise->size = size;
|
||||
return promise;
|
||||
}
|
||||
|
||||
unsigned int Promise_Size(Promise *promise)
|
||||
{
|
||||
return promise->size;
|
||||
}
|
||||
|
||||
_Bool Promise_hasResolved(Promise *promise)
|
||||
{
|
||||
return promise->set;
|
||||
}
|
||||
|
||||
void Promise_Free(Promise *promise)
|
||||
{
|
||||
(void) promise;
|
||||
//assert(promise->set == 1);
|
||||
}
|
||||
|
||||
void Promise_Resolve(Promise *promise, const void *data, int size)
|
||||
{
|
||||
assert(size >= 0);
|
||||
assert(size == promise->size);
|
||||
assert(promise->set == 0);
|
||||
|
||||
memcpy(promise->body, data, size);
|
||||
promise->set = 1;
|
||||
|
||||
Gap *gap = promise->gaps;
|
||||
while(gap)
|
||||
{
|
||||
memcpy(gap->dest, data, size);
|
||||
|
||||
if(gap->callback)
|
||||
gap->callback(gap->userp);
|
||||
|
||||
gap = gap->next;
|
||||
}
|
||||
|
||||
promise->gaps = NULL;
|
||||
}
|
||||
|
||||
_Bool Promise_Subscribe(Promise *promise, void *dest)
|
||||
{
|
||||
assert(promise != NULL);
|
||||
assert(dest != NULL);
|
||||
return Promise_Subscribe2(promise, dest, NULL, NULL);
|
||||
}
|
||||
|
||||
_Bool Promise_Subscribe2(Promise *promise, void *dest, void *userp, void (*callback)(void*))
|
||||
{
|
||||
assert(promise != NULL);
|
||||
assert(dest != NULL);
|
||||
|
||||
if(promise->set == 0)
|
||||
{
|
||||
Gap *gap = BPAlloc_Malloc(promise->alloc, sizeof(Gap));
|
||||
|
||||
if(gap == NULL)
|
||||
return 0;
|
||||
|
||||
gap->next = promise->gaps;
|
||||
gap->dest = dest;
|
||||
gap->userp = userp;
|
||||
gap->callback = callback;
|
||||
promise->gaps = gap;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(dest, promise->body, promise->size);
|
||||
|
||||
if(callback)
|
||||
callback(userp);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | 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/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef PROMISE_H
|
||||
#define PROMISE_H
|
||||
#include "bpalloc.h"
|
||||
typedef struct xPromise Promise;
|
||||
Promise *Promise_New(BPAlloc *alloc, int size);
|
||||
unsigned int Promise_Size(Promise *promise);
|
||||
void Promise_Free(Promise *promise);
|
||||
void Promise_Resolve(Promise *promise, const void *data, int size);
|
||||
_Bool Promise_Subscribe(Promise *promise, void *dest);
|
||||
_Bool Promise_Subscribe2(Promise *promise, void *dest, void *userp, void (*callback)(void*));
|
||||
_Bool Promise_hasResolved(Promise *promise);
|
||||
#endif
|
||||
@@ -0,0 +1,193 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | 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 <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include "source.h"
|
||||
|
||||
struct xSource {
|
||||
char *name;
|
||||
char *body;
|
||||
int size;
|
||||
int refs;
|
||||
};
|
||||
|
||||
Source *Source_Copy(Source *s)
|
||||
{
|
||||
s->refs += 1;
|
||||
return s;
|
||||
}
|
||||
|
||||
void Source_Free(Source *s)
|
||||
{
|
||||
s->refs -= 1;
|
||||
assert(s->refs >= 0);
|
||||
|
||||
if(s->refs == 0)
|
||||
free(s);
|
||||
}
|
||||
|
||||
const char *Source_GetName(const Source *s)
|
||||
{
|
||||
return s->name;
|
||||
}
|
||||
|
||||
const char *Source_GetBody(const Source *s)
|
||||
{
|
||||
return s->body;
|
||||
}
|
||||
|
||||
unsigned int Source_GetSize(const Source *s)
|
||||
{
|
||||
return s->size;
|
||||
}
|
||||
|
||||
Source *Source_FromFile(const char *file, Error *error)
|
||||
{
|
||||
assert(file != NULL);
|
||||
|
||||
// Open the file and get it's size.
|
||||
// at the end of the block, the file
|
||||
// cursor will point at the start of
|
||||
// the file.
|
||||
FILE *fp;
|
||||
int size;
|
||||
{
|
||||
fp = fopen(file, "rb");
|
||||
|
||||
if(fp == NULL)
|
||||
{
|
||||
if(errno == ENOENT)
|
||||
Error_Report(error, 0, "File \"%s\" doesn't exist", file);
|
||||
else
|
||||
Error_Report(error, 1, "Call to fopen failed (%s, errno = %d)", strerror(errno), errno);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(fseek(fp, 0, SEEK_END))
|
||||
{
|
||||
Error_Report(error, 1, "Call to fseek failed (%s, errno = %d)", strerror(errno), errno);
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size = ftell(fp);
|
||||
|
||||
if(size < 0)
|
||||
{
|
||||
Error_Report(error, 1, "Call to ftell failed (%s, errno = %d)", strerror(errno), errno);
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(fseek(fp, 0, SEEK_SET))
|
||||
{
|
||||
Error_Report(error, 1, "Call to fseek failed (%s, errno = %d)", strerror(errno), errno);
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate the source structure.
|
||||
Source *s;
|
||||
{
|
||||
int namel = strlen(file);
|
||||
|
||||
s = malloc(sizeof(Source) + namel + size + 2);
|
||||
|
||||
if(s == NULL)
|
||||
{
|
||||
Error_Report(error, 1, "No memory");
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s->name = (char*) (s + 1);
|
||||
s->body = s->name + namel + 1;
|
||||
}
|
||||
|
||||
// Copy the name into it.
|
||||
strcpy(s->name, file);
|
||||
s->size = size;
|
||||
s->refs = 1;
|
||||
|
||||
// Now copy the file contents into it.
|
||||
{
|
||||
int p = fread(s->body, 1, size, fp);
|
||||
|
||||
if(p != size)
|
||||
{
|
||||
Error_Report(error, 1, "Call to fread failed, %d bytes out of %d were read (%s, errno = %d)", p, size, strerror(errno), errno);
|
||||
fclose(fp);
|
||||
free(s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s->body[s->size] = '\0';
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return s;
|
||||
}
|
||||
|
||||
Source *Source_FromString(const char *name, const char *body, int size, Error *error)
|
||||
{
|
||||
assert(body != NULL);
|
||||
|
||||
if(size < 0)
|
||||
size = strlen(body);
|
||||
|
||||
int namel = name ? strlen(name) : 0;
|
||||
|
||||
void *memory = malloc(sizeof(Source) + namel + size + 2);
|
||||
|
||||
if(memory == NULL)
|
||||
{
|
||||
Error_Report(error, 1, "No memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Source *s = memory;
|
||||
s->name = (char*) (s + 1);
|
||||
s->body = s->name + namel + 1;
|
||||
s->size = size;
|
||||
s->refs = 1;
|
||||
|
||||
if(name)
|
||||
strcpy(s->name, name);
|
||||
else
|
||||
s->name = NULL;
|
||||
|
||||
strncpy(s->body, body, size);
|
||||
return s;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | 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/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef SOURCE_H
|
||||
#define SOURCE_H
|
||||
#include "error.h"
|
||||
typedef struct xSource Source;
|
||||
Source *Source_Copy(Source *s);
|
||||
void Source_Free(Source *s);
|
||||
const char *Source_GetName(const Source *s);
|
||||
const char *Source_GetBody(const Source *s);
|
||||
unsigned int Source_GetSize(const Source *s);
|
||||
Source *Source_FromFile(const char *file, Error *error);
|
||||
Source *Source_FromString(const char *name, const char *body, int size, Error *error);
|
||||
#endif
|
||||
@@ -0,0 +1,182 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | 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 <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "stack.h"
|
||||
#include "defs.h"
|
||||
|
||||
struct xStack {
|
||||
unsigned int size,
|
||||
used;
|
||||
int refs;
|
||||
void *body[];
|
||||
};
|
||||
|
||||
_Bool Stack_IsReadOnlyCopy(Stack *s)
|
||||
{
|
||||
return (uintptr_t) s & (uintptr_t) 1;
|
||||
}
|
||||
|
||||
void *Stack_New(int size)
|
||||
{
|
||||
if(size < 0)
|
||||
size = 1024;
|
||||
|
||||
Stack *s = malloc(sizeof(Stack) + sizeof(void*) * size);
|
||||
|
||||
if(s == NULL)
|
||||
return NULL;
|
||||
|
||||
assert((intptr_t) s % 8 == 0);
|
||||
|
||||
s->size = size;
|
||||
s->used = 0;
|
||||
s->refs = 1;
|
||||
return s;
|
||||
}
|
||||
|
||||
static Stack *unmark(Stack *s)
|
||||
{
|
||||
return (Stack*) ((intptr_t) s & ~ (intptr_t) 1);
|
||||
}
|
||||
|
||||
void *Stack_Top(Stack *s, int n)
|
||||
{
|
||||
assert(n <= 0);
|
||||
|
||||
// Remove readonly bit.
|
||||
s = unmark(s);
|
||||
|
||||
if(s->used == 0)
|
||||
return NULL;
|
||||
|
||||
if((int) s->used + n - 1 < 0)
|
||||
return NULL;
|
||||
|
||||
return s->body[s->used + n - 1];
|
||||
}
|
||||
|
||||
void **Stack_TopRef(Stack *s, int n)
|
||||
{
|
||||
assert(n <= 0);
|
||||
|
||||
if(Stack_IsReadOnlyCopy(s))
|
||||
return NULL;
|
||||
|
||||
// Remove readonly bit.
|
||||
s = unmark(s);
|
||||
|
||||
if(s->used == 0)
|
||||
return NULL;
|
||||
|
||||
if((int) s->used + n - 1 < 0)
|
||||
return NULL;
|
||||
|
||||
return &s->body[s->used + n - 1];
|
||||
}
|
||||
|
||||
|
||||
_Bool Stack_Pop(Stack *s, unsigned int n)
|
||||
{
|
||||
if(Stack_IsReadOnlyCopy(s))
|
||||
return 0;
|
||||
|
||||
if(s->used < n)
|
||||
return 0;
|
||||
|
||||
s->used -= n;
|
||||
return 1;
|
||||
}
|
||||
|
||||
_Bool Stack_Push(Stack *s, void *item)
|
||||
{
|
||||
assert(s != NULL);
|
||||
assert(item != NULL);
|
||||
|
||||
if(Stack_IsReadOnlyCopy(s))
|
||||
return 0;
|
||||
|
||||
if(s->used == s->size)
|
||||
return 0;
|
||||
|
||||
s->body[s->used] = item;
|
||||
s->used += 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
Stack *Stack_Copy(Stack *s, _Bool readonly)
|
||||
{
|
||||
if(Stack_IsReadOnlyCopy(s))
|
||||
{
|
||||
// Reference is readonly,
|
||||
// so the copy must be
|
||||
// readonly.
|
||||
readonly = 1;
|
||||
|
||||
// Remove readonly bit.
|
||||
s = unmark(s);
|
||||
}
|
||||
|
||||
s->refs += 1;
|
||||
|
||||
if(readonly)
|
||||
return (Stack*) ((uintptr_t) s | (intptr_t) 1);
|
||||
else
|
||||
return s;
|
||||
}
|
||||
|
||||
void Stack_Free(Stack *s)
|
||||
{
|
||||
// Remove readonly bit.
|
||||
s = unmark(s);
|
||||
|
||||
s->refs -= 1;
|
||||
assert(s->refs >= 0);
|
||||
|
||||
if(s->refs == 0)
|
||||
free(s);
|
||||
}
|
||||
|
||||
unsigned int Stack_Size(Stack *s)
|
||||
{
|
||||
// Remove readonly bit.
|
||||
s = unmark(s);
|
||||
|
||||
return s->used;
|
||||
}
|
||||
|
||||
unsigned int Stack_Capacity(Stack *s)
|
||||
{
|
||||
// Remove readonly bit.
|
||||
s = unmark(s);
|
||||
|
||||
return s->size;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | 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/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef STACK_H
|
||||
#define STACK_H
|
||||
typedef struct xStack Stack;
|
||||
void *Stack_New(int size);
|
||||
void *Stack_Top(Stack *s, int n);
|
||||
_Bool Stack_Pop(Stack *s, unsigned int n);
|
||||
_Bool Stack_Push(Stack *s, void *item);
|
||||
void Stack_Free(Stack *s);
|
||||
unsigned int Stack_Size(Stack *s);
|
||||
Stack *Stack_Copy(Stack *s, _Bool readonly);
|
||||
void **Stack_TopRef(Stack *s, int n);
|
||||
unsigned int Stack_Capacity(Stack *s);
|
||||
_Bool Stack_IsReadOnlyCopy(Stack *s);
|
||||
#endif
|
||||
@@ -0,0 +1,453 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | 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 <stddef.h> // NULL
|
||||
#include "utf8.h"
|
||||
|
||||
// If this is turned on, these functions will assume
|
||||
// the UTF-8 strings will mainly contain ASCII characters.
|
||||
#define ASSUME_ASCII 1
|
||||
|
||||
/* SYMBOL
|
||||
** utf8_sequence_from_utf32_codepoint
|
||||
**
|
||||
** DESCRIPTION
|
||||
** Transform a UTF-32 encoded codepoint to a UTF-8 encoded byte sequence.
|
||||
**
|
||||
** ARGUMENTS
|
||||
** The [utf8_data] pointer refers to the location where the UTF-8 sequence
|
||||
** will be stored.
|
||||
**
|
||||
** The [nbytes] argument specifies the maximum number of bytes that can
|
||||
** be written to [utf8_data]. It can't be negative.
|
||||
**
|
||||
** The [utf32_code] argument is the UTF-32 code that will be converted.
|
||||
**
|
||||
** RETURN
|
||||
** If [utf32_code] is valid UTF-32 and the provided buffer is big enough,
|
||||
** the UTF-8 equivalent sequence is stored in [utf8_data]. No more than
|
||||
** [nbytes] are ever written. If one of those conitions isn't true, -1 is
|
||||
** returned.
|
||||
*/
|
||||
int utf8_sequence_from_utf32_codepoint(char *utf8_data, int nbytes, uint32_t utf32_code)
|
||||
{
|
||||
if(utf32_code < 128)
|
||||
{
|
||||
if(nbytes < 1)
|
||||
return -1;
|
||||
|
||||
utf8_data[0] = utf32_code;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(utf32_code < 2048)
|
||||
{
|
||||
if(nbytes < 2)
|
||||
return -1;
|
||||
|
||||
utf8_data[0] = 0xc0 | (utf32_code >> 6);
|
||||
utf8_data[1] = 0x80 | (utf32_code & 0x3f);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if(utf32_code < 65536)
|
||||
{
|
||||
if(nbytes < 3)
|
||||
return -1;
|
||||
|
||||
utf8_data[0] = 0xe0 | (utf32_code >> 12);
|
||||
utf8_data[1] = 0x80 | ((utf32_code >> 6) & 0x3f);
|
||||
utf8_data[2] = 0x80 | (utf32_code & 0x3f);
|
||||
return 3;
|
||||
}
|
||||
|
||||
if(utf32_code <= 0x10ffff)
|
||||
{
|
||||
if(nbytes < 4)
|
||||
return -1;
|
||||
|
||||
utf8_data[0] = 0xf0 | (utf32_code >> 18);
|
||||
utf8_data[1] = 0x80 | ((utf32_code >> 12) & 0x3f);
|
||||
utf8_data[2] = 0x80 | ((utf32_code >> 6) & 0x3f);
|
||||
utf8_data[3] = 0x80 | (utf32_code & 0x3f);
|
||||
return 4;
|
||||
}
|
||||
|
||||
// Code is out of range for UTF-8.
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* SYMBOL
|
||||
** utf8_sequence_to_utf32_codepoint
|
||||
**
|
||||
** DESCRIPTION
|
||||
** Transform a UTF-8 encoded byte sequence pointed by `utf8_data`
|
||||
** into a UTF-32 encoded codepoint.
|
||||
**
|
||||
** ARGUMENTS
|
||||
** The [utf8_data] pointer refers to the location of the UTF-8 sequence.
|
||||
**
|
||||
** The [nbytes] argument specifies the maximum number of bytes that can
|
||||
** be read after [utf8_data]. It can't be negative.
|
||||
**
|
||||
** NOTE: The [nbytes] argument has no relation to the UTF-8 byte count sequence.
|
||||
** You may think about this argument as the "raw" string length (the one
|
||||
** [strlen] whould return if [utf8_data] were zero-terminated).
|
||||
**
|
||||
** The [utf32_code] argument is the location where the encoded UTF-32 code
|
||||
** will be stored. It may be NULL, in which case the value is evaluated and then
|
||||
** thrown away.
|
||||
**
|
||||
** RETURN
|
||||
** The codepoint is returned through the output parameter `utf32_code`.
|
||||
** The returned value is the number of bytes of the UTF-8 sequence that
|
||||
** were scanned to encode the UTF-32 code, or -1 if the UTF-8 sequence
|
||||
** is invalid.
|
||||
**
|
||||
** NOTE: By calling this function with a NULL [utf32_code], you can check the
|
||||
** validity of a UTF-8 sequence.
|
||||
*/
|
||||
int utf8_sequence_to_utf32_codepoint(const char *utf8_data, int nbytes, uint32_t *utf32_code)
|
||||
{
|
||||
assert(utf8_data != NULL);
|
||||
assert(nbytes >= 0);
|
||||
|
||||
uint32_t dummy;
|
||||
if(utf32_code == NULL)
|
||||
utf32_code = &dummy;
|
||||
|
||||
if(nbytes == 0)
|
||||
return -1;
|
||||
|
||||
if(utf8_data[0] & 0x80)
|
||||
{
|
||||
// May be UTF-8.
|
||||
|
||||
if((unsigned char) utf8_data[0] >= 0xF0)
|
||||
{
|
||||
// 4 bytes.
|
||||
// 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
|
||||
if(nbytes < 4)
|
||||
return -1;
|
||||
|
||||
uint32_t temp
|
||||
= (((uint32_t) utf8_data[0] & 0x07) << 18)
|
||||
| (((uint32_t) utf8_data[1] & 0x3f) << 12)
|
||||
| (((uint32_t) utf8_data[2] & 0x3f) << 6)
|
||||
| (((uint32_t) utf8_data[3] & 0x3f));
|
||||
|
||||
if(temp > 0x10ffff)
|
||||
return -1;
|
||||
|
||||
*utf32_code = temp;
|
||||
return 4;
|
||||
}
|
||||
|
||||
if((unsigned char) utf8_data[0] >= 0xE0)
|
||||
{
|
||||
// 3 bytes.
|
||||
// 1110xxxx 10xxxxxx 10xxxxxx
|
||||
|
||||
if(nbytes < 3)
|
||||
return -1;
|
||||
|
||||
uint32_t temp
|
||||
= (((uint32_t) utf8_data[0] & 0x0f) << 12)
|
||||
| (((uint32_t) utf8_data[1] & 0x3f) << 6)
|
||||
| (((uint32_t) utf8_data[2] & 0x3f));
|
||||
|
||||
if(temp > 0x10ffff)
|
||||
return -1;
|
||||
|
||||
*utf32_code = temp;
|
||||
return 3;
|
||||
}
|
||||
|
||||
if((unsigned char) utf8_data[0] >= 0xC0)
|
||||
{
|
||||
// 2 bytes.
|
||||
// 110xxxxx 10xxxxxx
|
||||
|
||||
if(nbytes < 2)
|
||||
return -1;
|
||||
|
||||
*utf32_code
|
||||
= (((uint32_t) utf8_data[0] & 0x1f) << 6)
|
||||
| (((uint32_t) utf8_data[1] & 0x3f));
|
||||
|
||||
assert(*utf32_code <= 0x10ffff);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// 1 byte
|
||||
// 10xxxxxx
|
||||
*utf32_code = (uint32_t) utf8_data[0] & 0x3f;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// It's ASCII
|
||||
// 0xxxxxxx
|
||||
|
||||
*utf32_code = (uint32_t) utf8_data[0];
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* SYMBOL
|
||||
** utf8_strlen
|
||||
**
|
||||
** DESCRIPTION
|
||||
** Count the number of characters of a UTF-8 string.
|
||||
**
|
||||
** NOTE: By "character" we mean a valid UTF-8 sequence.
|
||||
**
|
||||
** ARGUMENTS
|
||||
** The [utf8_data] pointer refers to the location of the UTF-8 string.
|
||||
**
|
||||
** The [nbytes] argument specifies the byte count of the string referred
|
||||
** by [utf8_data]. It can't be negative.
|
||||
**
|
||||
** RETURN
|
||||
** Returns the number of characters encoded by [utf8_data], or -1 if
|
||||
** the string is not valid UTF-8.
|
||||
**
|
||||
** NOTE: By calling this function on an ASCII-only string, the return
|
||||
** value is equal to [nbytes].
|
||||
**
|
||||
** NOTE: You can check the validity of a UTF-8 string
|
||||
** by calling this function and checking that it's
|
||||
** return value is not negative.
|
||||
*/
|
||||
int utf8_strlen(const char *utf8_data, int nbytes)
|
||||
{
|
||||
assert(utf8_data != NULL);
|
||||
assert(nbytes >= 0);
|
||||
|
||||
int len = 0;
|
||||
|
||||
int i = 0;
|
||||
while(i < nbytes)
|
||||
{
|
||||
|
||||
#if ASSUME_ASCII
|
||||
{
|
||||
int ASCII_start = i;
|
||||
|
||||
// Skip through ASCII
|
||||
while(i < nbytes && (utf8_data[i] & 0x80) == 0)
|
||||
i += 1;
|
||||
|
||||
int ASCII_end = i;
|
||||
|
||||
len += (ASCII_end - ASCII_start);
|
||||
|
||||
// Either we scanned through all of the
|
||||
// string, or we encountered some unicode.
|
||||
|
||||
if(i == nbytes)
|
||||
// String ended.
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Found unicode.
|
||||
{
|
||||
int n = utf8_sequence_to_utf32_codepoint(utf8_data + i, nbytes - i, NULL);
|
||||
|
||||
if(n < 1)
|
||||
return -1;
|
||||
|
||||
i += n;
|
||||
len += 1;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/* SYMBOL
|
||||
** utf8_prev
|
||||
**
|
||||
** DESCRIPTION
|
||||
** Get the UTF-8 sequence that comes before a given byte index
|
||||
** inside a given string.
|
||||
**
|
||||
** NOTE: This is what you use when you want to iterate over a
|
||||
** UTF-8 string backwards.
|
||||
**
|
||||
** ARGUMENTS
|
||||
** The [utf8_data] pointer refers to the location of the UTF-8 string.
|
||||
**
|
||||
** The [nbytes] argument is the raw size of the [utf8_data] string.
|
||||
** It can't be negative.
|
||||
**
|
||||
** The [idx] argument is the index of the byte that follows the UTF-8
|
||||
** sequence to be decoded.
|
||||
**
|
||||
** The [utf32_code] argument, if not NULL, is used to return the UTF-32
|
||||
** version of the decoded UTF-8 sequence.
|
||||
**
|
||||
** RETURN
|
||||
** Returns the index of the first byte of the decoded UTF-8 sequence,
|
||||
** or -1 is the sequence wasn't valid UTF-8.
|
||||
**
|
||||
** NOTE: If the function didn't fail, by subtracting the returned value
|
||||
** from [idx], you'll get the number of bytes of the decoded
|
||||
** sequence.
|
||||
*/
|
||||
int utf8_prev(const char *utf8_data, int nbytes, int idx, uint32_t *utf32_code)
|
||||
{
|
||||
assert(idx >= 0);
|
||||
assert(idx <= nbytes);
|
||||
|
||||
// [idx] currently refers to the head byte
|
||||
// of a UTF-8 sequence. We need to first
|
||||
// get to the last byte of the previous
|
||||
// sequence.
|
||||
idx -= 1;
|
||||
|
||||
if(idx == -1)
|
||||
// There was no previous sequence!
|
||||
return 0; // Return the same index that was provided.
|
||||
|
||||
int tail = idx;
|
||||
|
||||
#if ASSUME_ASCII
|
||||
{
|
||||
// This block isn't necessary for
|
||||
// this function to work but it
|
||||
// makes strings that are mainly ascii
|
||||
// to go faster.
|
||||
|
||||
if((utf8_data[tail] & 0x80) == 0)
|
||||
{
|
||||
if(utf32_code)
|
||||
*utf32_code = utf8_data[tail];
|
||||
return tail;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Skip all of the auxiliary bytes in the
|
||||
// form '10xxxxxx'.
|
||||
|
||||
while(idx > -1 && (utf8_data[idx] & 0xc0) == 0x80)
|
||||
idx -= 1;
|
||||
|
||||
if(idx == -1)
|
||||
{
|
||||
// No head sequence byte was found,
|
||||
// so this isn't valid UTF-8.
|
||||
return -1;
|
||||
}
|
||||
|
||||
// The index of the head byte.
|
||||
int head = idx;
|
||||
|
||||
// The number of auxiliary bytes is given
|
||||
// by the difference
|
||||
int aux = tail - head;
|
||||
|
||||
// The total number of bytes of the
|
||||
// sequence is [aux + 1].
|
||||
|
||||
int n = utf8_sequence_to_utf32_codepoint(utf8_data + head, aux + 1, utf32_code);
|
||||
|
||||
if(n < 1)
|
||||
// The sequence wasn't valid UTF-8.
|
||||
return -1;
|
||||
|
||||
assert(n > 0);
|
||||
|
||||
if(n < aux + 1)
|
||||
// Not all of the auxiliary bytes were considered while parsing.
|
||||
return -1;
|
||||
|
||||
assert(n == aux + 1);
|
||||
|
||||
return head;
|
||||
}
|
||||
|
||||
/* SYMBOL
|
||||
** utf8_next
|
||||
**
|
||||
** DESCRIPTION
|
||||
** Get the UTF-8 sequence from a UTF-8 string that starts AFTER the
|
||||
** sequence that starts at a given byte index.
|
||||
**
|
||||
** NOTE: This is what you use when you want to iterate over a
|
||||
** UTF-8 string.
|
||||
**
|
||||
** ARGUMENTS
|
||||
** The [utf8_data] pointer refers to the location of the UTF-8 string.
|
||||
**
|
||||
** The [nbytes] argument is the raw size of the [utf8_data] string.
|
||||
** It can't be negative.
|
||||
**
|
||||
** The [idx] argument is the index of the first byte of the sequence
|
||||
** that comes before the sequence to be decoded.
|
||||
**
|
||||
** The [utf32_code] argument, if not NULL, is used to return the UTF-32
|
||||
** version of the decoded UTF-8 sequence.
|
||||
**
|
||||
** RETURN
|
||||
** Returns the index of the first byte of the decoded UTF-8 sequence,
|
||||
** or -1 is the sequence wasn't valid UTF-8.
|
||||
**
|
||||
** NOTE: If the function didn't fail, by subtracting [idx] from the
|
||||
** returned value, you'll get the number of bytes of the decoded
|
||||
** sequence.
|
||||
*/
|
||||
int utf8_next(const char *utf8_data, int nbytes, int idx, uint32_t *utf32_code)
|
||||
{
|
||||
// Get the byte count of the current sequence.
|
||||
int n = utf8_sequence_to_utf32_codepoint(utf8_data + idx, nbytes, NULL);
|
||||
|
||||
if(n < 1)
|
||||
return -1;
|
||||
|
||||
// Now get the codepoint of the next sequence.
|
||||
int k = utf8_sequence_to_utf32_codepoint(utf8_data + idx + n, nbytes, utf32_code);
|
||||
|
||||
if(k < 1)
|
||||
return -1;
|
||||
|
||||
return idx + n;
|
||||
}
|
||||
|
||||
int utf8_curr(const char *utf8_data, int nbytes, int idx, uint32_t *utf32_code)
|
||||
{
|
||||
assert(idx >= 0);
|
||||
assert(idx < nbytes);
|
||||
|
||||
int n = utf8_sequence_to_utf32_codepoint(utf8_data + idx, nbytes - idx, utf32_code);
|
||||
|
||||
return n > 0;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
/* +--------------------------------------------------------------------------+
|
||||
** | _ _ _ |
|
||||
** | | \ | | (_) |
|
||||
** | | \| | ___ _ __ _ |
|
||||
** | | . ` |/ _ \| |/ _` | |
|
||||
** | | |\ | (_) | | (_| | |
|
||||
** | |_| \_|\___/| |\__,_| |
|
||||
** | _/ | |
|
||||
** | |__/ |
|
||||
** +--------------------------------------------------------------------------+
|
||||
** | 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/>. |
|
||||
** +--------------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef UTF8_H
|
||||
#define UTF8_H
|
||||
#include <stdint.h> // uint32_t
|
||||
int utf8_sequence_from_utf32_codepoint(char *utf8_data, int nbytes, uint32_t utf32_code);
|
||||
int utf8_sequence_to_utf32_codepoint(const char *utf8_data, int nbytes, uint32_t *utf32_code);
|
||||
int utf8_strlen(const char *utf8_data, int nbytes);
|
||||
int utf8_prev(const char *utf8_data, int nbytes, int idx, uint32_t *utf32_code);
|
||||
int utf8_next(const char *utf8_data, int nbytes, int idx, uint32_t *utf32_code);
|
||||
int utf8_curr(const char *utf8_data, int nbytes, int idx, uint32_t *utf32_code);
|
||||
#endif // #ifndef UTF8_H
|
||||
Reference in New Issue
Block a user