From 56c3b8aa94fff0ebc6287e3d8f347abe077cc039 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sun, 9 Sep 2018 12:13:32 -0400 Subject: [PATCH] Add ability to index into structs and dictionaries generically. --- natives/json/json.c | 5 +++-- natives/sqlite3/main.c | 3 +-- src/core/struct.c | 13 ++++++------ src/core/table.c | 35 ++----------------------------- src/core/util.c | 44 +++++++++++++++++++++++++++++++++++++++ src/core/util.h | 3 +++ src/include/janet/janet.h | 9 ++++++-- 7 files changed, 66 insertions(+), 46 deletions(-) diff --git a/natives/json/json.c b/natives/json/json.c index 212a87ea..c1b568c9 100644 --- a/natives/json/json.c +++ b/natives/json/json.c @@ -70,7 +70,8 @@ static char *decode_utf16_escape(const char *p, uint32_t *outpoint) { return NULL; } -/* Parse a string */ +/* Parse a string. Also handles the conversion of utf-16 to + * utf-8. */ const char *decode_string(const char **p, Janet *out) { JanetBuffer *buffer = janet_buffer(0); const char *cp = *p; @@ -571,7 +572,7 @@ static int json_encode(JanetArgs args) { JANET_ARG_BYTES(e.newline, e.newlinelen, args, 2); } else { e.newline = (const uint8_t *)"\r\n"; - e.newlinelen = 1; + e.newlinelen = 2; } } const char *err = encode_one(&e, args.v[0], 0); diff --git a/natives/sqlite3/main.c b/natives/sqlite3/main.c index 827d88c6..71da5582 100644 --- a/natives/sqlite3/main.c +++ b/natives/sqlite3/main.c @@ -77,8 +77,7 @@ static int sql_open(JanetArgs args) { static int sql_close(JanetArgs args) { Db *db; JANET_FIXARITY(args, 1); - JANET_CHECKABSTRACT(args, 0, &sql_conn_type); - db = (Db *)janet_unwrap_abstract(args.v[0]); + JANET_ARG_ABSTRACT(db, args, 0, &sql_conn_type); closedb(db); JANET_RETURN_NIL(args); } diff --git a/src/core/struct.c b/src/core/struct.c index 4bf48949..8b160e61 100644 --- a/src/core/struct.c +++ b/src/core/struct.c @@ -24,8 +24,6 @@ #include "gc.h" #include "util.h" -#define janet_struct_maphash(cap, hash) ((uint32_t)(hash & (cap - 1))); - /* Begin creation of a struct */ JanetKV *janet_struct_begin(int32_t count) { @@ -43,10 +41,11 @@ JanetKV *janet_struct_begin(int32_t count) { return st; } -/* Find an item in a struct */ +/* Find an item in a struct. Should be similar to janet_dict_find, but + * specialized to structs (slightly more compact). */ const JanetKV *janet_struct_find(const JanetKV *st, Janet key) { int32_t cap = janet_struct_capacity(st); - int32_t index = janet_struct_maphash(cap, janet_hash(key)); + int32_t index = janet_maphash(cap, janet_hash(key)); int32_t i; for (i = index; i < cap; i++) if (janet_checktype(st[i].key, JANET_NIL) || janet_equals(st[i].key, key)) @@ -68,7 +67,7 @@ const JanetKV *janet_struct_find(const JanetKV *st, Janet key) { void janet_struct_put(JanetKV *st, Janet key, Janet value) { int32_t cap = janet_struct_capacity(st); int32_t hash = janet_hash(key); - int32_t index = janet_struct_maphash(cap, hash); + int32_t index = janet_maphash(cap, hash); int32_t i, j, dist; int32_t bounds[4] = {index, cap, 0, index}; if (janet_checktype(key, JANET_NIL) || janet_checktype(value, JANET_NIL)) return; @@ -95,7 +94,7 @@ void janet_struct_put(JanetKV *st, Janet key, Janet value) { * will compare properly - i.e., {1 2 3 4} should equal {3 4 1 2}. * Collisions are resolved via an insertion sort insertion. */ otherhash = janet_hash(kv->key); - otherindex = janet_struct_maphash(cap, otherhash); + otherindex = janet_maphash(cap, otherhash); otherdist = (i + cap - otherindex) & (cap - 1); if (dist < otherdist) status = -1; @@ -231,4 +230,4 @@ int janet_struct_compare(const JanetKV *lhs, const JanetKV *rhs) { return 0; } -#undef janet_struct_maphash +#undef janet_maphash diff --git a/src/core/table.c b/src/core/table.c index 82546d5a..fe268fcb 100644 --- a/src/core/table.c +++ b/src/core/table.c @@ -24,8 +24,6 @@ #include "gc.h" #include "util.h" -#define janet_table_maphash(cap, hash) ((uint32_t)(hash) & (cap - 1)) - /* Initialize a table */ JanetTable *janet_table_init(JanetTable *table, int32_t capacity) { JanetKV *data; @@ -61,36 +59,7 @@ JanetTable *janet_table(int32_t capacity) { /* Find the bucket that contains the given key. Will also return * bucket where key should go if not in the table. */ JanetKV *janet_table_find(JanetTable *t, Janet key) { - int32_t index = janet_table_maphash(t->capacity, janet_hash(key)); - int32_t i; - JanetKV *first_bucket = NULL; - /* Higher half */ - for (i = index; i < t->capacity; i++) { - JanetKV *kv = t->data + i; - if (janet_checktype(kv->key, JANET_NIL)) { - if (janet_checktype(kv->value, JANET_NIL)) { - return kv; - } else if (NULL == first_bucket) { - first_bucket = kv; - } - } else if (janet_equals(kv->key, key)) { - return t->data + i; - } - } - /* Lower half */ - for (i = 0; i < index; i++) { - JanetKV *kv = t->data + i; - if (janet_checktype(kv->key, JANET_NIL)) { - if (janet_checktype(kv->value, JANET_NIL)) { - return kv; - } else if (NULL == first_bucket) { - first_bucket = kv; - } - } else if (janet_equals(kv->key, key)) { - return t->data + i; - } - } - return first_bucket; + return (JanetKV *) janet_dict_find(t->data, t->capacity, key); } /* Resize the dictionary table. */ @@ -298,4 +267,4 @@ int janet_lib_table(JanetArgs args) { return 0; } -#undef janet_table_maphash +#undef janet_maphash diff --git a/src/core/util.c b/src/core/util.c index 3e8dddb1..4d098dba 100644 --- a/src/core/util.c +++ b/src/core/util.c @@ -95,6 +95,50 @@ int32_t janet_tablen(int32_t n) { return n + 1; } +/* Helper to find a value in a Janet struct or table. Returns the bucket + * containg the key, or the first empty bucket if there is no such key. */ +const JanetKV *janet_dict_find(const JanetKV *buckets, int32_t cap, Janet key) { + int32_t index = janet_maphash(cap, janet_hash(key)); + int32_t i; + const JanetKV *first_bucket = NULL; + /* Higher half */ + for (i = index; i < cap; i++) { + const JanetKV *kv = buckets + i; + if (janet_checktype(kv->key, JANET_NIL)) { + if (janet_checktype(kv->value, JANET_NIL)) { + return kv; + } else if (NULL == first_bucket) { + first_bucket = kv; + } + } else if (janet_equals(kv->key, key)) { + return buckets + i; + } + } + /* Lower half */ + for (i = 0; i < index; i++) { + const JanetKV *kv = buckets + i; + if (janet_checktype(kv->key, JANET_NIL)) { + if (janet_checktype(kv->value, JANET_NIL)) { + return kv; + } else if (NULL == first_bucket) { + first_bucket = kv; + } + } else if (janet_equals(kv->key, key)) { + return buckets + i; + } + } + return first_bucket; +} + +/* Get a value from a janet struct or table. */ +Janet janet_dictionary_get(const JanetKV *data, int32_t cap, Janet key) { + const JanetKV *kv = janet_dict_find(data, cap, key); + if (kv && !janet_checktype(kv->key, JANET_NIL)) { + return kv->value; + } + return janet_wrap_nil(); +} + /* Compare a janet string with a cstring. more efficient than loading * c string as a janet string. */ int janet_cstrcmp(const uint8_t *str, const char *other) { diff --git a/src/core/util.h b/src/core/util.h index 51d6afdf..76f87831 100644 --- a/src/core/util.h +++ b/src/core/util.h @@ -26,12 +26,15 @@ #include /* Utils */ +#define janet_maphash(cap, hash) ((uint32_t)(hash) & (cap - 1)) extern const char janet_base64[65]; int32_t janet_array_calchash(const Janet *array, int32_t len); int32_t janet_kv_calchash(const JanetKV *kvs, int32_t len); int32_t janet_string_calchash(const uint8_t *str, int32_t len); int32_t janet_tablen(int32_t n); void janet_buffer_push_types(JanetBuffer *buffer, int types); +const JanetKV *janet_dict_find(const JanetKV *buckets, int32_t cap, Janet key); +Janet janet_dict_get(const JanetKV *buckets, int32_t cap, Janet key); const void *janet_strbinsearch( const void *tab, size_t tabcount, diff --git a/src/include/janet/janet.h b/src/include/janet/janet.h index 59274f72..b02707cb 100644 --- a/src/include/janet/janet.h +++ b/src/include/janet/janet.h @@ -1001,6 +1001,7 @@ JANET_API JanetFiber *janet_fiber(JanetFunction *callee, int32_t capacity); JANET_API int janet_indexed_view(Janet seq, const Janet **data, int32_t *len); JANET_API int janet_bytes_view(Janet str, const uint8_t **data, int32_t *len); JANET_API int janet_dictionary_view(Janet tab, const JanetKV **data, int32_t *len, int32_t *cap); +JANET_API Janet janet_dictionary_get(const JanetKV *data, int32_t cap, Janet key); /* Abstract */ #define janet_abstract_header(u) ((JanetAbstractHeader *)(u) - 1) @@ -1100,7 +1101,7 @@ JANET_API int janet_typeabstract_err(JanetArgs args, int32_t n, const JanetAbstr Janet x = (A).v[(N)];\ if (!janet_checktype(x, JANET_ABSTRACT) ||\ janet_abstract_type(janet_unwrap_abstract(x)) != (AT))\ - return janet_typeabstract_err(A, N, AT);\ + return janet_typeabstract_err(A, N, AT);\ } else {\ return janet_typeabstract_err(A, N, AT);\ }\ @@ -1154,7 +1155,11 @@ JANET_API int janet_typeabstract_err(JanetArgs args, int32_t n, const JanetAbstr #define JANET_ARG_BUFFER(DEST, A, N) _JANET_ARG(JANET_BUFFER, buffer, DEST, A, N) #define JANET_ARG_FUNCTION(DEST, A, N) _JANET_ARG(JANET_FUNCTION, function, DEST, A, N) #define JANET_ARG_CFUNCTION(DEST, A, N) _JANET_ARG(JANET_CFUNCTION, cfunction, DEST, A, N) -#define JANET_ARG_ABSTRACT(DEST, A, N) _JANET_ARG(JANET_ABSTRACT, abstract, DEST, A, N) + +#define JANET_ARG_ABSTRACT(DEST, A, N, AT) do { \ + JANET_CHECKABSTRACT(A, N, AT); \ + DEST = janet_unwrap_abstract((A).v[(N)]); \ +} while (0) #define JANET_RETURN_NIL(A) do { return JANET_SIGNAL_OK; } while (0) #define JANET_RETURN_FALSE(A) JANET_RETURN(A, janet_wrap_false())