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:
parent
a9f6593979
commit
120ed36552
52
core/ssb.js
52
core/ssb.js
@ -5,54 +5,6 @@ let g_attendants = {};
|
|||||||
const k_use_create_history_stream = false;
|
const k_use_create_history_stream = false;
|
||||||
const k_blobs_concurrent_target = 8;
|
const k_blobs_concurrent_target = 8;
|
||||||
|
|
||||||
function following(db, id) {
|
|
||||||
var o = db.get(id + ":following");
|
|
||||||
const k_version = 5;
|
|
||||||
var f = o ? JSON.parse(o) : o;
|
|
||||||
if (!f || f.version != k_version) {
|
|
||||||
f = {users: [], sequence: 0, version: k_version};
|
|
||||||
}
|
|
||||||
f.users = new Set(f.users);
|
|
||||||
ssb.sqlStream(
|
|
||||||
"SELECT "+
|
|
||||||
" sequence, "+
|
|
||||||
" json_extract(content, '$.contact') AS contact, "+
|
|
||||||
" json_extract(content, '$.following') AS following "+
|
|
||||||
"FROM messages "+
|
|
||||||
"WHERE "+
|
|
||||||
" author = ?1 AND "+
|
|
||||||
" sequence > ?2 AND "+
|
|
||||||
" json_extract(content, '$.type') = 'contact' "+
|
|
||||||
"UNION SELECT MAX(sequence) AS sequence, NULL, NULL FROM messages WHERE author = ?1 "+
|
|
||||||
"ORDER BY sequence",
|
|
||||||
[id, f.sequence],
|
|
||||||
function(row) {
|
|
||||||
if (row.following) {
|
|
||||||
f.users.add(row.contact);
|
|
||||||
} else {
|
|
||||||
f.users.delete(row.contact);
|
|
||||||
}
|
|
||||||
f.sequence = row.sequence;
|
|
||||||
});
|
|
||||||
f.users = Array.from(f.users).sort();
|
|
||||||
var j = JSON.stringify(f);
|
|
||||||
if (o != j) {
|
|
||||||
db.set(id + ":following", j);
|
|
||||||
}
|
|
||||||
return f.users;
|
|
||||||
}
|
|
||||||
|
|
||||||
function followingDeep(db, seed_ids, depth) {
|
|
||||||
if (depth <= 0) {
|
|
||||||
return seed_ids;
|
|
||||||
}
|
|
||||||
var f = seed_ids.map(x => following(db, x));
|
|
||||||
var ids = [].concat(...f);
|
|
||||||
var x = followingDeep(db, [...new Set(ids)].sort(), depth - 1);
|
|
||||||
x = [...new Set([].concat(...x, ...seed_ids))].sort();
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_latest_sequence_for_author(author) {
|
function get_latest_sequence_for_author(author) {
|
||||||
var sequence = 0;
|
var sequence = 0;
|
||||||
ssb.sqlStream(
|
ssb.sqlStream(
|
||||||
@ -140,7 +92,7 @@ ssb.addEventListener('connections', function on_connections_changed(change, conn
|
|||||||
if (k_use_create_history_stream) {
|
if (k_use_create_history_stream) {
|
||||||
connection.send_json({'name': ['createHistoryStream'], 'type': 'source', 'args': [{'id': connection.id, 'seq': sequence, 'live': true, 'keys': false}]}, storeMessage);
|
connection.send_json({'name': ['createHistoryStream'], 'type': 'source', 'args': [{'id': connection.id, 'seq': sequence, 'live': true, 'keys': false}]}, storeMessage);
|
||||||
var identities = ssb.getAllIdentities();
|
var identities = ssb.getAllIdentities();
|
||||||
followingDeep(g_database, identities, 2).then(function(ids) {
|
ssb.followingDeep(identities, 2).then(function(ids) {
|
||||||
for (let id of ids) {
|
for (let id of ids) {
|
||||||
if (identities.indexOf(id) != -1) {
|
if (identities.indexOf(id) != -1) {
|
||||||
continue;
|
continue;
|
||||||
@ -226,7 +178,7 @@ function ebtReplicateSendClock(request, have) {
|
|||||||
var identities = ssb.getAllIdentities();
|
var identities = ssb.getAllIdentities();
|
||||||
var message = {};
|
var message = {};
|
||||||
var last_sent = request.connection.sent_clock || {};
|
var last_sent = request.connection.sent_clock || {};
|
||||||
var ids = followingDeep(g_database, identities, 2).concat([request.connection.id]);
|
var ids = ssb.followingDeep(identities, 2).concat([request.connection.id]);
|
||||||
if (!Object.keys(last_sent).length) {
|
if (!Object.keys(last_sent).length) {
|
||||||
for (let id of ids) {
|
for (let id of ids) {
|
||||||
message[id] = get_latest_sequence_for_author(id);
|
message[id] = get_latest_sequence_for_author(id);
|
||||||
|
123
src/ssb.db.c
123
src/ssb.db.c
@ -973,6 +973,129 @@ bool tf_ssb_db_identity_get_private_key(tf_ssb_t* ssb, const char* user, const c
|
|||||||
return success;
|
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)
|
static void _test_private(sqlite3* db, const uint8_t* private_key)
|
||||||
{
|
{
|
||||||
sqlite3_stmt* statement = NULL;
|
sqlite3_stmt* statement = NULL;
|
||||||
|
@ -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);
|
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);
|
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);
|
void tf_ssb_db_private(sqlite3* db);
|
||||||
|
40
src/ssb.js.c
40
src/ssb.js.c
@ -966,6 +966,45 @@ static JSValue _tf_ssb_connectionSendJson(JSContext* context, JSValueConst this_
|
|||||||
return JS_NewInt32(context, request_number);
|
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)
|
void tf_ssb_register(JSContext* context, tf_ssb_t* ssb)
|
||||||
{
|
{
|
||||||
JS_NewClassID(&_tf_ssb_classId);
|
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, "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, "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, "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. */
|
/* Should be trusted only. */
|
||||||
JS_SetPropertyStr(context, object, "addRpc", JS_NewCFunction(context, _tf_ssb_add_rpc, "addRpc", 2));
|
JS_SetPropertyStr(context, object, "addRpc", JS_NewCFunction(context, _tf_ssb_add_rpc, "addRpc", 2));
|
||||||
|
Loading…
Reference in New Issue
Block a user