|
| 1 | + |
| 2 | +#include "nostrdb.h" |
| 3 | +#include "binmoji.h" |
| 4 | + |
| 5 | +// these must be byte-aligned, they are directly accessing the serialized data |
| 6 | +// representation |
| 7 | +#pragma pack(push, 1) |
| 8 | + |
| 9 | +// 16 bytes |
| 10 | +struct ndb_note_meta_entry { |
| 11 | + // 4 byte entry header |
| 12 | + uint16_t type; |
| 13 | + uint16_t flags; |
| 14 | + |
| 15 | + // additional 4 bytes of aux storage for payloads that are >8 bytes |
| 16 | + // |
| 17 | + // for reactions types, this is used for counts |
| 18 | + // normally this would have been padding but we make use of it |
| 19 | + // in our manually packed structure |
| 20 | + // |
| 21 | + uint32_t aux; |
| 22 | + |
| 23 | + // 8 byte metadata payload |
| 24 | + union { |
| 25 | + struct { |
| 26 | + uint32_t offset; |
| 27 | + uint32_t padding; |
| 28 | + } offset; |
| 29 | + |
| 30 | + // the reaction binmoji[1] for reaction, count is stored in aux |
| 31 | + union ndb_reaction_str reaction_str; |
| 32 | + } payload; |
| 33 | +}; |
| 34 | +STATIC_ASSERT(sizeof(struct ndb_note_meta_entry) == 16, note_meta_entry_should_be_16_bytes); |
| 35 | + |
| 36 | +/* newtype wrapper around the header entry */ |
| 37 | +struct ndb_note_meta { |
| 38 | + // 4 bytes |
| 39 | + uint8_t version; |
| 40 | + uint8_t padding; |
| 41 | + uint16_t count; |
| 42 | + |
| 43 | + // 4 bytes |
| 44 | + uint32_t data_table_size; |
| 45 | + |
| 46 | + // 8 bytes |
| 47 | + uint64_t flags; |
| 48 | +}; |
| 49 | +STATIC_ASSERT(sizeof(struct ndb_note_meta) == 16, note_meta_entry_should_be_16_bytes); |
| 50 | + |
| 51 | +#pragma pack(pop) |
| 52 | + |
| 53 | +int ndb_reaction_str_is_emoji(union ndb_reaction_str str) |
| 54 | +{ |
| 55 | + return binmoji_get_user_flag(str.binmoji) == 0; |
| 56 | +} |
| 57 | + |
| 58 | +uint16_t ndb_note_meta_entries_count(struct ndb_note_meta *meta) |
| 59 | +{ |
| 60 | + return meta->count; |
| 61 | +} |
| 62 | + |
| 63 | +int ndb_reaction_set_emoji(union ndb_reaction_str *str, const char *emoji) |
| 64 | +{ |
| 65 | + struct binmoji binmoji; |
| 66 | + /* TODO: parse failures? */ |
| 67 | + binmoji_parse(emoji, &binmoji); |
| 68 | + str->binmoji = binmoji_encode(&binmoji); |
| 69 | + return 1; |
| 70 | +} |
| 71 | + |
| 72 | +int ndb_reaction_set_str(union ndb_reaction_str *reaction, const char *str) |
| 73 | +{ |
| 74 | + int i; |
| 75 | + char c; |
| 76 | + |
| 77 | + /* this is like memset'ing the packed string to all 0s as well */ |
| 78 | + reaction->binmoji = 0; |
| 79 | + |
| 80 | + /* set the binmoji user flag so we can catch corrupt binmojis */ |
| 81 | + /* this is in the LSB so it will only touch reaction->packed.flag */ |
| 82 | + reaction->binmoji = binmoji_set_user_flag(reaction->binmoji, 1); |
| 83 | + assert(reaction->packed.flag != 0); |
| 84 | + |
| 85 | + for (i = 0; i < 7; i++) { |
| 86 | + c = str[i]; |
| 87 | + /* string is too big */ |
| 88 | + if (i == 6 && c != '\0') |
| 89 | + return 0; |
| 90 | + reaction->packed.str[i] = c; |
| 91 | + if (c == '\0') |
| 92 | + return 1; |
| 93 | + } |
| 94 | + |
| 95 | + return 0; |
| 96 | +} |
| 97 | + |
| 98 | +static void ndb_note_meta_header_init(struct ndb_note_meta *meta) |
| 99 | +{ |
| 100 | + meta->version = 1; |
| 101 | + meta->flags = 0; |
| 102 | + meta->count = 0; |
| 103 | + meta->data_table_size = 0; |
| 104 | +} |
| 105 | + |
| 106 | +static inline size_t ndb_note_meta_entries_size(struct ndb_note_meta *meta) |
| 107 | +{ |
| 108 | + return (sizeof(struct ndb_note_meta_entry) * meta->count); |
| 109 | +} |
| 110 | + |
| 111 | +void *ndb_note_meta_data_table(struct ndb_note_meta *meta, size_t *size) |
| 112 | +{ |
| 113 | + return meta + ndb_note_meta_entries_size(meta); |
| 114 | +} |
| 115 | + |
| 116 | +size_t ndb_note_meta_total_size(struct ndb_note_meta *header) |
| 117 | +{ |
| 118 | + size_t total_size = header->data_table_size + ndb_note_meta_entries_size(header); |
| 119 | + assert((total_size % 8) == 0); |
| 120 | + return total_size; |
| 121 | +} |
| 122 | + |
| 123 | +struct ndb_note_meta_entry *ndb_note_meta_add_entry(struct ndb_note_meta_builder *builder) |
| 124 | +{ |
| 125 | + struct ndb_note_meta *header = (struct ndb_note_meta *)builder->cursor.start; |
| 126 | + struct ndb_note_meta_entry *entry = NULL; |
| 127 | + |
| 128 | + assert(builder->cursor.p != builder->cursor.start); |
| 129 | + |
| 130 | + if (!(entry = cursor_malloc(&builder->cursor, sizeof(*entry)))) |
| 131 | + return NULL; |
| 132 | + |
| 133 | + /* increase count entry count */ |
| 134 | + header->count++; |
| 135 | + |
| 136 | + return entry; |
| 137 | +} |
| 138 | + |
| 139 | +int ndb_note_meta_builder_init(struct ndb_note_meta_builder *builder, unsigned char *buf, size_t bufsize) |
| 140 | +{ |
| 141 | + make_cursor(buf, buf + bufsize, &builder->cursor); |
| 142 | + |
| 143 | + /* allocate some space for the header */ |
| 144 | + if (!cursor_malloc(&builder->cursor, sizeof(struct ndb_note_meta))) |
| 145 | + return 0; |
| 146 | + |
| 147 | + ndb_note_meta_header_init((struct ndb_note_meta*)builder->cursor.start); |
| 148 | + |
| 149 | + return 1; |
| 150 | +} |
| 151 | + |
| 152 | +/* note flags are stored in the header entry */ |
| 153 | +uint32_t ndb_note_meta_flags(struct ndb_note_meta *meta) |
| 154 | +{ |
| 155 | + return meta->flags; |
| 156 | +} |
| 157 | + |
| 158 | +/* note flags are stored in the header entry */ |
| 159 | +void ndb_note_meta_set_flags(struct ndb_note_meta *meta, uint32_t flags) |
| 160 | +{ |
| 161 | + meta->flags = flags; |
| 162 | +} |
| 163 | + |
| 164 | +static int compare_entries(const void *a, const void *b) |
| 165 | +{ |
| 166 | + struct ndb_note_meta_entry *entry_a, *entry_b; |
| 167 | + uint64_t binmoji_a, binmoji_b; |
| 168 | + int res; |
| 169 | + |
| 170 | + entry_a = (struct ndb_note_meta_entry *)a; |
| 171 | + entry_b = (struct ndb_note_meta_entry *)b; |
| 172 | + |
| 173 | + res = entry_a->type - entry_b->type; |
| 174 | + |
| 175 | + if (res == 0 && entry_a->type == NDB_NOTE_META_REACTION) { |
| 176 | + /* we sort by reaction string for stability */ |
| 177 | + binmoji_a = entry_a->payload.reaction_str.binmoji; |
| 178 | + binmoji_b = entry_b->payload.reaction_str.binmoji; |
| 179 | + |
| 180 | + if (binmoji_a < binmoji_b) { |
| 181 | + return -1; |
| 182 | + } else if (binmoji_a > binmoji_b) { |
| 183 | + return 1; |
| 184 | + } else { |
| 185 | + return 0; |
| 186 | + } |
| 187 | + } else { |
| 188 | + return res; |
| 189 | + } |
| 190 | +} |
| 191 | + |
| 192 | +struct ndb_note_meta_entry *ndb_note_meta_entries(struct ndb_note_meta *meta) |
| 193 | +{ |
| 194 | + /* entries start at the end of the header record */ |
| 195 | + return (struct ndb_note_meta_entry *)((uint8_t*)meta + sizeof(*meta)); |
| 196 | +} |
| 197 | + |
| 198 | +void ndb_note_meta_build(struct ndb_note_meta_builder *builder, struct ndb_note_meta **meta) |
| 199 | +{ |
| 200 | + /* sort entries */ |
| 201 | + struct ndb_note_meta_entry *entries; |
| 202 | + struct ndb_note_meta *header = (struct ndb_note_meta*)builder->cursor.start; |
| 203 | + |
| 204 | + /* not initialized */ |
| 205 | + assert(builder->cursor.start != builder->cursor.p); |
| 206 | + |
| 207 | + if (header->count > 0) { |
| 208 | + entries = ndb_note_meta_entries(header); |
| 209 | + |
| 210 | + /* ensure entries are always sorted so bsearch is possible for large metadata |
| 211 | + * entries. probably won't need that for awhile though */ |
| 212 | + qsort(entries, header->count, sizeof(struct ndb_note_meta_entry), compare_entries); |
| 213 | + } |
| 214 | + |
| 215 | + *meta = header; |
| 216 | + return; |
| 217 | +} |
| 218 | + |
| 219 | +void ndb_note_meta_reaction_set(struct ndb_note_meta_entry *entry, uint32_t count, union ndb_reaction_str str) |
| 220 | +{ |
| 221 | + entry->type = NDB_NOTE_META_REACTION; |
| 222 | + entry->flags = 0; |
| 223 | + entry->aux = count; |
| 224 | + entry->payload.reaction_str = str; |
| 225 | +} |
| 226 | + |
| 227 | +uint32_t ndb_note_meta_reaction_count(struct ndb_note_meta_entry *entry) |
| 228 | +{ |
| 229 | + return entry->aux; |
| 230 | +} |
| 231 | + |
| 232 | +void ndb_note_meta_reaction_set_count(struct ndb_note_meta_entry *entry, uint32_t count) |
| 233 | +{ |
| 234 | + entry->aux = count; |
| 235 | +} |
| 236 | + |
| 237 | +union ndb_reaction_str ndb_note_meta_reaction_str(struct ndb_note_meta_entry *entry) |
| 238 | +{ |
| 239 | + return entry->payload.reaction_str; |
| 240 | +} |
0 commit comments