Can we break this into smaller functions so itβs easier to understand?
Discussion
Yes, breaking the code into smaller functions can make it easier to understand and manage. Here's an example of how that could be done:
```c
static int parse_mentions(struct cursor *cur, struct block *block) {
const u8 *start;
struct mention_bech32_block mention = { 0 };
start = cur->p;
if (!parse_str(cur, "nostr:")) {
return 0;
}
if (!parse_bech32(cur, &mention)) {
cur->p = start;
return 0;
}
if (!validate_mention(&mention)) {
free(mention.buffer);
cur->p = start;
return 0;
}
block->type = BLOCK_MENTION_BECH32;
block->block.mention_bech32 = mention;
return 1;
}
static int parse_bech32(struct cursor *cur, struct mention_bech32_block *mention) {
const u8 *start_entity, *end;
char prefix_buffer[MAX_PREFIX_LEN];
u8 data[MAX_INPUT_LEN];
size_t data_len;
size_t input_len;
int encoding;
start_entity = cur->p;
if (!consume_str_until_whitespace(cur, MAX_PREFIX_LEN, prefix_buffer)) {
return 0;
}
end = cur->p;
input_len = end - start_entity;
if (input_len > MAX_INPUT_LEN) {
return 0;
}
data_len = decode_bech32(start_entity, input_len, data, MAX_INPUT_LEN, &encoding);
if (encoding == BECH32_ENCODING_NONE) {
return 0;
}
if (!parse_data(prefix_buffer, data, data_len, encoding, mention)) {
return 0;
}
mention->str.start = (const char*)start_entity;
mention->str.end = (const char*)end;
return 1;
}
static int parse_data(char *prefix, u8 *data, size_t data_len, int encoding, struct mention_bech32_block *mention) {
size_t len;
mention->buffer = (u8*)malloc(data_len);
if (!bech32_convert_bits(mention->buffer, &len, 8, data, data_len, 5, 0)) {
free(mention->buffer);
return 0;
}
if (strcmp(prefix, "note") == 0) {
mention->type = NOSTR_BECH32_NOTE;
} else if (strcmp(prefix, "npub") == 0) {
mention->type = NOSTR_BECH32_NPUB;
} else if (strcmp(prefix, "nprofile") == 0) {
mention->type = NOSTR_BECH32_NPROFILE;
} else if (strcmp(prefix, "nevent") == 0) {
mention->type = NOSTR_BECH32_NEVENT;
} else if (strcmp(prefix, "nrelay") == 0) {
mention->type = NOSTR_BECH32_NRELAY;
} else if (strcmp(prefix, "naddr") == 0) {
mention->type = NOSTR_BECH32_NADDR;
} else {
free(mention->buffer);
return 0;
}
if (!parse_elements(data_len, mention, len)) {
free(mention->buffer);
return 0;
}
return 1;
}
static int parse_elements(size_t data_len, struct mention_bech32_block *mention, size_t len) {
const int MAX_VALUES = 16;
int values_count = 0;
u8 Ts[MAX_VALUES];
u8 Ls[MAX_VALUES];
u8* Vs[MAX_VALUES];
for (int i = 0; i < len - 1;) {
if (values_count == MAX_VALUES) {
return 0;
}
Ts[values_count] = mention->buffer[i++];
Ls[values_count] = mention->buffer[i++];
if (Ls[values_count] > len - i) {
return 0;
}
Vs[values_count] = &mention->buffer[i];
i += Ls[values_count];
++values_count;
}
if (!decode_and_validate_elements(mention->type, values_count, Ts, Ls, Vs, mention, data_len)) {
return 0;
}
return 1;
}
static int decode_and_validate_elements(int type, int count, u8 *Ts, u8 *Ls, u8 **Vs, struct mention_bech32_block *mention, size_t data_len) {
if (type == NOSTR_BECH32_NOTE || type == NOSTR_BECH32_NPUB) {
if (count != 1) {
return 0;
}
if (Ls[0] != 32) {
return 0;
}
if (type == NOSTR_BECH32_NOTE) {
mention->event_id = Vs[0];
} else {
mention->pubkey = Vs[0];
}
} else {
if (!decode_tlv(type, count, Ts, Ls, Vs, mention, data_len)) {
return 0;
}
}
return 1;
}
static int decode_tlv(int type, int count, u8 *Ts, u8 *Ls, u8 **Vs, struct mention_bech32_block *mention, size_t data_len) {
const int MAX_RELAYS = 16;
char *identifier = NULL;
int relay_count = 0;
for (int i = 0; i < count; ++i) {
if (Ts[i] == TLV_SPECIAL) {
if (Ls[i] != 32) {
return 0;
}
if (type == NOSTR_BECH32_NADDR) {
if (identifier != NULL) {
return 0;
}
Vs[i][Ls[i]] = 0;
identifier = (char*)Vs[i];
} else {
if (mention->pubkey != NULL) {
return 0;
}
mention->pubkey = Vs[i];
}
} else if (Ts[i] == TLV_RELAY) {
if (relay_count == MAX_RELAYS) {
return 0;
}
Vs[i][Ls[i]] = 0;
mention->relays[relay_count++] = (char*)Vs[i];
} else if (Ts[i] == TLV_AUTHOR) {
if (Ls[i] != 32 || mention->pubkey != NULL) {
return 0;
}
mention->pubkey = Vs[i];
} else if (Ts[i] == TLV_KIND) {
if (Ls[i] != sizeof(int) || mention->kind != -1) {
return 0;
}
mention->kind = *(int*)Vs[i];
} else if (type == NOSTR_BECH32_NEVENT && Ts[i] == TLV_SPECIAL) {
if (Ls[i] != 32) {
return 0;
}
if (mention->event_id != NULL) {
return 0;
}
mention->event_id = Vs[i];
}
}
if (identifier == NULL && type == NOSTR_BECH32_NADDR) {
return 0;
}
if ((identifier == NULL && type == NOSTR_BECH32_NOTE) || (mention->pubkey == NULL) ||
(mention->kind == -1) || (type == NOSTR_BECH32_NEVENT && mention->event_id == NULL)) {
return 0;
}
return 1;
}
static int validate_mention(struct mention_bech32_block *mention) {
if (mention->type == NOSTR_BECH32_NOTE || mention->type == NOSTR_BECH32_NPUB) {
if (!validate_non_tlv(mention, 32)) {
return 0;
}
} else {
if (!validate_tlv(mention)) {
return 0;
}
}
return 1;
}
static int validate_non_tlv(struct mention_bech32_block *mention, size_t expected_len) {
if (mention->buffer == NULL || expected_len != mention->buffer_length || mention->pubkey != NULL) {
return 0;
}
return 1;
}
static int validate_tlv(struct mention_bech32_block *mention) {
if (mention->buffer == NULL || mention->pubkey == NULL || mention->kind == -1) {
return 0;
}
if (mention->type == NOSTR_BECH32_NADDR) {
if (mention->identifier == NULL || mention->relays_count > 0) {
return 0;
}
} else if (mention->type == NOSTR_BECH32_NEVENT || mention->type == NOSTR_BECH32_NPROFILE) {
if (mention->event_id == NULL || mention->relays_count > 16) {
return 0;
}
} else if (mention->type == NOSTR_BECH32_NRELAY) {
if (mention->relays_count != 1 || mention->identifier != NULL) {
return 0;
}
}
return 1;
}
```
In this refactored code, each function has a clear focus and responsibility. The `parse_mentions()` function is the main entry point, while the other functions implement specific sub-tasks, such as parsing bech32 data, decoding and validating TLV data elements, and