00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <microfeed-common/microfeeditem.h>
00021 #include <microfeed-common/microfeedmisc.h>
00022
00023 #include <string.h>
00024 #include <stdint.h>
00025 #include <ctype.h>
00026 #include <inttypes.h>
00027 #include <time.h>
00028 #include <stdio.h>
00029
00030 typedef uint16_t property_size_t;
00031
00032 struct _MicrofeedItem {
00033 char* uid;
00034 time_t timestamp;
00035 MicrofeedItemStatus status;
00036 size_t* properties;
00037 unsigned int properties_length;
00038 unsigned int properties_reserved;
00039 char* buffer;
00040 size_t buffer_size;
00041 MicrofeedItemIterator* iterators;
00042 };
00043
00044 struct _MicrofeedItemIterator {
00045 MicrofeedItemIterator* next;
00046 MicrofeedItemIterator* previous;
00047
00048 MicrofeedItem* item;
00049 unsigned int index;
00050 };
00051
00052 static int get_index(const MicrofeedItem* item, const char* key, size_t length, unsigned int* index);
00053
00054 MicrofeedItem* microfeed_item_new(const char* uid, time_t timestamp) {
00055 MicrofeedItem* item;
00056
00057 item = microfeed_memory_allocate(MicrofeedItem);
00058 if (uid) {
00059 item->uid = strdup(uid);
00060 }
00061 item->timestamp = timestamp;
00062
00063 return item;
00064 }
00065
00066 MicrofeedItem* microfeed_item_new_temporary(void) {
00067 MicrofeedItem* item;
00068 struct timespec now;
00069 char buffer[1024];
00070
00071 if (!(clock_gettime(CLOCK_REALTIME, &now))) {
00072 snprintf(buffer, 1024, "%ld.%ld", (long)now.tv_sec, now.tv_nsec);
00073 } else {
00074 buffer[0] = 0;
00075 }
00076 item = microfeed_item_new(buffer, now.tv_sec);
00077
00078 return item;
00079 }
00080
00081 MicrofeedItem* microfeed_item_new_with_status(const char* uid, time_t timestamp, MicrofeedItemStatus status) {
00082 MicrofeedItem* item;
00083
00084 item = microfeed_item_new(uid, timestamp);
00085 item->status = status | MICROFEED_ITEM_STATUS_SET;
00086
00087 return item;
00088 }
00089
00090 void microfeed_item_free(MicrofeedItem* item) {
00091 if (item) {
00092 free(item->uid);
00093 free(item->buffer);
00094 item->uid = NULL;
00095 item->properties = NULL;
00096 item->buffer = NULL;
00097 microfeed_memory_free(item);
00098 }
00099 }
00100
00101 MicrofeedItem* microfeed_item_duplicate(MicrofeedItem* item) {
00102 MicrofeedItem* duplicate;
00103
00104 duplicate = microfeed_memory_allocate(MicrofeedItem);
00105 if (item->uid) {
00106 duplicate->uid = strdup(item->uid);
00107 } else {
00108 duplicate->uid = NULL;
00109 }
00110 duplicate->properties = (size_t*)malloc(item->properties_length * sizeof(size_t));
00111 memcpy(duplicate->properties, item->properties, item->properties_length * sizeof(size_t));
00112 duplicate->properties_length = duplicate->properties_reserved = item->properties_length;
00113 duplicate->buffer = (char*)malloc(item->buffer_size);
00114 memcpy(duplicate->buffer, item->buffer, item->buffer_size);
00115 duplicate->buffer_size = item->buffer_size;
00116
00117 return duplicate;
00118 }
00119
00120 int microfeed_item_demarshal_properties(MicrofeedItem* item, const void* data, size_t size) {
00121 size_t offset;
00122
00123 if (item->buffer) {
00124 free(item->buffer);
00125 }
00126 item->buffer = (char*)malloc(size);
00127 item->buffer_size = size;
00128 memcpy(item->buffer, data, size);
00129
00130 for (offset = 0; offset < size; offset += *((property_size_t*)(item->buffer + offset))) {
00131 if (item->properties_length == item->properties_reserved) {
00132 item->properties_reserved += 10 * sizeof(size_t);
00133 item->properties = (size_t*)realloc(item->properties, item->properties_reserved);
00134 }
00135 item->properties[item->properties_length++] = offset;
00136 }
00137
00138 if (offset > size) {
00139 free(item->buffer);
00140 item->buffer = NULL;
00141 item->buffer_size = 0;
00142 free(item->properties);
00143 item->properties = NULL;
00144 item->properties_length = 0;
00145 item->properties_reserved = 0;
00146
00147 return 0;
00148 }
00149
00150 return 1;
00151 }
00152
00153 const char* microfeed_item_get_uid(MicrofeedItem* item) {
00154
00155 return item->uid;
00156 }
00157
00158 time_t microfeed_item_get_timestamp(MicrofeedItem* item) {
00159
00160 return item->timestamp;
00161 }
00162
00163 void microfeed_item_set_timestamp(MicrofeedItem* item, time_t timestamp) {
00164 item->timestamp = timestamp;
00165 }
00166
00167 MicrofeedItemStatus microfeed_item_get_status(MicrofeedItem* item) {
00168
00169 return item->status;
00170 }
00171
00172 const char* microfeed_item_get_property(MicrofeedItem* item, const char* key) {
00173 size_t length;
00174 const char* value;
00175 unsigned int index;
00176
00177 length = strlen(key);
00178 if (!item->properties) {
00179 value = NULL;
00180 } else if (get_index(item, key, length, &index)) {
00181 value = item->buffer + item->properties[index] + sizeof(property_size_t) + length + 1;
00182 } else {
00183 value = NULL;
00184 }
00185
00186 return value;
00187 }
00188
00189 MicrofeedItemIterator* microfeed_item_iterate_properties(MicrofeedItem* item, const char* start_key) {
00190 MicrofeedItemIterator* iterator;
00191 unsigned int index;
00192
00193 iterator = microfeed_memory_allocate(MicrofeedItemIterator);
00194 iterator->item = item;
00195
00196 if (start_key) {
00197 if (get_index(item, start_key, strlen(start_key), &index)) {
00198 iterator->index = index;
00199 } else {
00200 iterator->index = item->properties_length;
00201 }
00202 } else {
00203 iterator->index = 0;
00204 }
00205
00206 if (item->iterators) {
00207 item->iterators->previous = iterator;
00208 iterator->next = item->iterators;
00209 } else {
00210 iterator->next = NULL;
00211 }
00212 iterator->previous = NULL;
00213 item->iterators = iterator;
00214
00215
00216 return iterator;
00217 }
00218
00219 void microfeed_item_marshal_properties(MicrofeedItem* item, const void** data_pointer, size_t* size_pointer) {
00220 *data_pointer = item->buffer;
00221 *size_pointer = item->buffer_size;
00222 }
00223
00224 void microfeed_item_set_property(MicrofeedItem* item, const char* key, const char* value) {
00225 if (value) {
00226 microfeed_item_set_property_full(item, key, strlen(key), value, strlen(value));
00227 } else {
00228
00229 }
00230 }
00231
00232 void microfeed_item_set_property_with_length(MicrofeedItem* item, const char* key, const char* value, size_t value_length) {
00233 microfeed_item_set_property_full(item, key, strlen(key), value, value_length);
00234 }
00235
00236 void microfeed_item_set_property_full(MicrofeedItem* item, const char* key, size_t key_length, const char* value, size_t value_length) {
00237 unsigned int index;
00238 size_t length;
00239 size_t old_length;
00240 size_t buffer_size;
00241 unsigned int i;
00242
00243 length = sizeof(uint16_t) + key_length + value_length + 2;
00244 length += length % sizeof(property_size_t);
00245
00246 if (get_index(item, key, key_length, &index)) {
00247 old_length = *((property_size_t*)(item->buffer + item->properties[index]));
00248 if (length != old_length) {
00249 buffer_size = item->buffer_size;
00250 if (old_length < length) {
00251 item->buffer_size += length - old_length;
00252 item->buffer = (char*)realloc(item->buffer, item->buffer_size);
00253 }
00254 if (index < item->properties_length - 1) {
00255 memmove(item->buffer + item->properties[index + 1] + length - old_length,
00256 item->buffer + item->properties[index + 1],
00257 buffer_size - item->properties[index + 1]);
00258 for (i = index + 1; i < item->properties_length; i++) {
00259 item->properties[i] += length - old_length;
00260 }
00261 }
00262 if (old_length > length) {
00263 item->buffer_size += length - old_length;
00264 item->buffer = (char*)realloc(item->buffer, item->buffer_size);
00265 }
00266 }
00267 *((property_size_t*)(item->buffer + item->properties[index])) = length;
00268 memcpy(item->buffer + item->properties[index] + sizeof(property_size_t) + key_length + 1, value, value_length);
00269 (item->buffer + item->properties[index] + sizeof(property_size_t) + key_length + 1)[value_length] = 0;
00270 } else {
00271 if (item->properties_length == item->properties_reserved) {
00272 item->properties_reserved += 10 * sizeof(char*);
00273 item->properties = (size_t*)realloc(item->properties, item->properties_reserved);
00274 }
00275
00276 buffer_size = item->buffer_size;
00277 item->buffer_size += length;
00278 item->buffer = (char*)realloc(item->buffer, item->buffer_size);
00279
00280 if (index < item->properties_length) {
00281 memmove(item->properties + index + 1, item->properties + index,
00282 (item->properties_length - index + 1) * sizeof(char*));
00283 memmove(item->buffer + item->properties[index + 1] + length,
00284 item->buffer + item->properties[index + 1],
00285 buffer_size - item->properties[index + 1]);
00286 for (i = index + 1; i <= item->properties_length; i++) {
00287 item->properties[i] += length;
00288 }
00289 } else {
00290 item->properties[index] = item->buffer_size - length;
00291 }
00292 *((property_size_t*)(item->buffer + item->properties[index])) = length;
00293 memcpy(item->buffer + item->properties[index] + sizeof(property_size_t), key, key_length);
00294 (item->buffer + item->properties[index] + sizeof(property_size_t))[key_length] = 0;
00295 memcpy(item->buffer + item->properties[index] + sizeof(property_size_t) + key_length + 1, value, value_length);
00296 (item->buffer + item->properties[index] + sizeof(property_size_t) + key_length + 1)[value_length] = 0;
00297 item->properties_length++;
00298 }
00299 }
00300
00301 char* microfeed_item_get_properties_as_string(MicrofeedItem* item) {
00302 char* string;
00303 char* pointer;
00304 int i;
00305 property_size_t property_size;
00306 size_t length;
00307
00308 string = pointer = (char*)microfeed_memory_allocate_bytes(item->buffer_size - item->properties_length * (sizeof(property_size_t) - 1) + 1);
00309 for (i = 0; i < item->properties_length; i++) {
00310 property_size = *((property_size_t*)(item->buffer + item->properties[i]));
00311 length = strlen(item->buffer + item->properties[i] + sizeof(property_size_t));
00312 memcpy(pointer, item->buffer + item->properties[i] + sizeof(property_size_t), length);
00313 pointer += length;
00314 *pointer++ = ':';
00315 *pointer++ = ' ';
00316 memcpy(pointer, item->buffer + item->properties[i] + sizeof(property_size_t) + length + 1, property_size - length - 1);
00317 pointer += property_size - length - 2 * sizeof(property_size_t) - 1;
00318 while (*pointer) {
00319 pointer++;
00320 }
00321 *pointer++ = '\n';
00322 }
00323 *pointer = 0;
00324
00325 return string;
00326 }
00327
00328 int microfeed_item_set_properties_from_string(MicrofeedItem* item, const char* string) {
00329 const char* key;
00330 size_t key_length;
00331 const char* value;
00332 size_t value_length;
00333 const char* end;
00334
00335 while (*string) {
00336 key = string;
00337 while (*key == ' ' || *key == '\t') {
00338 key++;
00339 }
00340 value = key;
00341 while (*value != 0 && *value != ':' && *value != ' ' && *value != '\t') {
00342 value++;
00343 }
00344 key_length = value - key;
00345 while (*value == ' ' || *value == '\t') {
00346 value++;
00347 }
00348 if (*value != ':') {
00349
00350 return (*value == 0 ? 1 : 0);
00351 }
00352 value++;
00353 while (*value == ' ' || *value == '\t') {
00354 value++;
00355 }
00356 end = value;
00357 while (*end != 0 && *end != '\n' && *end != '\r') {
00358 end++;
00359 }
00360 if (*end) {
00361 string = end + 1;
00362 } else {
00363 string = end;
00364 }
00365 end--;
00366 while ((*end == ' ' || *end == '\t') && end > value) {
00367 end--;
00368 }
00369 value_length = end - value + 1;
00370
00371 microfeed_item_set_property_full(item, key, key_length, value, value_length);
00372 }
00373
00374 return 1;
00375 }
00376
00377
00378 MicrofeedItemPermission microfeed_item_permission_from_string(const char* string) {
00379 MicrofeedItemPermission item_permission = MICROFEED_ITEM_PERMISSION_NONE;
00380
00381 while (*string == ':') {
00382 string++;
00383 if (!strncmp(string, "modify", 6) && (string[6] == 0 || string[6] == ':')) {
00384 item_permission |= MICROFEED_ITEM_PERMISSION_MODIFY;
00385 string += 6;
00386 } else if (!strncmp(string, "remove", 6) && (string[6] == 0 || string[6] == ':')) {
00387 item_permission |= MICROFEED_ITEM_PERMISSION_REMOVE;
00388 string += 6;
00389 } else if (!strncmp(string, "reply", 5) && (string[5] == 0 || string[5] == ':')) {
00390 item_permission |= MICROFEED_ITEM_PERMISSION_REPLY;
00391 string += 5;
00392 }
00393 }
00394
00395 return item_permission;
00396 }
00397
00398
00399 char* microfeed_item_permission_to_string(MicrofeedItemPermission item_permission) {
00400 char buffer[1024];
00401 char* string = buffer;
00402
00403 if (item_permission & MICROFEED_ITEM_PERMISSION_MODIFY) {
00404 memcpy(string, ":modify", 7);
00405 string += 7;
00406 }
00407 if (item_permission & MICROFEED_ITEM_PERMISSION_REMOVE) {
00408 memcpy(string, ":remove", 7);
00409 string += 7;
00410 }
00411 if (item_permission & MICROFEED_ITEM_PERMISSION_REPLY) {
00412 memcpy(string, ":reply", 6);
00413 string += 6;
00414 }
00415 *string = 0;
00416
00417 return strdup(buffer);
00418 }
00419
00420 void microfeed_item_iterator_free(MicrofeedItemIterator* iterator) {
00421 if (iterator->next) {
00422 iterator->next->previous = iterator->previous;
00423 }
00424 if (iterator->previous) {
00425 iterator->previous->next = iterator->next;
00426 } else if (iterator->item) {
00427 iterator->item->iterators = iterator->next;
00428 }
00429
00430 iterator->item = NULL;
00431 microfeed_memory_free(iterator);
00432 }
00433
00434 int microfeed_item_iterator_get(MicrofeedItemIterator* iterator, const char** key, const char** value) {
00435 int success = 0;
00436 size_t length;
00437
00438 if (iterator->item && iterator->index < iterator->item->properties_length) {
00439 *key = iterator->item->buffer + iterator->item->properties[iterator->index] + sizeof(property_size_t);
00440 length = strlen(iterator->item->buffer + iterator->item->properties[iterator->index] + sizeof(property_size_t)) + 1;
00441 *value = iterator->item->buffer + iterator->item->properties[iterator->index] + sizeof(property_size_t) + length;
00442 success = 1;
00443 }
00444
00445 return success;
00446 }
00447
00448 void microfeed_item_iterator_next(MicrofeedItemIterator* iterator) {
00449 if (iterator->item && iterator->index < iterator->item->properties_length) {
00450 iterator->index++;
00451 }
00452 }
00453
00454 static int get_index(const MicrofeedItem* item, const char* key, size_t length, unsigned int* index) {
00455 int retval = 0;
00456 int result;
00457 unsigned int i, min, max;
00458
00459 if (item->properties_length == 0) {
00460 *index = 0;
00461 } else if ((result = memcmp(key, item->buffer + item->properties[0] + sizeof(property_size_t), length)) == 0 && (item->buffer + item->properties[0] + sizeof(property_size_t))[length] == 0) {
00462 *index = 0;
00463 retval = 1;
00464 } else if (result < 0) {
00465 *index = 0;
00466 } else if (item->properties_length == 1) {
00467 *index = 1;
00468 } else if ((result = memcmp(key, item->buffer + item->properties[item->properties_length - 1] + sizeof(property_size_t), length)) == 0) {
00469 *index = item->properties_length - 1;
00470 retval = 1;
00471 } else if (result > 0) {
00472 *index = item->properties_length;
00473 } else if (item->properties_length == 2) {
00474 *index = item->properties_length - 1;
00475 } else {
00476 min = i = 0;
00477 max = item->properties_length - 1;
00478
00479 while (min <= max) {
00480 i = (min + max) / 2;
00481 if ((result = memcmp(key, item->buffer + item->properties[i] + sizeof(property_size_t), length)) == 0 && (item->buffer + item->properties[i] + sizeof(property_size_t))[length] == 0) {
00482 retval = 1;
00483 break;
00484 } else if (result < 0) {
00485 max = i - 1;
00486 } else {
00487 i++;
00488 min = i;
00489 }
00490 }
00491 *index = i;
00492 }
00493
00494 return retval;
00495 }
00496