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