cleanups and more test cases

This commit is contained in:
cozis
2022-08-12 05:30:41 +02:00
parent f630830b3c
commit 18e936f0ad
145 changed files with 1445 additions and 234 deletions
+247
View File
@@ -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);
}
+39
View File
@@ -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
+212
View File
@@ -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;
}
+41
View File
@@ -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
+47
View File
@@ -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))
+118
View File
@@ -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);
}
+56
View File
@@ -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
+49
View File
@@ -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;
}
+34
View File
@@ -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
+99
View File
@@ -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;
}
+13
View File
@@ -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 */
+142
View File
@@ -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;
}
+42
View File
@@ -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
+193
View File
@@ -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;
}
+42
View File
@@ -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
+182
View File
@@ -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;
}
+44
View File
@@ -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
+453
View File
@@ -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;
}
+40
View File
@@ -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