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