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