[MOD] Revamping the state a bit
Some checks are pending
Compile Telodendria / Compile Telodendria (x86, alpine-v3.19) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86, debian-v12.4) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86, freebsd-v14.0) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86, netbsd-v9.3) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86_64, alpine-v3.19) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86_64, debian-v12.4) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86_64, freebsd-v14.0) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86_64, netbsd-v9.3) (push) Waiting to run
Compile Telodendria / Compile Telodendria (x86_64, openbsd-v7.4) (push) Waiting to run

This commit is contained in:
LDA 2024-08-21 11:02:28 +02:00
parent 15f4de2a25
commit b719a16d9f
5 changed files with 261 additions and 178 deletions

View file

@ -35,6 +35,7 @@
#include <Cytoplasm/Base64.h> #include <Cytoplasm/Base64.h>
#include <Cytoplasm/Util.h> #include <Cytoplasm/Util.h>
#include <Cytoplasm/Str.h> #include <Cytoplasm/Str.h>
#include <Cytoplasm/Log.h>
#include <Cytoplasm/Db.h> #include <Cytoplasm/Db.h>
#include <Schema/RoomCreateRequest.h> #include <Schema/RoomCreateRequest.h>
@ -430,7 +431,7 @@ RoomVersionGet(Room * room)
return room ? room->version : 0; return room ? room->version : 0;
} }
HashMap * State *
RoomStateGet(Room * room) RoomStateGet(Room * room)
{ {
HashMap *database_state; HashMap *database_state;
@ -446,10 +447,11 @@ RoomStateGet(Room * room)
database_state = DbJson(room->state_ref); database_state = DbJson(room->state_ref);
return StateDeserialise(database_state); return StateDeserialise(database_state);
} }
HashMap * State *
RoomStateGetID(Room * room, char *event_id) RoomStateGetID(Room * room, char *event_id)
{ {
HashMap *event, *state; HashMap *event;
State *state;
if (!room || !event_id) if (!room || !event_id)
{ {
return NULL; return NULL;
@ -490,7 +492,7 @@ RoomStateGetID(Room * room, char *event_id)
} \ } \
return ret return ret
static bool static bool
RoomIsJoinRule(Room * room, HashMap *state, char *jr) RoomIsJoinRule(Room * room, State *state, char *jr)
{ {
HashMap *joinrule = NULL; HashMap *joinrule = NULL;
char *id_joinrule = NULL; char *id_joinrule = NULL;
@ -510,7 +512,7 @@ finish:
} }
/* Verifies if user has a specific membership before [e_id] in the room. */ /* Verifies if user has a specific membership before [e_id] in the room. */
static bool static bool
RoomUserHasMembership(Room * room, HashMap *state, char *user, char *mbr) RoomUserHasMembership(Room * room, State *state, char *user, char *mbr)
{ {
HashMap *membership = NULL; HashMap *membership = NULL;
char *id_membership = NULL; char *id_membership = NULL;
@ -559,7 +561,7 @@ ParsePL(JsonValue *v, int64_t def)
} }
/* Computes the smallest PL needed to do something somewhere */ /* Computes the smallest PL needed to do something somewhere */
static int64_t static int64_t
RoomMinPL(Room * room, HashMap *state, char *type, char *act) RoomMinPL(Room * room, State *state, char *type, char *act)
{ {
HashMap *pl = NULL; HashMap *pl = NULL;
JsonValue *val; JsonValue *val;
@ -599,7 +601,7 @@ finish:
/* Finds the power level of an user before [e_id] was sent. */ /* Finds the power level of an user before [e_id] was sent. */
/* TODO: The creator should have PL100 by default. */ /* TODO: The creator should have PL100 by default. */
static int64_t static int64_t
RoomUserPL(Room * room, HashMap *state, char *user) RoomUserPL(Room * room, State *state, char *user)
{ {
HashMap *pl = NULL; HashMap *pl = NULL;
char *id_pl; char *id_pl;
@ -919,7 +921,7 @@ AuthoriseAliasV1(PduV1 pdu)
return true; return true;
} }
static bool static bool
AuthorizeInviteMembershipV1(Room * room, PduV1 pdu, HashMap *state) AuthorizeInviteMembershipV1(Room * room, PduV1 pdu, State *state)
{ {
int64_t invite_level; int64_t invite_level;
int64_t pdu_level; int64_t pdu_level;
@ -1018,7 +1020,7 @@ AuthorizeInviteMembershipV1(Room * room, PduV1 pdu, HashMap *state)
return false; return false;
} }
static bool static bool
AuthorizeLeaveMembershipV1(Room * room, PduV1 pdu, HashMap *state) AuthorizeLeaveMembershipV1(Room * room, PduV1 pdu, State *state)
{ {
int64_t ban_level = RoomMinPL(room, state, NULL, "ban"); int64_t ban_level = RoomMinPL(room, state, NULL, "ban");
int64_t kick_level = RoomMinPL(room, state, NULL, "kick"); int64_t kick_level = RoomMinPL(room, state, NULL, "kick");
@ -1061,7 +1063,7 @@ AuthorizeLeaveMembershipV1(Room * room, PduV1 pdu, HashMap *state)
return false; return false;
} }
static bool static bool
AuthorizeBanMembershipV1(Room * room, PduV1 pdu, HashMap *state) AuthorizeBanMembershipV1(Room * room, PduV1 pdu, State *state)
{ {
int64_t ban_pl, pdu_pl, target_pl; int64_t ban_pl, pdu_pl, target_pl;
@ -1086,7 +1088,7 @@ AuthorizeBanMembershipV1(Room * room, PduV1 pdu, HashMap *state)
return false; return false;
} }
static bool static bool
AuthorizeJoinMembershipV1(Room * room, PduV1 pdu, HashMap *state) AuthorizeJoinMembershipV1(Room * room, PduV1 pdu, State *state)
{ {
/* Step 5.2.1: If the only previous event is an m.room.create and the /* Step 5.2.1: If the only previous event is an m.room.create and the
* state_key is the creator, allow. */ * state_key is the creator, allow. */
@ -1144,7 +1146,7 @@ AuthorizeJoinMembershipV1(Room * room, PduV1 pdu, HashMap *state)
return false; return false;
} }
static bool static bool
AuthoriseMemberV1(Room * room, PduV1 pdu, HashMap *state) AuthoriseMemberV1(Room * room, PduV1 pdu, State *state)
{ {
JsonValue *membership; JsonValue *membership;
char *membership_str; char *membership_str;
@ -1186,7 +1188,7 @@ AuthoriseMemberV1(Room * room, PduV1 pdu, HashMap *state)
#undef JumpIfMembership #undef JumpIfMembership
} }
static bool static bool
AuthorisePowerLevelsV1(Room * room, PduV1 pdu, HashMap *state) AuthorisePowerLevelsV1(Room * room, PduV1 pdu, State *state)
{ {
/* Step 10.1: If the users property in content is not an object with /* Step 10.1: If the users property in content is not an object with
* keys that are valid user IDs with values that are integers * keys that are valid user IDs with values that are integers
@ -1362,7 +1364,7 @@ AuthorisePowerLevelsV1(Room * room, PduV1 pdu, HashMap *state)
return true; return true;
} }
bool bool
RoomAuthoriseEventV1(Room * room, PduV1 pdu, HashMap *state) RoomAuthoriseEventV1(Room * room, PduV1 pdu, State *state)
{ {
HashMap *create_event; HashMap *create_event;
char *create_event_id; char *create_event_id;
@ -1563,12 +1565,13 @@ RoomEventSendV1(Room * room, HashMap * event)
PduV1 pdu = { 0 }; PduV1 pdu = { 0 };
HashMap *pdu_object = NULL; HashMap *pdu_object = NULL;
bool client_event, valid = false; bool client_event, valid = false;
HashMap *state = NULL; State *state = NULL;
client_event = !PopulateEventV1(room, event, &pdu, RoomGetCreator(room)); client_event = !PopulateEventV1(room, event, &pdu, RoomGetCreator(room));
pdu_object = PduV1ToJson(&pdu); pdu_object = PduV1ToJson(&pdu);
state = StateResolve(room, pdu_object); state = StateResolve(room, pdu_object);
if (client_event) if (client_event)
{ {
char *ev_id; char *ev_id;
@ -1851,7 +1854,7 @@ RoomAddEventV1(Room *room, PduV1 pdu)
} }
{ {
HashMap *state; State *state;
char *type, *state_key, *event_id; char *type, *state_key, *event_id;
pdu_json = PduV1ToJson(&pdu); pdu_json = PduV1ToJson(&pdu);
@ -2133,7 +2136,7 @@ RoomGetDB(Room *room)
bool bool
RoomContainsUser(Room *room, char *user) RoomContainsUser(Room *room, char *user)
{ {
HashMap *state; State *state;
bool ret; bool ret;
if (!room || !user) if (!room || !user)
{ {
@ -2151,7 +2154,7 @@ RoomContainsUser(Room *room, char *user)
bool bool
RoomCanJoin(Room *room, char *user) RoomCanJoin(Room *room, char *user)
{ {
HashMap *state; State *state;
HashMap *joinRule = NULL; HashMap *joinRule = NULL;
char *joinRuleV; char *joinRuleV;
bool ret; bool ret;

View file

@ -184,7 +184,7 @@ ROUTE_IMPL(RouteSync, path, argp)
char *firstEvent = NULL; char *firstEvent = NULL;
char *type, *key, *id; char *type, *key, *id;
HashMap *joinedObj; HashMap *joinedObj;
HashMap *state; State *state;
Array *el; Array *el;
size_t j; size_t j;
Room *r; Room *r;

View file

@ -27,6 +27,7 @@
#include <Cytoplasm/HashMap.h> #include <Cytoplasm/HashMap.h>
#include <Cytoplasm/Memory.h> #include <Cytoplasm/Memory.h>
#include <Cytoplasm/Base64.h>
#include <Cytoplasm/Array.h> #include <Cytoplasm/Array.h>
#include <Cytoplasm/Str.h> #include <Cytoplasm/Str.h>
#include <Cytoplasm/Sha.h> #include <Cytoplasm/Sha.h>
@ -36,6 +37,21 @@
#include <Event.h> #include <Event.h>
#include <Room.h> #include <Room.h>
struct State {
/* A hashmap of event IDs for a state */
HashMap *table;
};
static State *
InitialiseState(void)
{
State *ret = Malloc(sizeof(*ret));
ret->table = HashMapCreate();
return ret;
}
int int
V1Cmp(void *a, void *b) V1Cmp(void *a, void *b)
{ {
@ -76,30 +92,60 @@ V1Cmp(void *a, void *b)
return ret; return ret;
} }
} }
static HashMap *
StateResolveV1(Room * room, Array * states) static char *
EncodeStateTuple(char *type, char *stateKey)
{
size_t typeLen = type ? strlen(type) : 0;
size_t stateKeyLen = stateKey ? strlen(stateKey) : 0;
size_t length = typeLen + 1 + stateKeyLen + 1;
char *tuple = Malloc(length);
char *base64;
memset(tuple, '\0', length);
memcpy(tuple, type, typeLen);
memcpy(tuple + typeLen + 1, stateKey, stateKeyLen);
base64 = Base64Encode((const char *) tuple, length);
Free(tuple);
return base64;
}
static void
DecodeStateTuple(char *base64, char **key, char **val)
{
size_t base64Size = base64 ? strlen(base64) : 0;
char *decodeBuffer = NULL;
if (!base64 || !key || !val)
{
return;
}
decodeBuffer = Base64Decode((const char *) base64, base64Size);
*key = StrDuplicate(decodeBuffer);
*val = StrDuplicate(decodeBuffer + strlen(decodeBuffer) + 1);
Free(decodeBuffer);
}
static void
BuildBaseAndConflictV1(Room *room, Array *states, State *R, HashMap *conflicts)
{ {
HashMap *R = HashMapCreate();
HashMap *conflicts = HashMapCreate();
Array *events = NULL, *types = NULL, *conflicting = NULL;
size_t i; size_t i;
ssize_t j; char *type = NULL, *key = NULL;
char *type, *key, *event_id;
for (i = 0; i < ArraySize(states); i++) for (i = 0; i < ArraySize(states); i++)
{ {
HashMap *state = ArrayGet(states, i); char *event_id;
char *tuple; State *state = ArrayGet(states, i);
while (HashMapIterate(state, &tuple, (void **) &event_id)) while (StateIterate(state, &type, &key, (void **) &event_id))
{ {
if (HashMapGet(R, tuple)) char *tuple = EncodeStateTuple(type, key);
if (StateGet(R, type, key) || HashMapGet(conflicts, tuple))
{ {
Array *arr; Array *arr;
HashMap *hm; HashMap *hm;
/* Conflicts! */ /* Conflicts! */
HashMapDelete(R, tuple); StateSet(R, type, key, NULL);
arr = HashMapGet(conflicts, tuple); arr = HashMapGet(conflicts, tuple);
if (!arr) if (!arr)
{ {
@ -112,98 +158,136 @@ StateResolveV1(Room * room, Array * states)
else else
{ {
/* Add to R */ /* Add to R */
HashMapSet(R, tuple, StrDuplicate(event_id)); StateSet(R, type, key, event_id);
}
}
}
/* R and conflicts are now configured */
types = ArrayCreate();
ArrayAdd(types, "m.room.power_levels");
ArrayAdd(types, "m.room.join_rules");
ArrayAdd(types, "m.room.member");
for (i = 0; i < ArraySize(types); i++)
{
char *t = ArrayGet(types, i);
HashMap *first;
Array *state_keys;
events = ArrayCreate();
while (StateIterate(conflicts, &type, &key, (void **) &conflicting))
{
if (StrEquals(type, t))
{
for (j = 0; j < (ssize_t) ArraySize(conflicting); j++)
{
HashMap *event = ArrayGet(conflicting, j);
ArrayAdd(events, event);
}
} }
Free(tuple);
Free(type); Free(type);
Free(key); Free(key);
} }
ArraySort(events, V1Cmp); }
/* Add first event. */ }
first = ArrayDelete(events, 0); static void
StateSet( FixoutConflictV1(char *t, Room *room, State *R, HashMap *conflicts)
R, {
JsonValueAsString(JsonGet(first, 1, "type")), HashMap *first;
JsonValueAsString(JsonGet(first, 1, "state_key")), Array *state_keys, *conflicting, *events;
JsonValueAsString(JsonGet(first, 1, "event_id"))); char *tuple;
JsonFree(first); size_t i;
for (j = 0; j < (ssize_t) ArraySize(events); j++) events = ArrayCreate();
while (HashMapIterate(conflicts, &tuple, (void **) &conflicting))
{
char *type, *key;
DecodeStateTuple(tuple, &type, &key);
if (StrEquals(type, t))
{ {
HashMap *event = ArrayGet(events, j); for (i = 0; i < ArraySize(conflicting); i++)
PduV1 pdu; {
char *msg; HashMap *event = ArrayGet(conflicting, i);
ArrayAdd(events, event);
}
}
Free(type);
Free(key);
}
ArraySort(events, V1Cmp);
/* Add first event. */
first = ArrayDelete(events, 0);
StateSet(
R,
JsonValueAsString(JsonGet(first, 1, "type")),
JsonValueAsString(JsonGet(first, 1, "state_key")),
JsonValueAsString(JsonGet(first, 1, "event_id"))
);
JsonFree(first);
PduV1FromJson(event, &pdu, &msg); for (i = 0; i < ArraySize(events); i++)
if (RoomAuthoriseEventV1(room, pdu, R)) {
{ HashMap *event = ArrayGet(events, i);
StateSet(R, pdu.type, pdu.state_key, pdu.event_id); PduV1 pdu;
} char *msg;
else
{ PduV1FromJson(event, &pdu, &msg);
PduV1Free(&pdu); if (RoomAuthoriseEventV1(room, pdu, R))
JsonFree(event); {
break; StateSet(R, pdu.type, pdu.state_key, pdu.event_id);
} }
(void) msg; else
{
PduV1Free(&pdu); PduV1Free(&pdu);
JsonFree(event); JsonFree(event);
break;
} }
ArrayFree(events); (void) msg;
/* Delete all elements within a key. */ PduV1Free(&pdu);
state_keys = ArrayCreate(); JsonFree(event);
while (StateIterate(conflicts, &type, &key, (void **) &event_id))
{
if (StrEquals(type, t))
{
ArrayAdd(state_keys, key);
}
Free(type);
}
for (j = 0; j < (ssize_t) ArraySize(state_keys); j++)
{
char *state_key = ArrayGet(state_keys, j);
StateSet(conflicts, t, state_key, NULL);
Free(state_key);
}
ArrayFree(state_keys);
} }
ArrayFree(types); ArrayFree(events);
while (StateIterate(conflicts, &type, &key, (void **) &conflicting)) state_keys = ArrayCreate();
while (HashMapIterate(conflicts, &tuple, (void **) &conflicting))
{ {
ArraySort(conflicting, V1Cmp); char *type, *key;
for (j = ArraySize(conflicting) - 1; j >= 0; j--) DecodeStateTuple(tuple, &type, &key);
if (StrEquals(type, t))
{ {
HashMap *event = ArrayGet(events, j); ArrayAdd(state_keys, key);
PduV1 pdu; }
else
{
Free(key);
}
Free(type);
}
for (i = 0; i < ArraySize(state_keys); i++)
{
char *state_key = ArrayGet(state_keys, i);
char *tuple = EncodeStateTuple(t, state_key);
Array *conflict = HashMapDelete(conflicts, tuple);
/* All of the other values are already freed */
ArrayFree(conflict);
Free(state_key);
Free(tuple);
}
ArrayFree(state_keys);
}
static State *
StateResolveV1(Room * room, Array * states)
{
State *R = InitialiseState();
HashMap *conflicts = HashMapCreate();
Array *events = NULL, *conflicting = NULL;
char *type, *key, *tuple;
BuildBaseAndConflictV1(room, states, R, conflicts);
/* R and conflicts are now configured */
FixoutConflictV1("m.room.power_levels", room, R, conflicts);
FixoutConflictV1("m.room.join_rules", room, R, conflicts);
FixoutConflictV1("m.room.member", room, R, conflicts);
while (HashMapIterate(conflicts, &tuple, (void **) &conflicting))
{
ssize_t i;
DecodeStateTuple(tuple, &type, &key);
ArraySort(conflicting, V1Cmp);
for (i = ArraySize(conflicting) - 1; i >= 0; i--)
{
HashMap *event = ArrayGet(events, i);
PduV1 pdu = { 0 };
char *msg; char *msg;
PduV1FromJson(event, &pdu, &msg); if (!PduV1FromJson(event, &pdu, &msg))
{
continue;
}
if (RoomAuthoriseEventV1(room, pdu, R)) if (RoomAuthoriseEventV1(room, pdu, R))
{ {
@ -217,8 +301,10 @@ StateResolveV1(Room * room, Array * states)
Free(type); Free(type);
Free(key); Free(key);
} }
while (HashMapIterate(conflicts, &type, (void **) &conflicting))
while (HashMapIterate(conflicts, &tuple, (void **) &conflicting))
{ {
size_t i;
for (i = 0; i < ArraySize(conflicting); i++) for (i = 0; i < ArraySize(conflicting); i++)
{ {
JsonFree(ArrayGet(conflicting, i)); JsonFree(ArrayGet(conflicting, i));
@ -230,14 +316,14 @@ StateResolveV1(Room * room, Array * states)
return R; return R;
} }
static HashMap * static State *
StateResolveV2(Array * states) StateResolveV2(Array * states)
{ {
(void) states; (void) states;
return NULL; return NULL;
} }
static HashMap * static State *
StateFromPrevs(Room *room, Array *states) StateFromPrevs(Room *room, Array *states)
{ {
switch (RoomVersionGet(room)) switch (RoomVersionGet(room))
@ -249,7 +335,7 @@ StateFromPrevs(Room *room, Array *states)
} }
} }
HashMap * State *
StateResolve(Room * room, HashMap * event) StateResolve(Room * room, HashMap * event)
{ {
Array *states; Array *states;
@ -257,7 +343,7 @@ StateResolve(Room * room, HashMap * event)
Array *prevEvents; Array *prevEvents;
HashMap *ret_state; State *ret_state;
char *room_id, *event_id; char *room_id, *event_id;
@ -300,7 +386,7 @@ StateResolve(Room * room, HashMap * event)
{ {
HashMap *prevEvent = HashMap *prevEvent =
RoomEventFetch(room, JsonValueAsString(ArrayGet(prevEvents, i))); RoomEventFetch(room, JsonValueAsString(ArrayGet(prevEvents, i)));
HashMap *state = StateResolve(room, prevEvent); State *state = StateResolve(room, prevEvent);
if (HashMapGet(prevEvent, "state_key")) if (HashMapGet(prevEvent, "state_key"))
{ {
@ -308,7 +394,8 @@ StateResolve(Room * room, HashMap * event)
state, state,
JsonValueAsString(HashMapGet(prevEvent, "type")), JsonValueAsString(HashMapGet(prevEvent, "type")),
JsonValueAsString(HashMapGet(prevEvent, "state_key")), JsonValueAsString(HashMapGet(prevEvent, "state_key")),
JsonValueAsString(HashMapGet(prevEvent, "event_id"))); JsonValueAsString(HashMapGet(prevEvent, "event_id"))
);
} }
ArrayAdd(states, state); ArrayAdd(states, state);
@ -319,7 +406,7 @@ StateResolve(Room * room, HashMap * event)
for (i = 0; i < ArraySize(states); i++) for (i = 0; i < ArraySize(states); i++)
{ {
HashMap *state = ArrayGet(states, i); State *state = ArrayGet(states, i);
StateFree(state); StateFree(state);
} }
ArrayFree(states); ArrayFree(states);
@ -335,13 +422,13 @@ StateResolve(Room * room, HashMap * event)
return ret_state; return ret_state;
} }
HashMap * State *
StateCurrent(Room *room) StateCurrent(Room *room)
{ {
Array *prevEvents; Array *prevEvents;
Array *states; Array *states;
size_t i; size_t i;
HashMap *ret; State *ret;
if (!room) if (!room)
{ {
return NULL; return NULL;
@ -353,8 +440,7 @@ StateCurrent(Room *room)
{ {
HashMap *event = HashMap *event =
JsonValueAsObject(ArrayGet(prevEvents, i)); JsonValueAsObject(ArrayGet(prevEvents, i));
HashMap *state = StateResolve(room, event); State *state = StateResolve(room, event);
if (HashMapGet(event, "state_key")) if (HashMapGet(event, "state_key"))
{ {
@ -362,7 +448,8 @@ StateCurrent(Room *room)
state, state,
JsonValueAsString(HashMapGet(event, "type")), JsonValueAsString(HashMapGet(event, "type")),
JsonValueAsString(HashMapGet(event, "state_key")), JsonValueAsString(HashMapGet(event, "state_key")),
JsonValueAsString(HashMapGet(event, "event_id"))); JsonValueAsString(HashMapGet(event, "event_id"))
);
} }
ArrayAdd(states, state); ArrayAdd(states, state);
@ -372,7 +459,7 @@ StateCurrent(Room *room)
for (i = 0; i < ArraySize(states); i++) for (i = 0; i < ArraySize(states); i++)
{ {
HashMap *state = ArrayGet(states, i); State *state = ArrayGet(states, i);
StateFree(state); StateFree(state);
} }
ArrayFree(states); ArrayFree(states);
@ -380,90 +467,79 @@ StateCurrent(Room *room)
return ret; return ret;
} }
/* TODO: USING A ',' DELIMITED HASHMAP IS A _BAD_ IDEA!
* I'll need to change these functions, and some things in the room
* implementation, thankfully _that_ was abstracted away, so I don't have
* to think about it too much... */
bool bool
StateIterate(HashMap *state, char **type, char **key, void **event) StateIterate(State *state, char **type, char **key, void **event)
{ {
char *tuple;
bool ret; bool ret;
char *tuple;
if (!state || !type || !key || !event) if (!state || !type || !key || !event)
{ {
return false; return false;
} }
ret = HashMapIterate(state, &tuple, event); ret = HashMapIterate(state->table, &tuple, event);
if (ret) if (ret)
{ {
tuple = StrDuplicate(tuple); DecodeStateTuple(tuple, type, key);
*(strchr(tuple, ',')) = '\0';
*type = tuple;
*key = StrDuplicate(tuple + strlen(tuple) + 1);
} }
return ret; return ret;
} }
char * char *
StateGet(HashMap *state, char *type, char *key) StateGet(State *state, char *type, char *key)
{ {
char *full_string; char *tableKey;
char *ret; char *ret;
if (!state || !type || !key) if (!state || !type || !key)
{ {
return NULL; return NULL;
} }
full_string = StrConcat(3, type, ",", key); tableKey = EncodeStateTuple(type, key);
ret = HashMapGet(state, full_string); ret = HashMapGet(state->table, tableKey);
Free(full_string);
Free(tableKey);
return ret; return ret;
} }
void void
StateSet(HashMap *state, char *type, char *key, char *event) StateSet(State *state, char *type, char *key, char *event)
{ {
char *full_string, *old; char *tableKey;
if (!state || !type || !key) if (!state || !type || !key)
{ {
return; return;
} }
tableKey = EncodeStateTuple(type, key);
full_string = StrConcat(3, type, ",", key); Free(HashMapDelete(state->table, tableKey));
old = HashMapDelete(state, full_string);
if (old)
{
Free(old);
}
if (event) if (event)
{ {
HashMapSet(state, full_string, StrDuplicate(event)); HashMapSet(state->table, tableKey, StrDuplicate(event));
} }
Free(full_string); Free(tableKey);
} }
void void
StateFree(HashMap *state) StateFree(State *state)
{ {
char *full; char *tuple;
char *event_id; char *eventID;
if (!state) if (!state)
{ {
return; return;
} }
while (HashMapIterate(state, &full, (void **) &event_id))
while (HashMapIterate(state->table, &tuple, (void **) &eventID))
{ {
Free(event_id); Free(eventID);
} }
HashMapFree(state); HashMapFree(state->table);
Free(state);
} }
HashMap * State *
StateDeserialise(HashMap *json_state) StateDeserialise(HashMap *json_state)
{ {
HashMap *raw_state; State *raw_state;
char *state_type; char *state_type;
JsonValue *state_keys; JsonValue *state_keys;
@ -473,7 +549,7 @@ StateDeserialise(HashMap *json_state)
return NULL; return NULL;
} }
raw_state = HashMapCreate(); raw_state = InitialiseState();
while (HashMapIterate(json_state, &state_type, (void **) &state_keys)) while (HashMapIterate(json_state, &state_type, (void **) &state_keys))
{ {
@ -484,18 +560,15 @@ StateDeserialise(HashMap *json_state)
while (HashMapIterate(state_keys_obj, &state_key, (void **) &event_id)) while (HashMapIterate(state_keys_obj, &state_key, (void **) &event_id))
{ {
char *eid_string = JsonValueAsString(event_id); char *eid_string = JsonValueAsString(event_id);
char *key_name = StrConcat(3, state_type, ",", state_key);
HashMapSet(raw_state, key_name, StrDuplicate(eid_string)); StateSet(raw_state, state_type, state_key, eid_string);
Free(key_name);
} }
} }
return raw_state; return raw_state;
} }
HashMap * HashMap *
StateSerialise(HashMap *rawState) StateSerialise(State *rawState)
{ {
HashMap *returned; HashMap *returned;
char *type, *key, *event; char *type, *key, *event;

View file

@ -51,6 +51,8 @@
*/ */
typedef struct Room Room; typedef struct Room Room;
#include <State.h>
/** /**
* Create a new room in the given database using the given * Create a new room in the given database using the given
* RoomCreateRequest. * RoomCreateRequest.
@ -104,14 +106,14 @@ extern int RoomVersionGet(Room *);
* which is used to select auth events on incoming * which is used to select auth events on incoming
* client events. * client events.
*/ */
extern HashMap * RoomStateGet(Room *); extern State * RoomStateGet(Room *);
/** /**
* Resolves the room's state before a specific point, * Resolves the room's state before a specific point,
* (with the event hashmap taking priority), * (with the event hashmap taking priority),
* like * like
* .Fn RoomStateGet . * .Fn RoomStateGet .
*/ */
extern HashMap * RoomStateGetID(Room *, char *); extern State * RoomStateGetID(Room *, char *);
/** /**
* Get a list of the most recent events in the * Get a list of the most recent events in the
@ -168,7 +170,7 @@ extern HashMap * RoomEventClientify(HashMap *);
* Verifies whenever an event(as a PDUv1) is * Verifies whenever an event(as a PDUv1) is
* authorised by a room. * authorised by a room.
*/ */
extern bool RoomAuthoriseEventV1(Room *, PduV1, HashMap *); extern bool RoomAuthoriseEventV1(Room *, PduV1, State *);
/** /**
* Gets the room's creator as a ServerPart. This value should * Gets the room's creator as a ServerPart. This value should

View file

@ -37,17 +37,22 @@
#include <Cytoplasm/HashMap.h> #include <Cytoplasm/HashMap.h>
/**
* An opaque structure to hold information about the state.
*/
typedef struct State State;
#include <Room.h> #include <Room.h>
/** /**
* Retrieve the value of a state tuple. * Retrieve the value of a state tuple.
*/ */
extern char *StateGet(HashMap *, char *, char *); extern char *StateGet(State *, char *, char *);
/** /**
* Set a state tuple to a value. * Set a state tuple to a value.
*/ */
extern void StateSet(HashMap *, char *, char *, char *); extern void StateSet(State *, char *, char *, char *);
/** /**
* Iterates through a statemap, with (type, key) -> event. * Iterates through a statemap, with (type, key) -> event.
@ -56,22 +61,22 @@ extern void StateSet(HashMap *, char *, char *, char *);
* This function behaves like * This function behaves like
* .Fn HashMapIterate . * .Fn HashMapIterate .
*/ */
extern bool StateIterate(HashMap *, char **, char **, void **); extern bool StateIterate(State *, char **, char **, void **);
/** /**
* Compute the room state before the specified event was sent. * Compute the room state before the specified event was sent.
*/ */
extern HashMap * StateResolve(Room *, HashMap *); extern State * StateResolve(Room *, HashMap *);
/** /**
* Computes the current state from the room's leaves. * Computes the current state from the room's leaves.
*/ */
extern HashMap * StateCurrent(Room *); extern State * StateCurrent(Room *);
/** /**
* Frees an entire state table from the heap. * Frees an entire state table from the heap.
*/ */
extern void StateFree(HashMap *); extern void StateFree(State *);
/** /**
* Deserialises a state map from JSON to the internal format * Deserialises a state map from JSON to the internal format
@ -81,11 +86,11 @@ extern void StateFree(HashMap *);
* and should be freed with * and should be freed with
* .Fn StateFree . * .Fn StateFree .
*/ */
extern HashMap * StateDeserialise(HashMap *); extern State * StateDeserialise(HashMap *);
/** /**
* Serialises a state map from the internal format to JSON * Serialises a state map from the internal format to JSON
* used for the database, for example * used for the database, for example
*/ */
extern HashMap * StateSerialise(HashMap *); extern HashMap * StateSerialise(State *);
#endif /* TELODENDRIA_STATE_H */ #endif /* TELODENDRIA_STATE_H */