2017-11-27 19:03:34 +00:00
|
|
|
/*
|
2023-01-07 21:03:35 +00:00
|
|
|
* Copyright (c) 2023 Calvin Rose
|
2017-11-27 19:03:34 +00:00
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to
|
|
|
|
* deal in the Software without restriction, including without limitation the
|
|
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
|
|
* IN THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* The symbol cache is an open hashtable with all active symbols in the program
|
|
|
|
* stored in it. As the primary use of symbols is table lookups and equality
|
|
|
|
* checks, all symbols are interned so that there is a single copy of it in the
|
|
|
|
* whole program. Equality is then just a pointer check. */
|
|
|
|
|
2019-01-24 05:15:58 +00:00
|
|
|
#ifndef JANET_AMALG
|
2019-12-31 00:06:15 +00:00
|
|
|
#include "features.h"
|
2019-02-19 01:13:35 +00:00
|
|
|
#include <janet.h>
|
2018-03-31 20:40:36 +00:00
|
|
|
#include "state.h"
|
2017-12-21 04:03:34 +00:00
|
|
|
#include "gc.h"
|
2018-01-06 16:09:15 +00:00
|
|
|
#include "util.h"
|
2019-02-19 01:13:35 +00:00
|
|
|
#include "symcache.h"
|
2019-01-24 05:15:58 +00:00
|
|
|
#endif
|
2017-11-27 19:03:34 +00:00
|
|
|
|
2019-12-31 00:06:15 +00:00
|
|
|
#include <string.h>
|
|
|
|
|
2017-11-27 19:03:34 +00:00
|
|
|
/* Initialize the cache (allocate cache memory) */
|
2018-09-06 02:18:42 +00:00
|
|
|
void janet_symcache_init() {
|
2021-07-17 01:59:03 +00:00
|
|
|
janet_vm.cache_capacity = 1024;
|
|
|
|
janet_vm.cache = janet_calloc(1, (size_t) janet_vm.cache_capacity * sizeof(const uint8_t *));
|
|
|
|
if (NULL == janet_vm.cache) {
|
2018-09-06 02:18:42 +00:00
|
|
|
JANET_OUT_OF_MEMORY;
|
2017-11-27 19:03:34 +00:00
|
|
|
}
|
2021-08-06 21:24:30 +00:00
|
|
|
memset(&janet_vm.gensym_counter, '0', sizeof(janet_vm.gensym_counter));
|
|
|
|
janet_vm.gensym_counter[0] = '_';
|
2021-07-17 01:59:03 +00:00
|
|
|
janet_vm.cache_count = 0;
|
|
|
|
janet_vm.cache_deleted = 0;
|
2017-11-27 19:03:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Deinitialize the cache (free the cache memory) */
|
2018-09-06 02:18:42 +00:00
|
|
|
void janet_symcache_deinit() {
|
2021-07-17 01:59:03 +00:00
|
|
|
janet_free((void *)janet_vm.cache);
|
|
|
|
janet_vm.cache = NULL;
|
|
|
|
janet_vm.cache_capacity = 0;
|
|
|
|
janet_vm.cache_count = 0;
|
|
|
|
janet_vm.cache_deleted = 0;
|
2017-11-27 19:03:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Mark an entry in the table as deleted. */
|
2018-10-17 03:08:26 +00:00
|
|
|
static const uint8_t JANET_SYMCACHE_DELETED[1] = {0};
|
2017-11-27 19:03:34 +00:00
|
|
|
|
|
|
|
/* Find an item in the cache and return its location.
|
|
|
|
* If the item is not found, return the location
|
|
|
|
* where one would put it. */
|
2018-09-06 02:18:42 +00:00
|
|
|
static const uint8_t **janet_symcache_findmem(
|
2019-02-20 01:51:34 +00:00
|
|
|
const uint8_t *str,
|
|
|
|
int32_t len,
|
|
|
|
int32_t hash,
|
|
|
|
int *success) {
|
2017-11-29 20:17:56 +00:00
|
|
|
uint32_t bounds[4];
|
|
|
|
uint32_t i, j, index;
|
2017-11-27 19:03:34 +00:00
|
|
|
const uint8_t **firstEmpty = NULL;
|
|
|
|
|
|
|
|
/* We will search two ranges - index to the end,
|
|
|
|
* and 0 to the index. */
|
2021-07-17 01:59:03 +00:00
|
|
|
index = (uint32_t)hash & (janet_vm.cache_capacity - 1);
|
2017-11-27 19:03:34 +00:00
|
|
|
bounds[0] = index;
|
2021-07-17 01:59:03 +00:00
|
|
|
bounds[1] = janet_vm.cache_capacity;
|
2017-11-27 19:03:34 +00:00
|
|
|
bounds[2] = 0;
|
|
|
|
bounds[3] = index;
|
|
|
|
for (j = 0; j < 4; j += 2)
|
2019-02-20 01:51:34 +00:00
|
|
|
for (i = bounds[j]; i < bounds[j + 1]; ++i) {
|
2021-07-17 01:59:03 +00:00
|
|
|
const uint8_t *test = janet_vm.cache[i];
|
2017-11-27 19:03:34 +00:00
|
|
|
/* Check empty spots */
|
|
|
|
if (NULL == test) {
|
|
|
|
if (NULL == firstEmpty)
|
2021-07-17 01:59:03 +00:00
|
|
|
firstEmpty = janet_vm.cache + i;
|
2017-11-27 19:03:34 +00:00
|
|
|
goto notfound;
|
|
|
|
}
|
|
|
|
/* Check for marked deleted */
|
2018-09-06 02:18:42 +00:00
|
|
|
if (JANET_SYMCACHE_DELETED == test) {
|
2017-11-27 19:03:34 +00:00
|
|
|
if (firstEmpty == NULL)
|
2021-07-17 01:59:03 +00:00
|
|
|
firstEmpty = janet_vm.cache + i;
|
2017-11-27 19:03:34 +00:00
|
|
|
continue;
|
|
|
|
}
|
2018-09-06 02:18:42 +00:00
|
|
|
if (janet_string_equalconst(test, str, len, hash)) {
|
2017-11-27 19:03:34 +00:00
|
|
|
/* Replace first deleted */
|
|
|
|
*success = 1;
|
|
|
|
if (firstEmpty != NULL) {
|
|
|
|
*firstEmpty = test;
|
2021-07-17 01:59:03 +00:00
|
|
|
janet_vm.cache[i] = JANET_SYMCACHE_DELETED;
|
2017-11-27 19:03:34 +00:00
|
|
|
return firstEmpty;
|
|
|
|
}
|
2021-07-17 01:59:03 +00:00
|
|
|
return janet_vm.cache + i;
|
2017-11-27 19:03:34 +00:00
|
|
|
}
|
|
|
|
}
|
2019-02-20 01:51:34 +00:00
|
|
|
notfound:
|
2017-11-27 19:03:34 +00:00
|
|
|
*success = 0;
|
2023-06-01 15:52:34 +00:00
|
|
|
janet_assert(firstEmpty != NULL, "symcache failed to get memory");
|
2017-11-27 19:03:34 +00:00
|
|
|
return firstEmpty;
|
|
|
|
}
|
|
|
|
|
2018-09-06 02:18:42 +00:00
|
|
|
#define janet_symcache_find(str, success) \
|
|
|
|
janet_symcache_findmem((str), janet_string_length(str), janet_string_hash(str), (success))
|
2017-11-27 19:03:34 +00:00
|
|
|
|
|
|
|
/* Resize the cache. */
|
2018-09-06 02:18:42 +00:00
|
|
|
static void janet_cache_resize(uint32_t newCapacity) {
|
2017-11-29 20:17:56 +00:00
|
|
|
uint32_t i, oldCapacity;
|
2021-07-17 01:59:03 +00:00
|
|
|
const uint8_t **oldCache = janet_vm.cache;
|
2021-03-23 10:00:48 +00:00
|
|
|
const uint8_t **newCache = janet_calloc(1, (size_t) newCapacity * sizeof(const uint8_t *));
|
2017-11-27 19:03:34 +00:00
|
|
|
if (newCache == NULL) {
|
2018-09-06 02:18:42 +00:00
|
|
|
JANET_OUT_OF_MEMORY;
|
2017-11-27 19:03:34 +00:00
|
|
|
}
|
2021-07-17 01:59:03 +00:00
|
|
|
oldCapacity = janet_vm.cache_capacity;
|
|
|
|
janet_vm.cache = newCache;
|
|
|
|
janet_vm.cache_capacity = newCapacity;
|
|
|
|
janet_vm.cache_deleted = 0;
|
2017-11-27 19:03:34 +00:00
|
|
|
/* Add all of the old cache entries back */
|
|
|
|
for (i = 0; i < oldCapacity; ++i) {
|
|
|
|
int status;
|
|
|
|
const uint8_t **bucket;
|
|
|
|
const uint8_t *x = oldCache[i];
|
2018-09-06 02:18:42 +00:00
|
|
|
if (x != NULL && x != JANET_SYMCACHE_DELETED) {
|
|
|
|
bucket = janet_symcache_find(x, &status);
|
2017-11-27 19:03:34 +00:00
|
|
|
if (status || bucket == NULL) {
|
|
|
|
/* there was a problem with the algorithm. */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
*bucket = x;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Free the old cache */
|
2021-03-23 10:00:48 +00:00
|
|
|
janet_free((void *)oldCache);
|
2017-11-27 19:03:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Add an item to the cache */
|
2018-09-06 02:18:42 +00:00
|
|
|
static void janet_symcache_put(const uint8_t *x, const uint8_t **bucket) {
|
2021-07-17 01:59:03 +00:00
|
|
|
if ((janet_vm.cache_count + janet_vm.cache_deleted) * 2 > janet_vm.cache_capacity) {
|
2017-11-27 19:03:34 +00:00
|
|
|
int status;
|
2021-07-17 01:59:03 +00:00
|
|
|
janet_cache_resize(janet_tablen((2 * janet_vm.cache_count + 1)));
|
2018-09-06 02:18:42 +00:00
|
|
|
bucket = janet_symcache_find(x, &status);
|
2017-11-27 19:03:34 +00:00
|
|
|
}
|
|
|
|
/* Add x to the cache */
|
2021-07-17 01:59:03 +00:00
|
|
|
janet_vm.cache_count++;
|
2017-11-27 19:03:34 +00:00
|
|
|
*bucket = x;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove a symbol from the symcache */
|
2018-09-06 02:18:42 +00:00
|
|
|
void janet_symbol_deinit(const uint8_t *sym) {
|
2017-11-27 19:03:34 +00:00
|
|
|
int status = 0;
|
2018-09-06 02:18:42 +00:00
|
|
|
const uint8_t **bucket = janet_symcache_find(sym, &status);
|
2017-11-27 19:03:34 +00:00
|
|
|
if (status) {
|
2021-07-17 01:59:03 +00:00
|
|
|
janet_vm.cache_count--;
|
|
|
|
janet_vm.cache_deleted++;
|
2018-09-06 02:18:42 +00:00
|
|
|
*bucket = JANET_SYMCACHE_DELETED;
|
2017-11-27 19:03:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create a symbol from a byte string */
|
2018-09-06 02:18:42 +00:00
|
|
|
const uint8_t *janet_symbol(const uint8_t *str, int32_t len) {
|
|
|
|
int32_t hash = janet_string_calchash(str, len);
|
2017-11-27 19:03:34 +00:00
|
|
|
uint8_t *newstr;
|
|
|
|
int success = 0;
|
2018-09-06 02:18:42 +00:00
|
|
|
const uint8_t **bucket = janet_symcache_findmem(str, len, hash, &success);
|
2017-11-27 19:03:34 +00:00
|
|
|
if (success)
|
|
|
|
return *bucket;
|
2020-01-03 04:02:57 +00:00
|
|
|
JanetStringHead *head = janet_gcalloc(JANET_MEMORY_SYMBOL, sizeof(JanetStringHead) + (size_t) len + 1);
|
2019-02-21 16:22:29 +00:00
|
|
|
head->hash = hash;
|
|
|
|
head->length = len;
|
|
|
|
newstr = (uint8_t *)(head->data);
|
2020-01-29 05:38:52 +00:00
|
|
|
safe_memcpy(newstr, str, len);
|
2018-01-06 16:09:15 +00:00
|
|
|
newstr[len] = 0;
|
2018-09-06 02:18:42 +00:00
|
|
|
janet_symcache_put((const uint8_t *)newstr, bucket);
|
2017-11-27 19:03:34 +00:00
|
|
|
return newstr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get a symbol from a cstring */
|
2018-09-06 02:18:42 +00:00
|
|
|
const uint8_t *janet_csymbol(const char *cstr) {
|
2019-02-21 16:34:04 +00:00
|
|
|
return janet_symbol((const uint8_t *)cstr, (int32_t) strlen(cstr));
|
2017-11-27 19:03:34 +00:00
|
|
|
}
|
|
|
|
|
2018-07-01 19:49:33 +00:00
|
|
|
/* Increment the gensym buffer */
|
|
|
|
static void inc_gensym(void) {
|
2021-07-17 01:59:03 +00:00
|
|
|
for (int i = sizeof(janet_vm.gensym_counter) - 2; i; i--) {
|
|
|
|
if (janet_vm.gensym_counter[i] == '9') {
|
|
|
|
janet_vm.gensym_counter[i] = 'a';
|
2018-07-09 00:54:41 +00:00
|
|
|
break;
|
2021-07-17 01:59:03 +00:00
|
|
|
} else if (janet_vm.gensym_counter[i] == 'z') {
|
|
|
|
janet_vm.gensym_counter[i] = 'A';
|
2018-07-09 00:54:41 +00:00
|
|
|
break;
|
2021-07-17 01:59:03 +00:00
|
|
|
} else if (janet_vm.gensym_counter[i] == 'Z') {
|
|
|
|
janet_vm.gensym_counter[i] = '0';
|
2018-07-01 19:49:33 +00:00
|
|
|
} else {
|
2021-07-17 01:59:03 +00:00
|
|
|
janet_vm.gensym_counter[i]++;
|
2018-07-01 19:49:33 +00:00
|
|
|
break;
|
2017-11-27 19:03:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Generate a unique symbol. This is used in the library function gensym. The
|
2018-07-01 19:49:33 +00:00
|
|
|
* symbol will be of the format _XXXXXX, where X is a base64 digit, and
|
|
|
|
* prefix is the argument passed. No prefix for speed. */
|
2018-09-06 02:18:42 +00:00
|
|
|
const uint8_t *janet_symbol_gen(void) {
|
2018-03-18 13:13:21 +00:00
|
|
|
const uint8_t **bucket = NULL;
|
2018-07-01 19:49:33 +00:00
|
|
|
uint8_t *sym;
|
2018-03-18 13:13:21 +00:00
|
|
|
int32_t hash = 0;
|
2018-07-01 19:49:33 +00:00
|
|
|
int status;
|
2017-11-27 19:03:34 +00:00
|
|
|
/* Leave spaces for 6 base 64 digits and two dashes. That means 64^6 possible suffixes, which
|
|
|
|
* is enough for resolving collisions. */
|
2018-07-01 19:49:33 +00:00
|
|
|
do {
|
2018-09-06 02:18:42 +00:00
|
|
|
hash = janet_string_calchash(
|
2021-07-17 01:59:03 +00:00
|
|
|
janet_vm.gensym_counter,
|
|
|
|
sizeof(janet_vm.gensym_counter) - 1);
|
2018-09-06 02:18:42 +00:00
|
|
|
bucket = janet_symcache_findmem(
|
2021-07-17 01:59:03 +00:00
|
|
|
janet_vm.gensym_counter,
|
|
|
|
sizeof(janet_vm.gensym_counter) - 1,
|
2019-02-20 01:51:34 +00:00
|
|
|
hash,
|
|
|
|
&status);
|
2018-07-01 19:49:33 +00:00
|
|
|
} while (status && (inc_gensym(), 1));
|
2021-07-17 01:59:03 +00:00
|
|
|
JanetStringHead *head = janet_gcalloc(JANET_MEMORY_SYMBOL, sizeof(JanetStringHead) + sizeof(janet_vm.gensym_counter));
|
|
|
|
head->length = sizeof(janet_vm.gensym_counter) - 1;
|
2019-02-21 16:22:29 +00:00
|
|
|
head->hash = hash;
|
|
|
|
sym = (uint8_t *)(head->data);
|
2021-07-17 01:59:03 +00:00
|
|
|
memcpy(sym, janet_vm.gensym_counter, sizeof(janet_vm.gensym_counter));
|
2018-09-06 02:18:42 +00:00
|
|
|
janet_symcache_put((const uint8_t *)sym, bucket);
|
2018-07-01 19:49:33 +00:00
|
|
|
return (const uint8_t *)sym;
|
2017-11-27 19:03:34 +00:00
|
|
|
}
|