Continuing to chip away at moving ssb.js to C. This time, following.

git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4096 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
2022-12-31 21:44:48 +00:00
parent a9f6593979
commit 120ed36552
4 changed files with 167 additions and 50 deletions

View File

@ -973,6 +973,129 @@ bool tf_ssb_db_identity_get_private_key(tf_ssb_t* ssb, const char* user, const c
return success;
}
typedef struct _following_t following_t;
typedef struct _following_t
{
char id[k_id_base64_len];
following_t** following;
following_t** blocking;
int following_count;
int blocking_count;
int depth;
} following_t;
static int _following_compare(const void* a, const void* b)
{
const char* ida = a;
const following_t* const* followingb = b;
return strcmp(ida, (*followingb)->id);
}
static void _add_following_entry(following_t*** list, int* count, following_t* add)
{
int index = tf_util_insert_index(add->id, *list, *count, sizeof(following_t*), _following_compare);
if (index >= *count || strcmp(add->id, (*list)[index]->id) == 0)
{
*list = tf_resize_vec(*list, sizeof(**list) * (*count + 1));
if (*count - index)
{
memmove(*list + index + 1, *list + index, sizeof(following_t*) * (*count - index));
}
(*list)[index] = add;
}
}
static following_t* _get_following(tf_ssb_t* ssb, const char* id, following_t*** following, int* following_count, int depth, int max_depth)
{
int index = tf_util_insert_index(id, *following, *following_count, sizeof(following_t*), _following_compare);
following_t* entry = NULL;
if (index < *following_count && strcmp(id, (*following)[index]->id) == 0)
{
entry = (*following)[index];
}
else
{
*following = tf_resize_vec(*following, sizeof(*following) * (*following_count + 1));
if (*following_count - index)
{
memmove(*following + index + 1, *following + index, sizeof(following_t*) * (*following_count - index));
}
entry = tf_malloc(sizeof(following_t));
(*following)[index] = entry;
(*following_count)++;
*entry = (following_t) { 0 };
snprintf(entry->id, sizeof(entry->id), "%s", id);
entry->depth = depth;
if (depth < max_depth)
{
sqlite3* db = tf_ssb_get_db(ssb);
sqlite3_stmt* statement = NULL;
if (sqlite3_prepare(db, "SELECT json_extract(content, '$.contact'), json_extract(content, '$.following'), json_extract(content, '$.blocking') FROM messages WHERE author = ? AND json_extract(content, '$.type') = 'contact' ORDER BY sequence", -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW)
{
const char* contact = (const char*)sqlite3_column_text(statement, 0);
if (sqlite3_column_type(statement, 1) != SQLITE_NULL)
{
bool is_following = sqlite3_column_int(statement, 1);
following_t* next = _get_following(ssb, contact, following, following_count, depth + 1, max_depth);
if (is_following)
{
_add_following_entry(&entry->following, &entry->following_count, next);
}
}
if (sqlite3_column_type(statement, 2) != SQLITE_NULL)
{
bool is_blocking = sqlite3_column_int(statement, 2);
following_t* next = _get_following(ssb, contact, following, following_count, depth + 1, 0 /* don't dig deeper into blocked users */);
if (is_blocking)
{
_add_following_entry(&entry->blocking, &entry->blocking_count, next);
}
}
}
}
sqlite3_finalize(statement);
}
}
}
return entry;
}
const char** tf_ssb_db_following_deep(tf_ssb_t* ssb, const char** ids, int count, int depth)
{
following_t** following = NULL;
int following_count = 0;
for (int i = 0; i < count; i++)
{
_get_following(ssb, ids[i], &following, &following_count, 0, depth);
}
char** result = tf_malloc(sizeof(char*) * (following_count + 1) + k_id_base64_len * following_count);
char* result_ids = (char*)result + sizeof(char*) * (following_count + 1);
for (int i = 0; i < following_count; i++)
{
result[i] = result_ids + k_id_base64_len * i;
snprintf(result[i], k_id_base64_len, "%s", following[i]->id);
}
result[following_count] = NULL;
for (int i = 0; i < following_count; i++)
{
tf_free(following[i]->following);
tf_free(following[i]->blocking);
tf_free(following[i]);
}
tf_free(following);
return (const char**)result;
}
static void _test_private(sqlite3* db, const uint8_t* private_key)
{
sqlite3_stmt* statement = NULL;

View File

@ -26,4 +26,6 @@ void tf_ssb_db_identity_visit(tf_ssb_t* ssb, const char* user, void (*callback)(
void tf_ssb_db_identity_visit_all(tf_ssb_t* ssb, void (*callback)(const char* identity, void* user_data), void* user_data);
bool tf_ssb_db_identity_get_private_key(tf_ssb_t* ssb, const char* user, const char* public_key, uint8_t* out_private_key, size_t private_key_size);
const char** tf_ssb_db_following_deep(tf_ssb_t* ssb, const char** ids, int count, int depth);
void tf_ssb_db_private(sqlite3* db);

View File

@ -966,6 +966,45 @@ static JSValue _tf_ssb_connectionSendJson(JSContext* context, JSValueConst this_
return JS_NewInt32(context, request_number);
}
static JSValue _tf_ssb_followingDeep(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{
int depth = 2;
if (!JS_IsArray(context, argv[0]))
{
return JS_ThrowTypeError(context, "Expected argument 1 to be an array of ids.");
}
if (JS_ToInt32(context, &depth, argv[1]))
{
return JS_ThrowTypeError(context, "Could not convert argument 2 to integer.");
}
int count = tf_util_get_length(context, argv[0]);
const char** ids = tf_malloc(count * sizeof(char*));
for (int i = 0; i < count; i++)
{
JSValue id = JS_GetPropertyUint32(context, argv[0], i);
ids[i] = JS_ToCString(context, id);
JS_FreeValue(context, id);
}
tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
const char** following_deep = tf_ssb_db_following_deep(ssb, ids, count, depth);
JSValue result = JS_NewArray(context);
int index = 0;
for (const char** id = following_deep; *id; id++)
{
JS_SetPropertyUint32(context, result, index++, JS_NewString(context, *id));
}
tf_free(following_deep);
for (int i = 0; i < count; i++)
{
JS_FreeCString(context, ids[i]);
}
tf_free(ids);
return result;
}
void tf_ssb_register(JSContext* context, tf_ssb_t* ssb)
{
JS_NewClassID(&_tf_ssb_classId);
@ -1005,6 +1044,7 @@ void tf_ssb_register(JSContext* context, tf_ssb_t* ssb)
JS_SetPropertyStr(context, object, "getBroadcasts", JS_NewCFunction(context, _tf_ssb_getBroadcasts, "getBroadcasts", 0));
JS_SetPropertyStr(context, object, "connect", JS_NewCFunction(context, _tf_ssb_connect, "connect", 1));
JS_SetPropertyStr(context, object, "createTunnel", JS_NewCFunction(context, _tf_ssb_createTunnel, "createTunnel", 3));
JS_SetPropertyStr(context, object, "followingDeep", JS_NewCFunction(context, _tf_ssb_followingDeep, "followingDeep", 2));
/* Should be trusted only. */
JS_SetPropertyStr(context, object, "addRpc", JS_NewCFunction(context, _tf_ssb_add_rpc, "addRpc", 2));