File descriptor data example
00001 /* 00002 * build: gcc -o eet_data_file_descriptor eet-data-file_descriptor.c `pkg-config --cflags --libs eet eina` 00003 */ 00004 #include <Eina.h> 00005 #include <Eet.h> 00006 #include <stdio.h> 00007 #include <limits.h> 00008 #include <sys/types.h> 00009 #include <sys/stat.h> 00010 #include <unistd.h> 00011 00012 // complex real-world structures based on elmdentica database 00013 typedef struct 00014 { 00015 const char *screen_name; 00016 const char *name; 00017 const char *message; 00018 unsigned int id; 00019 unsigned int status_id; 00020 unsigned int date; 00021 unsigned int timeline; 00022 } My_Message; 00023 00024 typedef struct 00025 { 00026 const char *dm_to; 00027 const char *message; 00028 } My_Post; 00029 00030 typedef struct 00031 { 00032 unsigned int id; 00033 const char *name; 00034 Eina_List *messages; 00035 My_Post *posts; 00036 int posts_count; 00037 } My_Account; 00038 00039 typedef struct 00040 { 00041 unsigned int version; // it is recommended to use versioned configuration! 00042 Eina_Hash *accounts; 00043 } My_Cache; 00044 00045 // string that represents the entry in eet file, you might like to have 00046 // different profiles or so in the same file, this is possible with 00047 // different strings 00048 static const char MY_CACHE_FILE_ENTRY[] = "cache"; 00049 00050 // keep the descriptor static global, so it can be 00051 // shared by different functions (load/save) of this and only this 00052 // file. 00053 static Eet_Data_Descriptor *_my_cache_descriptor; 00054 static Eet_Data_Descriptor *_my_account_descriptor; 00055 static Eet_Data_Descriptor *_my_message_descriptor; 00056 static Eet_Data_Descriptor *_my_post_descriptor; 00057 00058 // keep file handle alive, so mmap()ed strings are all alive as well 00059 static Eet_File *_my_cache_file = NULL; 00060 static Eet_Dictionary *_my_cache_dict = NULL; 00061 00062 static void 00063 _my_cache_descriptor_init(void) 00064 { 00065 Eet_Data_Descriptor_Class eddc; 00066 00067 // The FILE variant is good for caches and things that are just 00068 // appended, but needs to take care when changing strings and files must 00069 // be kept open so mmap()ed strings will be kept alive. 00070 EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, My_Cache); 00071 _my_cache_descriptor = eet_data_descriptor_file_new(&eddc); 00072 00073 EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, My_Account); 00074 _my_account_descriptor = eet_data_descriptor_file_new(&eddc); 00075 00076 EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, My_Message); 00077 _my_message_descriptor = eet_data_descriptor_file_new(&eddc); 00078 00079 EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, My_Post); 00080 _my_post_descriptor = eet_data_descriptor_file_new(&eddc); 00081 00082 // Describe the members to be saved: 00083 // Use a temporary macro so we don't type a lot, also avoid errors: 00084 00085 #define ADD_BASIC(member, eet_type) \ 00086 EET_DATA_DESCRIPTOR_ADD_BASIC \ 00087 (_my_message_descriptor, My_Message, # member, member, eet_type) 00088 ADD_BASIC(screen_name, EET_T_STRING); 00089 ADD_BASIC(name, EET_T_STRING); 00090 ADD_BASIC(message, EET_T_STRING); 00091 ADD_BASIC(id, EET_T_UINT); 00092 ADD_BASIC(status_id, EET_T_UINT); 00093 ADD_BASIC(date, EET_T_UINT); 00094 ADD_BASIC(timeline, EET_T_UINT); 00095 #undef ADD_BASIC 00096 00097 #define ADD_BASIC(member, eet_type) \ 00098 EET_DATA_DESCRIPTOR_ADD_BASIC \ 00099 (_my_post_descriptor, My_Post, # member, member, eet_type) 00100 ADD_BASIC(dm_to, EET_T_STRING); 00101 ADD_BASIC(message, EET_T_STRING); 00102 #undef ADD_BASIC 00103 00104 #define ADD_BASIC(member, eet_type) \ 00105 EET_DATA_DESCRIPTOR_ADD_BASIC \ 00106 (_my_account_descriptor, My_Account, # member, member, eet_type) 00107 ADD_BASIC(name, EET_T_STRING); 00108 ADD_BASIC(id, EET_T_UINT); 00109 #undef ADD_BASIC 00110 00111 EET_DATA_DESCRIPTOR_ADD_LIST 00112 (_my_account_descriptor, My_Account, "messages", messages, 00113 _my_message_descriptor); 00114 EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY 00115 (_my_account_descriptor, My_Account, "posts", posts, 00116 _my_post_descriptor); 00117 00118 #define ADD_BASIC(member, eet_type) \ 00119 EET_DATA_DESCRIPTOR_ADD_BASIC \ 00120 (_my_cache_descriptor, My_Cache, # member, member, eet_type) 00121 ADD_BASIC(version, EET_T_UINT); 00122 #undef ADD_BASIC 00123 00124 EET_DATA_DESCRIPTOR_ADD_HASH 00125 (_my_cache_descriptor, My_Cache, "accounts", accounts, 00126 _my_account_descriptor); 00127 } /* _my_cache_descriptor_init */ 00128 00129 static void 00130 _my_cache_descriptor_shutdown(void) 00131 { 00132 eet_data_descriptor_free(_my_cache_descriptor); 00133 eet_data_descriptor_free(_my_account_descriptor); 00134 eet_data_descriptor_free(_my_message_descriptor); 00135 eet_data_descriptor_free(_my_post_descriptor); 00136 } /* _my_cache_descriptor_shutdown */ 00137 00138 // need to check if the pointer came from mmaped area in eet_dictionary 00139 // or it was allocated with eina_stringshare_add() 00140 static void 00141 _eet_string_free(const char *str) 00142 { 00143 if (!str) 00144 return; 00145 00146 if ((_my_cache_dict) && (eet_dictionary_string_check(_my_cache_dict, str))) 00147 return; 00148 00149 eina_stringshare_del(str); 00150 } /* _eet_string_free */ 00151 00152 static My_Message * 00153 _my_message_new(const char *message) 00154 { 00155 My_Message *msg = calloc(1, sizeof(My_Message)); 00156 if (!msg) 00157 { 00158 fprintf(stderr, "ERROR: could not calloc My_Message\n"); 00159 return NULL; 00160 } 00161 00162 msg->message = eina_stringshare_add(message); 00163 return msg; 00164 } /* _my_message_new */ 00165 00166 static void 00167 _my_message_free(My_Message *msg) 00168 { 00169 _eet_string_free(msg->screen_name); 00170 _eet_string_free(msg->name); 00171 _eet_string_free(msg->message); 00172 free(msg); 00173 } /* _my_message_free */ 00174 00175 static Eina_Bool 00176 _my_post_add(My_Account *acc, 00177 const char *message) 00178 { 00179 int new_count = acc->posts_count + 1; 00180 My_Post *post = realloc(acc->posts, new_count * sizeof(My_Post)); 00181 if (!post) 00182 { 00183 fprintf(stderr, "ERROR: could add My_Post\n"); 00184 return EINA_FALSE; 00185 } 00186 00187 post[acc->posts_count].message = eina_stringshare_add(message); 00188 post[acc->posts_count].dm_to = NULL; 00189 acc->posts_count = new_count; 00190 acc->posts = post; 00191 return EINA_TRUE; 00192 } /* _my_post_new */ 00193 00194 static void 00195 _my_post_free(My_Post *post) 00196 { 00197 _eet_string_free(post->dm_to); 00198 _eet_string_free(post->message); 00199 } /* _my_post_free */ 00200 00201 static My_Account * 00202 _my_account_new(const char *name) 00203 { 00204 My_Account *acc = calloc(1, sizeof(My_Account)); 00205 if (!acc) 00206 { 00207 fprintf(stderr, "ERROR: could not calloc My_Account\n"); 00208 return NULL; 00209 } 00210 00211 acc->name = eina_stringshare_add(name); 00212 return acc; 00213 } /* _my_account_new */ 00214 00215 static void 00216 _my_account_free(My_Account *acc) 00217 { 00218 My_Message *m; 00219 int i; 00220 00221 _eet_string_free(acc->name); 00222 00223 EINA_LIST_FREE(acc->messages, m) 00224 _my_message_free(m); 00225 00226 for (i = 0; i < acc->posts_count; i++) 00227 _my_post_free(&acc->posts[i]); 00228 free(acc->posts); 00229 00230 free(acc); 00231 } /* _my_account_free */ 00232 00233 static My_Cache * 00234 _my_cache_new(void) 00235 { 00236 My_Cache *my_cache = calloc(1, sizeof(My_Cache)); 00237 if (!my_cache) 00238 { 00239 fprintf(stderr, "ERROR: could not calloc My_Cache\n"); 00240 return NULL; 00241 } 00242 00243 my_cache->accounts = eina_hash_string_small_new(NULL); 00244 00245 my_cache->version = 1; 00246 return my_cache; 00247 } /* _my_cache_new */ 00248 00249 static Eina_Bool 00250 _my_cache_account_free_cb(const Eina_Hash *hash, 00251 const void *key, 00252 void *data, 00253 void *fdata) 00254 { 00255 _my_account_free(data); 00256 return EINA_TRUE; 00257 } 00258 00259 static void 00260 _my_cache_free(My_Cache *my_cache) 00261 { 00262 My_Account *acc; 00263 eina_hash_foreach(my_cache->accounts, _my_cache_account_free_cb, NULL); 00264 eina_hash_free(my_cache->accounts); 00265 free(my_cache); 00266 } /* _my_cache_free */ 00267 00268 static My_Account * 00269 _my_cache_account_find(My_Cache *my_cache, 00270 const char *name) 00271 { 00272 return eina_hash_find(my_cache->accounts, name); 00273 } /* _my_cache_account_find */ 00274 00275 static My_Cache * 00276 _my_cache_load(const char *filename) 00277 { 00278 My_Cache *my_cache; 00279 Eet_File *ef = eet_open(filename, EET_FILE_MODE_READ); 00280 if (!ef) 00281 { 00282 fprintf(stderr, "ERROR: could not open '%s' for read\n", filename); 00283 return NULL; 00284 } 00285 00286 my_cache = eet_data_read(ef, _my_cache_descriptor, MY_CACHE_FILE_ENTRY); 00287 if (!my_cache) 00288 { 00289 eet_close(ef); 00290 return NULL; 00291 } 00292 00293 if (my_cache->version < 1) 00294 { 00295 fprintf(stderr, 00296 "WARNING: version %#x was too old, upgrading it to %#x\n", 00297 my_cache->version, 1); 00298 00299 my_cache->version = 1; 00300 } 00301 00302 if (_my_cache_file) 00303 eet_close(_my_cache_file); 00304 00305 _my_cache_file = ef; 00306 _my_cache_dict = eet_dictionary_get(ef); 00307 00308 return my_cache; 00309 } /* _my_cache_load */ 00310 00311 static Eina_Bool 00312 _my_cache_save(const My_Cache *my_cache, 00313 const char *filename) 00314 { 00315 char tmp[PATH_MAX]; 00316 Eet_File *ef; 00317 Eina_Bool ret; 00318 unsigned int i, len; 00319 struct stat st; 00320 00321 len = eina_strlcpy(tmp, filename, sizeof(tmp)); 00322 if (len + 12 >= (int)sizeof(tmp)) 00323 { 00324 fprintf(stderr, "ERROR: file name is too big: %s\n", filename); 00325 return EINA_FALSE; 00326 } 00327 00328 i = 0; 00329 do 00330 { 00331 snprintf(tmp + len, 12, ".%u", i); 00332 i++; 00333 } 00334 while (stat(tmp, &st) == 0); 00335 00336 ef = eet_open(tmp, EET_FILE_MODE_WRITE); 00337 if (!ef) 00338 { 00339 fprintf(stderr, "ERROR: could not open '%s' for write\n", tmp); 00340 return EINA_FALSE; 00341 } 00342 00343 ret = eet_data_write 00344 (ef, _my_cache_descriptor, MY_CACHE_FILE_ENTRY, my_cache, EINA_TRUE); 00345 00346 // VERY IMPORTANT NOTE: 00347 // after eet_close(), all strings mmaped from file will be GONE, invalid! 00348 // you'll need to free the old cache and open the new one. 00349 // For cache this is okay, as you should be saving not so often or just 00350 // at end. 00351 // 00352 // This is a trade off, you save memory by using mmap()ed strings, but 00353 // you have to care about this. 00354 eet_close(ef); 00355 00356 if (ret) 00357 { 00358 unlink(filename); 00359 rename(tmp, filename); 00360 } 00361 00362 return ret; 00363 } /* _my_cache_save */ 00364 00365 int 00366 main(int argc, 00367 char *argv[]) 00368 { 00369 My_Cache *my_cache; 00370 const Eina_List *l_acc; 00371 Eina_Iterator *it; 00372 My_Account *acc; 00373 int ret = 0; 00374 00375 if (argc < 3) 00376 { 00377 fprintf(stderr, 00378 "Usage:\n\t%s <input> <output> [action] [action-params]\n\n" 00379 "Where actions and their parameters:\n" 00380 "\tacc <name>\n" 00381 "\tpost <account-name> <message>\n" 00382 "\tmessage <account-name> <message>\n" 00383 "\n", 00384 argv[0]); 00385 return -1; 00386 } 00387 00388 eina_init(); 00389 eet_init(); 00390 _my_cache_descriptor_init(); 00391 00392 my_cache = _my_cache_load(argv[1]); 00393 if (!my_cache) 00394 { 00395 printf("creating new cache.\n"); 00396 my_cache = _my_cache_new(); 00397 if (!my_cache) 00398 { 00399 ret = -2; 00400 goto end; 00401 } 00402 } 00403 00404 if (argc > 3) 00405 { 00406 if (strcmp(argv[3], "acc") == 0) 00407 { 00408 if (argc == 5) 00409 { 00410 My_Account *acc = _my_cache_account_find(my_cache, argv[4]); 00411 if (!acc) 00412 { 00413 acc = _my_account_new(argv[4]); 00414 eina_hash_direct_add(my_cache->accounts, acc->name, acc); 00415 } 00416 else 00417 fprintf(stderr, "ERROR: account '%s' already exists.\n", 00418 argv[4]); 00419 } 00420 else 00421 fprintf(stderr, 00422 "ERROR: wrong number of parameters (%d).\n", 00423 argc); 00424 } 00425 else if (strcmp(argv[3], "post") == 0) 00426 { 00427 if (argc == 6) 00428 { 00429 My_Account *acc = _my_cache_account_find(my_cache, argv[4]); 00430 if (acc) 00431 { 00432 _my_post_add(acc, argv[5]); 00433 } 00434 else 00435 fprintf(stderr, "ERROR: unknown account: '%s'\n", argv[4]); 00436 } 00437 else 00438 fprintf(stderr, 00439 "ERROR: wrong number of parameters (%d).\n", 00440 argc); 00441 } 00442 else if (strcmp(argv[3], "message") == 0) 00443 { 00444 if (argc == 6) 00445 { 00446 My_Account *acc = _my_cache_account_find(my_cache, argv[4]); 00447 if (acc) 00448 { 00449 My_Message *msg = _my_message_new(argv[5]); 00450 acc->messages = eina_list_append(acc->messages, msg); 00451 } 00452 else 00453 fprintf(stderr, "ERROR: unknown account: '%s'\n", argv[4]); 00454 } 00455 else 00456 fprintf(stderr, 00457 "ERROR: wrong number of parameters (%d).\n", 00458 argc); 00459 } 00460 else 00461 fprintf(stderr, "ERROR: unknown action '%s'\n", argv[2]); 00462 } 00463 00464 printf("My_Cache:\n" 00465 "\tversion.: %#x\n" 00466 "\taccounts: %u\n", 00467 my_cache->version, 00468 eina_hash_population(my_cache->accounts)); 00469 it = eina_hash_iterator_data_new(my_cache->accounts); 00470 EINA_ITERATOR_FOREACH(it, acc) 00471 { 00472 const My_Post *post; 00473 00474 printf("\t > %-#8x '%.20s' stats: m=%u, p=%u\n", 00475 acc->id, acc->name ? acc->name : "", 00476 eina_list_count(acc->messages), 00477 acc->posts_count); 00478 00479 if (eina_list_count(acc->messages)) 00480 { 00481 const Eina_List *l; 00482 const My_Message *msg; 00483 printf("\t |messages:\n"); 00484 00485 EINA_LIST_FOREACH(acc->messages, l, msg) 00486 { 00487 printf("\t | %-8x '%s' [%s]: '%.20s'\n", 00488 msg->id, 00489 msg->name ? msg->name : "", 00490 msg->screen_name ? msg->screen_name : "", 00491 msg->message ? msg->message : ""); 00492 } 00493 } 00494 00495 if (acc->posts_count) 00496 { 00497 const My_Post *post; 00498 int i; 00499 printf("\t |posts:\n"); 00500 00501 for (i = 0; i < acc->posts_count; i++) 00502 { 00503 post = &acc->posts[i]; 00504 if (post->dm_to) 00505 printf("\t | @%s: '%.20s'\n", post->dm_to, post->message); 00506 else 00507 printf("\t | '%.20s'\n", post->message); 00508 } 00509 } 00510 00511 printf("\n"); 00512 } 00513 eina_iterator_free(it); 00514 00515 if (!_my_cache_save(my_cache, argv[2])) 00516 ret = -3; 00517 00518 _my_cache_free(my_cache); 00519 00520 end: 00521 if (_my_cache_file) 00522 eet_close(_my_cache_file); 00523 _my_cache_descriptor_shutdown(); 00524 eet_shutdown(); 00525 eina_shutdown(); 00526 00527 return ret; 00528 } /* main */ 00529