mirror of
				https://github.com/janet-lang/janet
				synced 2025-10-31 15:43:01 +00:00 
			
		
		
		
	More work on open hashing implementation of objects.
Add metatable support for callable objects.
This commit is contained in:
		| @@ -3,8 +3,8 @@ | |||||||
|  |  | ||||||
| #include <stdint.h> | #include <stdint.h> | ||||||
|  |  | ||||||
| /* Flag for immutability in an otherwise mutable datastructure */ | /* Max search depth for classes. */ | ||||||
| #define GST_IMMUTABLE 1 | #define GST_MAX_SEARCH_DEPTH 128 | ||||||
|  |  | ||||||
| /* Verious types */ | /* Verious types */ | ||||||
| typedef enum GstType { | typedef enum GstType { | ||||||
| @@ -89,7 +89,6 @@ struct GstArray { | |||||||
|     uint32_t count; |     uint32_t count; | ||||||
|     uint32_t capacity; |     uint32_t capacity; | ||||||
|     GstValue *data; |     GstValue *data; | ||||||
|     uint32_t flags; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* A bytebuffer type. Used as a mutable string or string builder. */ | /* A bytebuffer type. Used as a mutable string or string builder. */ | ||||||
| @@ -97,7 +96,6 @@ struct GstBuffer { | |||||||
|     uint32_t count; |     uint32_t count; | ||||||
|     uint32_t capacity; |     uint32_t capacity; | ||||||
|     uint8_t *data; |     uint8_t *data; | ||||||
|     uint32_t flags; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* The main Gst type, an obect. Objects are just hashtables with some meta | /* The main Gst type, an obect. Objects are just hashtables with some meta | ||||||
| @@ -106,7 +104,6 @@ struct GstObject { | |||||||
|     uint32_t count; |     uint32_t count; | ||||||
|     uint32_t capacity; |     uint32_t capacity; | ||||||
|     GstBucket **buckets; |     GstBucket **buckets; | ||||||
|     uint32_t flags; |  | ||||||
|     GstObject *meta; |     GstObject *meta; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -168,6 +165,8 @@ struct Gst { | |||||||
|     /* Return state */ |     /* Return state */ | ||||||
|     const char *crash; |     const char *crash; | ||||||
|     GstValue ret; /* Returned value from gst_start. Also holds errors. */ |     GstValue ret; /* Returned value from gst_start. Also holds errors. */ | ||||||
|  |     /* Temporary array for use in function dispatch */ | ||||||
|  |     GstValue tempArray[GST_MAX_SEARCH_DEPTH]; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* Bytecode */ | /* Bytecode */ | ||||||
|   | |||||||
							
								
								
									
										354
									
								
								dict.c
									
									
									
									
									
								
							
							
						
						
									
										354
									
								
								dict.c
									
									
									
									
									
								
							| @@ -1,130 +1,264 @@ | |||||||
| #include "dict.h" | #include "dict.h" | ||||||
| #include "util.h" | #include "util.h" | ||||||
| #include "value.h" | #include "value.h" | ||||||
|  | #include "vm.h" | ||||||
|  |  | ||||||
|  | /****/ | ||||||
|  | /* Bag implementation */ | ||||||
|  | /****/ | ||||||
|  |  | ||||||
|  | /* Find a kv pair in a bag */ | ||||||
|  | static GstValue *gst_object_bag_find(GstDict *obj, GstValue key) { | ||||||
|  |     GstValue *start = obj->data; | ||||||
|  |     GstValue *end = obj->data + obj->count * 2; | ||||||
|  | 	while (start < end) { | ||||||
|  |     	if (gst_equals(*start, key)) | ||||||
|  |         	return start; | ||||||
|  | 		start += 2; | ||||||
|  | 	} | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Check for string equality */ | ||||||
|  | static int str_equal_value(GstValue v, const char *str, uint32_t len, uint32_t hash) { | ||||||
|  | 	uint32_t i; | ||||||
|  |     if (v.type != GST_STRING) return 0; | ||||||
|  | 	if (gst_string_length(str) != len) return 0; | ||||||
|  | 	if (!gst_string_hash(str)) | ||||||
|  | 		gst_string_hash(str) = gst_string_calchash((uint8_t *)str); | ||||||
|  | 	if (gst_string_hash(str) != hash) return 0; | ||||||
|  | 	for (i = 0; i < len; ++i) | ||||||
|  | 		if (str[1] != v.data.string[i]) return 0; | ||||||
|  | 	return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Find key value pair with c string key */ | ||||||
|  | static GstValue *gst_object_bag_findcstring(GstDict *obj, const char *key) { | ||||||
|  |     uint32_t len, hash; | ||||||
|  |     for (len = 0; key[len]; ++len); | ||||||
|  |     hash = gst_cstring_calchash((uint8_t *)key, len); | ||||||
|  |     GstValue *start = obj->data; | ||||||
|  |     GstValue *end = obj->data + obj->count * 2; | ||||||
|  | 	while (start < end) { | ||||||
|  |     	if (start->type == GST_STRING) { | ||||||
|  | 			uint8_t *str = start->data.string; | ||||||
|  | 			if (gst_string_length(str) == len) { | ||||||
|  | 				if (!gst_string_hash(str)) | ||||||
|  |     				gst_string_hash(str) = gst_string_calchash(str); | ||||||
|  |     			if (gst_string_hash(str) == hash) { | ||||||
|  | 					return start | ||||||
|  |     			} | ||||||
|  | 			} | ||||||
|  |     	} | ||||||
|  | 		start += 2; | ||||||
|  | 	} | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Remove a key from a bag */ | ||||||
|  | static void gst_object_bag_remove(GstDict *obj, GstValue key) { | ||||||
|  | 	GstValue *kv = gst_object_bag_find(obj, key); | ||||||
|  | 	if (kv != NULL) { | ||||||
|  |     	GstValue *lastKv = obj->data + --obj->count * 2; | ||||||
|  | 		kv[0] = lastKv[0]; | ||||||
|  | 		kv[1] = lastKv[1]; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Add a key to a bag */ | ||||||
|  | static void gst_object_bag_put(Gst *vm, GstDict *obj, GstValue key, GstValue value) { | ||||||
|  | 	GstValue *kv = gst_object_bag_find(obj, key); | ||||||
|  | 	if (kv != NULL) { | ||||||
|  |     	/* Replace value */ | ||||||
|  |     	kv[1] = value; | ||||||
|  | 	} else { | ||||||
|  | 		/* Check for need to resize */ | ||||||
|  | 		if (obj->count + 1 > obj->capacity) { | ||||||
|  |     		uint32_t newCap = 2 * obj->count + 2; | ||||||
|  |     		GstValue *newData = gst_alloc(vm, sizeof(GstValue) * 2 * newCap); | ||||||
|  | 			gst_memcpy(newData, obj->data, obj->count * 2 * sizeof(GstValue)); | ||||||
|  | 			obj->data = newData; | ||||||
|  | 			obj->capacity = newCap; | ||||||
|  | 		} | ||||||
|  | 		/* Push to end */ | ||||||
|  | 		kv = obj->data + obj->count * 2; | ||||||
|  | 		kv[0] = key; | ||||||
|  | 		kv[1] = value; | ||||||
|  | 		++obj->count; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /****/ | ||||||
|  | /* Hashtable implementaion */ | ||||||
|  | /****/ | ||||||
|  |  | ||||||
|  | /* Add a key value pair to a given array. Returns if key successfully added. */  | ||||||
|  | static void hash_putkv(GstValue *data, uint32_t cap, GstValue key, GstValue value) { | ||||||
|  | 	GstValue *end = data + 2 * cap; | ||||||
|  | 	GstValue *start = data + (gst_hash(key) % cap) * 2; | ||||||
|  | 	GstValue *bucket; | ||||||
|  | 	/* Check second half of array */ | ||||||
|  | 	for (bucket = start; bucket < end; bucket += 2) { | ||||||
|  | 		if (bucket[0].type == GST_NIL) { | ||||||
|  | 			bucket[0] = key; | ||||||
|  | 			bucket[1] = value; | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	/* Check first half of array */ | ||||||
|  | 	for (bucket = data; bucket < start; bucket += 2) { | ||||||
|  | 		if (bucket[0].type == GST_NIL) { | ||||||
|  |     		bucket[0] = key; | ||||||
|  |     		bucket[1] = value; | ||||||
|  |     		return; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	/* Should never reach here - data would be full */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Find a bucket in the hastable */ | ||||||
|  | static GstValue *hash_findkv(GstValue *data, uint32_t cap, GstValue key, GstValue **out) { | ||||||
|  | 	GstValue *end = data + 2 * cap; | ||||||
|  | 	GstValue *start = data + (gst_hash(key) % cap) * 2; | ||||||
|  | 	GstValue *bucket; | ||||||
|  | 	/* Check second half of array */ | ||||||
|  | 	for (bucket = start; bucket < end; bucket += 2) | ||||||
|  | 		if (bucket[0].type == GST_NIL) | ||||||
|  |     		if (bucket[1].type == GST_BOOLEAN) /* Check if just marked deleted */ | ||||||
|  | 				continue; | ||||||
|  | 			else { | ||||||
|  |     			*out = bucket; | ||||||
|  | 				return NULL; | ||||||
|  | 			} | ||||||
|  | 		else if (gst_equals(bucket[0], key)) | ||||||
|  |     		return bucket; | ||||||
|  | 	/* Check first half of array */ | ||||||
|  | 	for (bucket = data; bucket < start; bucket += 2) | ||||||
|  |     	if (bucket[0].type == GST_NIL) | ||||||
|  |     		if (bucket[1].type == GST_BOOLEAN) /* Check if just marked deleted */ | ||||||
|  | 				continue; | ||||||
|  | 			else { | ||||||
|  |     			*out = bucket; | ||||||
|  | 				return NULL; | ||||||
|  | 			} | ||||||
|  | 		else if (gst_equals(bucket[0], key)) | ||||||
|  |     		return bucket; | ||||||
|  | 	/* Should never reach here - data would be full */ | ||||||
|  | 	*out = bucket; | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Resize internal hashtable. Also works if currently a bag. */ | ||||||
|  | static void gst_object_rehash(Gst *vm, GstDict *obj, uint32_t capacity) { | ||||||
|  |     GstValue *toData, *fromBucket, *toBucket, *toStart *fromEnd, *toEnd; | ||||||
|  |     toData = gst_alloc(vm, capacity * 2 * sizeof(GstValue)); | ||||||
|  |     toEnd = toData + 2 * capacity; | ||||||
|  |     fromBucket = obj->data; | ||||||
|  |     fromEnd = fromBucket + obj->count * 2; | ||||||
|  | 	for (; fromBucket < fromEnd; fromBucket += 2) {	 | ||||||
|  | 		if (fromBucket[0].type == GST_NIL) continue; | ||||||
|  | 		toStart = toData + (gst_hash(fromBucket[0]) % capacity) * 2; | ||||||
|  | 		/* Check second half of array */ | ||||||
|  |     	for (toBucket = toStart; toBucket < toEnd; toBucket += 2) { | ||||||
|  |     		if (toBucket[0].type == GST_NIL) { | ||||||
|  |     			toBucket[0] = fromBucket[0]; | ||||||
|  |     			toBucket[1] = fromBucket[1]; | ||||||
|  |     			goto finish_put; | ||||||
|  |     		} | ||||||
|  |     	} | ||||||
|  |     	/* Check first half of array */ | ||||||
|  |     	for (toBucket = toData; toBucket < toStart; toBucket += 2) { | ||||||
|  |     		if (toBucket[0].type == GST_NIL) { | ||||||
|  |     			toBucket[0] = fromBucket[0]; | ||||||
|  |     			toBucket[1] = fromBucket[1]; | ||||||
|  |     			goto finish_put; | ||||||
|  |     		} | ||||||
|  |     	} | ||||||
|  |     	/* Error if we got here - backing array to small. */ | ||||||
|  |     	; | ||||||
|  |     	/* Continue. */ | ||||||
|  |     	finish_put: continue; | ||||||
|  | 	} | ||||||
|  | 	obj->capacity = capacity; | ||||||
|  | 	obj->data = toData; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /****/ | ||||||
|  | /* Interface */ | ||||||
|  | /****/ | ||||||
|  |  | ||||||
| /* Initialize a dictionary */ | /* Initialize a dictionary */ | ||||||
| GstDict *gst_dict_init(GstDict *dict, uint32_t capacity) { | GstDict *gst_dict(Gst *vm, uint32_t capacity) { | ||||||
|     GstDictBucket *buckets = gst_raw_calloc(1, sizeof(GstDictBucket) * capacity); |     GstDict *dict = gst_alloc(vm, sizeof(GstDict)); | ||||||
|     if (data == NULL) |     GstValue *data = gst_zalloc(vm, sizeof(GstValue) * 2 * capacity); | ||||||
|         return NULL; |     dict->data = data; | ||||||
|     dict->buckets = buckets; |  | ||||||
|     dict->capacity = capacity; |     dict->capacity = capacity; | ||||||
|     dict->count = 0; |     dict->count = 0; | ||||||
|  |     dict->flags = (capacity < GST_OBJECT_BAG_THRESHOLD) ? GST_OBJECT_FLAG_ISBAG : 0; | ||||||
|     return dict; |     return dict; | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Deinitialize a dictionary */ |  | ||||||
| GstDict *gst_dict_free(GstDict *dict) { |  | ||||||
|     gst_raw_free(dict->buckets); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* Rehash a dictionary */ |  | ||||||
| GstDict *gst_dict_rehash(GstDict *dict, uint32_t newCapacity) { |  | ||||||
|     GstDictBucket *newBuckets = gst_raw_calloc(1, sizeof(GstDictBucket) * newCapacity); |  | ||||||
|     GstDictBucket *buckets = dict->buckets; |  | ||||||
|     uint32_t i, j; |  | ||||||
|     if (newBuckets == NULL) |  | ||||||
|         return NULL; |  | ||||||
|     for (i = 0; i < dict->capacity; ++i) { |  | ||||||
|         int index; |  | ||||||
|         if (!(buckets[i].flags & GST_DICT_FLAG_OCCUPIED)) continue; |  | ||||||
|         if (buckets[i].flags & GST_DICT_FLAG_TOMBSTONE) continue; |  | ||||||
|         index = gst_hash(buckets[i].key) % newCapacity; |  | ||||||
|         for (j = index; j < dict->capacity; ++j) { |  | ||||||
|             if (newBuckets[j].flags & GST_DICT_FLAG_OCCUPIED) continue; |  | ||||||
|             newBuckets[j] = buckets[i]; |  | ||||||
|             goto done; |  | ||||||
|         } |  | ||||||
|         for (j = 0; j < index; ++j) { |  | ||||||
|             if (newBuckets[j].flags & GST_DICT_FLAG_OCCUPIED) continue; |  | ||||||
|             newBuckets[j] = buckets[i]; |  | ||||||
|             goto done; |  | ||||||
|         } |  | ||||||
|         /* Error - could not rehash a bucket - this should never happen */ |  | ||||||
|         gst_raw_free(newBuckets); |  | ||||||
|         return NULL; |  | ||||||
|         /* Successfully rehashed bucket */ |  | ||||||
|         done: |  | ||||||
|     } |  | ||||||
|     dict->capacity = newCapacity; |  | ||||||
|     return dict; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* Find a bucket with a given key */ |  | ||||||
| static int gst_dict_find(GstDict *dict, GstValue key, GstDictBucket **out) { |  | ||||||
|     uint32_t index, i; |  | ||||||
|     GstDictBucket *buckets = dict->buckets; |  | ||||||
|     index = gst_hash(key) % dict->capacity;  |  | ||||||
|     for (i = index; i < dict->capacity; ++i) { |  | ||||||
|         if (buckets[i].flags & GST_DICT_FLAGS_TOMBSTONE) continue; |  | ||||||
|         if (!(buckets[i].flags & GST_DICT_FLAGS_OCCUPIED)) continue; |  | ||||||
|         if (!gst_equals(key, buckets[i].key)) continue; |  | ||||||
|         *out = buckets + i; |  | ||||||
|         return 1; |  | ||||||
|     } |  | ||||||
|     for (i = 0; i < index; ++i) { |  | ||||||
|         if (buckets[i].flags & GST_DICT_FLAGS_TOMBSTONE) continue; |  | ||||||
|         if (!(buckets[i].flags & GST_DICT_FLAGS_OCCUPIED)) continue; |  | ||||||
|         if (!gst_equals(key, buckets[i].key)) continue; |  | ||||||
|         *out = buckets + i; |  | ||||||
|         return 1; |  | ||||||
|     } |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /* Get item from dictionary */ | /* Get item from dictionary */ | ||||||
| int gst_dict_get(GstDict *dict, GstValue key, GstValue *value) { | GstValue gst_dict_get(GstDict *dict, GstValue key) { | ||||||
|     GstDictBucket *bucket; |     GstValue *bucket *notused; | ||||||
|     int found = gst_dict_find(dict, key, &bucket); |     if (dict->flags & GST_OBJECT_FLAG_ISBAG) { | ||||||
|     if (found) | 		bucket = gst_object_bag_find(dict, key); | ||||||
|         *value = bucket->value; |     } else { | ||||||
|     return found; |         bucket = hash_findkv(obj->data, obj->capacity, key, ¬used); | ||||||
|  |     } | ||||||
|  |     if (bucket != NULL) { | ||||||
|  | 		return bucket[1]; | ||||||
|  |     } else { | ||||||
|  |         GstValue ret; | ||||||
|  | 		ret.type = GST_NIL; | ||||||
|  | 		return ret; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /* Get item with c string key */ | ||||||
|  | GstValue gst_dict_get_cstring(GstDict *dict, const char *key); | ||||||
|  |  | ||||||
| /* Add item to dictionary */ | /* Add item to dictionary */ | ||||||
| GstDict *gst_dict_put(GstDict *dict, GstValue key, GstValue value) { | void gst_dict_put(Gst *vm, GstDict *obj, GstValue key, GstValue value) { | ||||||
|     /* Check if we need to increase capacity. The load factor is low |     if (obj->flags & GST_OBJECT_FLAG_ISBAG) { | ||||||
|      * because we are using linear probing */ |         if (obj->count > GST_OBJECT_BAG_THRESHOLD) { | ||||||
|     uint32_t index, i; |             /* Change to hashtable */ | ||||||
|     uint32_t newCap = dict->count * 2 + 1; | 			obj->flags |= GST_OBJECT_FLAG_ISBAG; | ||||||
|     GstBucket *buckets; | 			gst_object_rehash(vm, obj, 4 * obj->count); | ||||||
|     if (newCap > dict->capacity) { | 			goto put_hash; | ||||||
|         dict = gst_dict_rehash(dict, newCap); |         } | ||||||
|         if (!dict) return dict; | 		gst_object_bag_put(vm, obj, key, value); | ||||||
|  |     } else { | ||||||
|  |         GstValue *bucket, *out; | ||||||
|  | 		put_hash: | ||||||
|  |         bucket = hash_findkv(obj->data, obj->capacity, key, &out); | ||||||
|  | 		if (bucket != NULL) { | ||||||
|  | 			bucket[1] = value; | ||||||
|  | 		} else { | ||||||
|  | 			/* Check for resize */ | ||||||
|  | 			if (obj->count + 1 > obj->capacity) { | ||||||
|  |     			gst_object_rehash(vm, obj, 2 * (obj->count + 1)); | ||||||
|  |                 bucket = hash_findkv(obj->data, obj->capacity, key, &out); | ||||||
|  | 			} | ||||||
|  | 			out[0] = key; | ||||||
|  | 			out[1] = value; | ||||||
|  | 			++obj->count; | ||||||
|  | 		} | ||||||
|     } |     } | ||||||
|     index = gst_hash(key) % dict->capacity; |  | ||||||
|     buckets = dict->buckets; |  | ||||||
|     for (i = index; i < dict->capacity; ++i) { |  | ||||||
|         if ((buckets[i].flags & GST_DICT_FLAGS_TOMBSTONE) || |  | ||||||
|                 !(buckets[i].flags & GST_DICT_FLAGS_OCCUPIED)) |  | ||||||
|             continue; |  | ||||||
|         dict->buckets[i].key = key; |  | ||||||
|         dict->buckets[i].value = value; |  | ||||||
|         dict->buckets[i].flags &= GST_DICT_FLAGS_OCCUPIED; |  | ||||||
|         dict->count++; |  | ||||||
|         return dict; |  | ||||||
|     } |  | ||||||
|     for (i = 0; i < index; ++i) { |  | ||||||
|         if ((buckets[i].flags & GST_DICT_FLAGS_TOMBSTONE) || |  | ||||||
|                 !(buckets[i].flags & GST_DICT_FLAGS_OCCUPIED)) |  | ||||||
|             continue; |  | ||||||
|         dict->buckets[i].key = key; |  | ||||||
|         dict->buckets[i].value = value; |  | ||||||
|         dict->buckets[i].flags &= GST_DICT_FLAGS_OCCUPIED; |  | ||||||
|         dict->count++; |  | ||||||
|         return dict; |  | ||||||
|     } |  | ||||||
|     /* Error - should never get here */ |  | ||||||
|     return NULL; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Remove item from dictionary */ | /* Remove item from dictionary */ | ||||||
| int gst_dict_remove(GstDict *dict, GstValue key) { | void gst_dict_remove(GstDict *obj, GstValue key) { | ||||||
|     GstDictBucket *bucket; |     if (obj->flags & GST_OBJECT_FLAG_ISBAG) { | ||||||
|     int found = gst_dict_find(dict, key, &bucket); | 		gst_object_bag_remove(obj, key); | ||||||
|     if (found) { |     } else { | ||||||
|         bucket->flags |= GST_DICT_FLAGS_TOMBSTONE; |         GstValue *bucket, *out; | ||||||
|         dict->count--; |         bucket = hash_findkv(obj->data, obj->capacity, key, &out); | ||||||
|  | 		if (bucket != NULL) { | ||||||
|  | 			--obj->count; | ||||||
|  | 			bucket[0].type = GST_NIL; | ||||||
|  | 			bucket[1].type = GST_BOOLEAN; | ||||||
|  | 		} | ||||||
|     } |     } | ||||||
|     return found; |  | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										35
									
								
								dict.h
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								dict.h
									
									
									
									
									
								
							| @@ -3,39 +3,36 @@ | |||||||
|  |  | ||||||
| #include "datatypes.h" | #include "datatypes.h" | ||||||
|  |  | ||||||
| #define GST_DICT_FLAG_OCCUPIED 1 | /* Indicates object is implement as unsorted array of keypairs */ | ||||||
| #define GST_DICT_FLAG_TOMBSTONE 2 | #define GST_OBJECT_FLAG_ISBAG (1 << 31) | ||||||
|  |  | ||||||
| typedef struct GstDictBucket GstDictBucket; | /* Indicates object is immutable */ | ||||||
| struct GstDictBucket { | #define GST_OBJECT_IMMUTABLE (1 << 30) | ||||||
|     GstValue key; |  | ||||||
|     GstValue value;    | /* Count at which the object goes from a linear search to a hash table */ | ||||||
|     uint8_t flags; | #define GST_OBJECT_BAG_THRESHOLD 8 | ||||||
| }; |  | ||||||
|  |  | ||||||
| typedef struct GstDict GstDict; | typedef struct GstDict GstDict; | ||||||
| struct GstDict { | struct GstDict { | ||||||
|     uint32_t capacity; |     uint32_t capacity; | ||||||
|     uint32_t count; |     uint32_t count; | ||||||
|     GstDictBucket *buckets; |     uint32_t flags; | ||||||
|  |     GstValue *data; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /* Initialize a dictionary */ | /* Initialize a dictionary */ | ||||||
| GstDict *gst_dict_init(GstDict *dict, uint32_t capacity); | GstDict *gst_dict(Gst *vm, uint32_t capacity); | ||||||
|  |  | ||||||
| /* Deinitialize a dictionary */ |  | ||||||
| GstDict *gst_dict_free(GstDict *dict); |  | ||||||
|  |  | ||||||
| /* Rehash a dictionary */ |  | ||||||
| GstDict *gst_dict_rehash(GstDict *dict, uint32_t newCapacity); |  | ||||||
|  |  | ||||||
| /* Get item from dictionary */ | /* Get item from dictionary */ | ||||||
| int gst_dict_get(GstDict *dict, GstValue key, GstValue *value); | GstValue gst_dict_get(GstDict *dict, GstValue key); | ||||||
|  |  | ||||||
|  | /* Get c string from object */ | ||||||
|  | GstValue gst_dict_get_cstring(GstDict *dict, const char *key); | ||||||
|  |  | ||||||
| /* Add item to dictionary */ | /* Add item to dictionary */ | ||||||
| GstDict *gst_dict_put(GstDict *dict, GstValue key, GstValue value); | void gst_dict_put(Gst *vm, GstDict *dict, GstValue key, GstValue value); | ||||||
|  |  | ||||||
| /* Remove item from dictionary */ | /* Remove item from dictionary */ | ||||||
| int gst_dict_remove(GstDict *dict, GstValue key); | void gst_dict_remove(GstDict *dict, GstValue key); | ||||||
|  |  | ||||||
| #endif // dict_h_INCLUDED | #endif // dict_h_INCLUDED | ||||||
|   | |||||||
							
								
								
									
										9
									
								
								ds.c
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								ds.c
									
									
									
									
									
								
							| @@ -14,7 +14,6 @@ GstBuffer *gst_buffer(Gst *vm, uint32_t capacity) { | |||||||
|     buffer->data = data; |     buffer->data = data; | ||||||
|     buffer->count = 0; |     buffer->count = 0; | ||||||
|     buffer->capacity = capacity; |     buffer->capacity = capacity; | ||||||
|     buffer->flags = 0; |  | ||||||
|     return buffer; |     return buffer; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -76,7 +75,6 @@ GstArray *gst_array(Gst * vm, uint32_t capacity) { | |||||||
|     array->data = data; |     array->data = data; | ||||||
|     array->count = 0; |     array->count = 0; | ||||||
|     array->capacity = capacity; |     array->capacity = capacity; | ||||||
|     array->flags = 0; |  | ||||||
|     return array; |     return array; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -181,7 +179,7 @@ GstObject* gst_object(Gst *vm, uint32_t capacity) { | |||||||
|     o->buckets = buckets; |     o->buckets = buckets; | ||||||
|     o->capacity = capacity; |     o->capacity = capacity; | ||||||
|     o->count = 0; |     o->count = 0; | ||||||
|     o->flags = 0; |     o->meta = NULL; | ||||||
|     return o; |     return o; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -230,9 +228,8 @@ GstValue gst_object_get(GstObject *o, GstValue key) { | |||||||
|  |  | ||||||
| /* Get a value of the object with a cstring key */ | /* Get a value of the object with a cstring key */ | ||||||
| GstValue gst_object_get_cstring(GstObject *obj, const char *key) { | GstValue gst_object_get_cstring(GstObject *obj, const char *key) { | ||||||
|     const char *end = key; |     uint32_t len; | ||||||
|     while (*end++); | 	for (len = 0; key[len]; ++len); | ||||||
|     uint32_t len = end - key; |  | ||||||
|     uint32_t hash = gst_cstring_calchash((uint8_t *)key, len); |     uint32_t hash = gst_cstring_calchash((uint8_t *)key, len); | ||||||
|     uint32_t index = hash % obj->capacity; |     uint32_t index = hash % obj->capacity; | ||||||
|     GstBucket *bucket = obj->buckets[index]; |     GstBucket *bucket = obj->buckets[index]; | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								gc.c
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								gc.c
									
									
									
									
									
								
							| @@ -149,6 +149,12 @@ void gst_mark(Gst *vm, GstValue *x) { | |||||||
| 						bucket = bucket->next; | 						bucket = bucket->next; | ||||||
| 					} | 					} | ||||||
|                 } |                 } | ||||||
|  |                 if (x->data.object->meta != NULL) { | ||||||
|  |                     GstValue temp; | ||||||
|  |                     temp.type = GST_OBJECT; | ||||||
|  |                     temp.data.object = x->data.object->meta; | ||||||
|  | 					gst_mark(vm, &temp); | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|             break; |             break; | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								thread.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								thread.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | #include "datatypes.h" | ||||||
|  |  | ||||||
|  | /* Push Value to thread */ | ||||||
|  | void gst_thread_push(Gst *vm, GstThread *thread, GstValue x) { | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* Push stack frame to thread */ | ||||||
|  | void gst_thread_pushframe(Gst *vm, GstThread *thread, GstValue callee) { | ||||||
|  |  | ||||||
|  | } | ||||||
							
								
								
									
										8
									
								
								value.c
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								value.c
									
									
									
									
									
								
							| @@ -444,16 +444,12 @@ const char *gst_set(Gst *vm, GstValue ds, GstValue key, GstValue value) { | |||||||
|     int32_t index; |     int32_t index; | ||||||
|     switch (ds.type) { |     switch (ds.type) { | ||||||
|     case GST_ARRAY: |     case GST_ARRAY: | ||||||
|         if (ds.data.array->flags & GST_IMMUTABLE) |  | ||||||
|             return "cannot set immutable value"; |  | ||||||
|         if (key.type != GST_NUMBER) return "expected numeric key"; |         if (key.type != GST_NUMBER) return "expected numeric key"; | ||||||
|         index = to_index(key.data.number, ds.data.array->count); |         index = to_index(key.data.number, ds.data.array->count); | ||||||
|         if (index == -1) return "invalid array access"; |         if (index == -1) return "invalid array access"; | ||||||
|         ds.data.array->data[index] = value; |         ds.data.array->data[index] = value; | ||||||
|         break; |         break; | ||||||
|     case GST_BYTEBUFFER: |     case GST_BYTEBUFFER: | ||||||
|         if (ds.data.buffer->flags & GST_IMMUTABLE) |  | ||||||
|             return "cannot set immutable value"; |  | ||||||
|         if (key.type != GST_NUMBER) return "expected numeric key"; |         if (key.type != GST_NUMBER) return "expected numeric key"; | ||||||
|         if (value.type != GST_NUMBER) return "expected numeric value"; |         if (value.type != GST_NUMBER) return "expected numeric value"; | ||||||
|         index = to_index(key.data.number, ds.data.buffer->count); |         index = to_index(key.data.number, ds.data.buffer->count); | ||||||
| @@ -461,8 +457,8 @@ const char *gst_set(Gst *vm, GstValue ds, GstValue key, GstValue value) { | |||||||
|         ds.data.buffer->data[index] = to_byte(value.data.number); |         ds.data.buffer->data[index] = to_byte(value.data.number); | ||||||
|         break; |         break; | ||||||
|     case GST_OBJECT: |     case GST_OBJECT: | ||||||
|         if (ds.data.object->flags & GST_IMMUTABLE) |         if (ds.data.object->meta != NULL) { | ||||||
|             return "cannot set immutable value"; |         } | ||||||
|         gst_object_put(vm, ds.data.object, key, value); |         gst_object_put(vm, ds.data.object, key, value); | ||||||
|         break; |         break; | ||||||
|     default: |     default: | ||||||
|   | |||||||
							
								
								
									
										82
									
								
								vm.c
									
									
									
									
									
								
							
							
						
						
									
										82
									
								
								vm.c
									
									
									
									
									
								
							| @@ -67,7 +67,7 @@ static void gst_load(Gst *vm, GstValue callee) { | |||||||
| #define GST_STATE_WRITE() do { \ | #define GST_STATE_WRITE() do { \ | ||||||
|     *vm->thread = thread; \ |     *vm->thread = thread; \ | ||||||
| } while (0) | } while (0) | ||||||
|         |  | ||||||
| /* Start running the VM from where it left off. Continue running | /* Start running the VM from where it left off. Continue running | ||||||
|  * until the stack size is smaller than minStackSize. */ |  * until the stack size is smaller than minStackSize. */ | ||||||
| static int gst_continue_size(Gst *vm, uint32_t stackBase) { | static int gst_continue_size(Gst *vm, uint32_t stackBase) { | ||||||
| @@ -382,27 +382,53 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { | |||||||
|         case GST_OP_PSH: /* Push stack frame */ |         case GST_OP_PSH: /* Push stack frame */ | ||||||
|             { |             { | ||||||
|                 GstValue *nextStack; |                 GstValue *nextStack; | ||||||
|                 uint32_t expectedArity, normalArity, arity, varArgs, i, locals, nextCount; |                 uint32_t argSlots, fullArgSlots, arity, prefixCount, varArgs, tupleCount, i, locals, nextCount; | ||||||
|  |  | ||||||
|                 /* Get arguments to op */ |                 /* Get arguments to op */ | ||||||
|                 temp = stack[pc[1]]; |                 temp = stack[pc[1]]; | ||||||
|                 arity = pc[2]; |                 arity = pc[2]; | ||||||
|  |  | ||||||
|                 /* Get the size of next stack frame */ |                 /* Get the size of next stack frame */ | ||||||
|                 if (temp.type == GST_FUNCTION) { |                 prefixCount = 0; | ||||||
|                     GstFunction *fn = temp.data.function; |                 recur: | ||||||
|                     locals = fn->def->locals; | 				switch(temp.type) { | ||||||
|                     varArgs = fn->def->flags & GST_FUNCDEF_FLAG_VARARG; | 				default: gst_error(vm, GST_EXPECTED_FUNCTION); | ||||||
|                     expectedArity = fn->def->arity; | 				case GST_FUNCTION: | ||||||
|                     if (arity > expectedArity) | 					{ | ||||||
|                         normalArity = expectedArity; |                         GstFunction *fn = temp.data.function; | ||||||
|                     else |                         locals = fn->def->locals; | ||||||
|                         normalArity = arity; |                         varArgs = fn->def->flags & GST_FUNCDEF_FLAG_VARARG; | ||||||
|                 } else if (temp.type == GST_CFUNCTION) { |                         argSlots = fn->def->arity; | ||||||
|                     locals = normalArity = expectedArity = arity; |                         if (arity + prefixCount > argSlots) { | ||||||
|                     varArgs = 0; |                             fullArgSlots = argSlots; | ||||||
|                 } else { |                             tupleCount = arity + prefixCount - fullArgSlots; | ||||||
|                     gst_error(vm, GST_EXPECTED_FUNCTION); |                         } else { | ||||||
|  |                             fullArgSlots = arity + prefixCount; | ||||||
|  |                             tupleCount = 0; | ||||||
|  |                         } | ||||||
|  |                         break; | ||||||
|  | 					} | ||||||
|  | 				case GST_CFUNCTION: | ||||||
|  |     				{ | ||||||
|  |                         locals = argSlots = fullArgSlots = arity + prefixCount; | ||||||
|  |                         varArgs = tupleCount = 0; | ||||||
|  |                         break; | ||||||
|  |     				} | ||||||
|  |     			case GST_OBJECT: | ||||||
|  |         			{ | ||||||
|  | 						GstObject *meta = temp.data.object->meta; | ||||||
|  | 						if (meta == NULL) gst_error(vm, GST_EXPECTED_FUNCTION); | ||||||
|  | 						vm->tempArray[prefixCount++] = temp; | ||||||
|  | 						temp = gst_object_get_cstring(meta, "call"); | ||||||
|  | 						goto recur; | ||||||
|  |         			} | ||||||
|  |     			case GST_USERDATA: | ||||||
|  |         			{ | ||||||
|  | 						GstObject *meta = ((GstUserdataHeader *)temp.data.pointer - 1)->meta; | ||||||
|  | 						vm->tempArray[prefixCount++] = temp; | ||||||
|  | 						temp = gst_object_get_cstring(meta, "call"); | ||||||
|  | 						goto recur; | ||||||
|  |         			} | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 /* Get next frame size */ |                 /* Get next frame size */ | ||||||
| @@ -426,12 +452,16 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { | |||||||
|                 gst_frame_env(nextStack) = NULL; |                 gst_frame_env(nextStack) = NULL; | ||||||
|                 gst_frame_callee(nextStack) = temp; |                 gst_frame_callee(nextStack) = temp; | ||||||
|                 gst_frame_errjmp(nextStack) = NULL; |                 gst_frame_errjmp(nextStack) = NULL; | ||||||
|  |  | ||||||
|  |                 /* Write prefix args to stack */ | ||||||
|  |                 for (i = 0; i < prefixCount; ++i) | ||||||
|  |                     nextStack[i] = vm->tempArray[i]; | ||||||
|                  |                  | ||||||
|                 /* Write arguments to new stack */ |                 /* Write arguments to new stack */ | ||||||
|                 for (i = 0; i < normalArity; ++i) |                 for (; i < fullArgSlots; ++i) | ||||||
|                     nextStack[i] = stack[pc[3 + i]]; |                     nextStack[i] = stack[pc[3 + i - prefixCount]]; | ||||||
|  |  | ||||||
|                 /* Clear stack */ |                 /* Clear rest of stack */ | ||||||
|                 for (; i < locals; ++i) |                 for (; i < locals; ++i) | ||||||
|                     nextStack[i].type = GST_NIL; |                     nextStack[i].type = GST_NIL; | ||||||
|  |  | ||||||
| @@ -439,11 +469,14 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { | |||||||
|                 if (varArgs) { |                 if (varArgs) { | ||||||
|                     GstValue *tuple; |                     GstValue *tuple; | ||||||
|                     uint32_t j; |                     uint32_t j; | ||||||
|                     tuple = gst_tuple(vm, arity - expectedArity); |                     tuple = gst_tuple(vm, tupleCount); | ||||||
|                     for (j = expectedArity; j < arity; ++j) |                     for (j = argSlots; j < arity; ++j) | ||||||
|                         tuple[j - expectedArity] = stack[pc[3 + j]]; |                         if (j < prefixCount) | ||||||
|                     nextStack[expectedArity].type = GST_TUPLE; | 							tuple[j - argSlots] = vm->tempArray[j - prefixCount]; | ||||||
|                     nextStack[expectedArity].data.tuple = tuple; |                         else | ||||||
|  |                             tuple[j - argSlots] = stack[pc[3 + j - prefixCount]]; | ||||||
|  |                     nextStack[argSlots].type = GST_TUPLE; | ||||||
|  |                     nextStack[argSlots].data.tuple = tuple; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 /* Increment pc */ |                 /* Increment pc */ | ||||||
| @@ -486,6 +519,7 @@ static int gst_continue_size(Gst *vm, uint32_t stackBase) { | |||||||
|             } else if (v1.type == GST_CFUNCTION) { |             } else if (v1.type == GST_CFUNCTION) { | ||||||
|                 int status; |                 int status; | ||||||
|                 GST_STATE_WRITE(); |                 GST_STATE_WRITE(); | ||||||
|  |                 vm->ret.type = GST_NIL; | ||||||
|                 status = v1.data.cfunction(vm); |                 status = v1.data.cfunction(vm); | ||||||
|                 GST_STATE_SYNC(); |                 GST_STATE_SYNC(); | ||||||
|                 if (status == GST_RETURN_OK) |                 if (status == GST_RETURN_OK) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Calvin Rose
					Calvin Rose