c0a04eecd05ef6c43a2f4110ab5814d6c9c58a13
[platform/upstream/gstreamer.git] / gst / indexers / gstfileindex.c
1 /* GStreamer
2  * Copyright (C) 2003 Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include <gst/gst_private.h>
21 #include <gst/gstversion.h>
22 #include <gst/gstplugin.h>
23 #include <gst/gstindex.h>
24
25 #include <unistd.h>
26 #include <sys/mman.h>
27 #include <sys/stat.h>
28 #include <errno.h>
29 #include <fcntl.h>
30
31 #define GST_TYPE_FILE_INDEX             \
32   (gst_file_index_get_type ())
33 #define GST_FILE_INDEX(obj)             \
34   (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_FILE_INDEX, GstFileIndex))
35 #define GST_FILE_INDEX_CLASS(klass)     \
36   (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_FILE_INDEX, GstFileIndexClass))
37 #define GST_IS_FILE_INDEX(obj)          \
38   (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_FILE_INDEX))
39 #define GST_IS_FILE_INDEX_CLASS(obj)    \
40   (GST_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_FILE_INDEX))
41         
42 /*
43  * Object model:
44  *
45  * We build an index to each entry for each id.
46  * 
47  *
48  *  fileindex
49  *    -----------------------------...
50  *    !                  !         
51  *   id1                 id2        
52  *    !
53  *   GArray
54  *
55  * The fileindex creates a FileIndexId object for each writer id, a
56  * Hashtable is kept to map the id to the FileIndexId
57  *
58  * The FileIndexId also keeps all the values in a sorted GArray.
59  *
60  * Finding a value for an id/format requires locating the correct GArray,
61  * then do a binary search to get the required value.
62  *
63  * Unlike gstmemindex:  All formats are assumed to sort to the
64  * same order.  All formats are assumed to be available from
65  * any entry.
66  */
67
68 /*
69  * Each array element is (32bits flags, nformats * 64bits)
70  */
71 typedef struct {
72   gint           id;
73   gchar         *id_desc;
74   gint           nformats;
75   GstFormat     *format;
76   GArray        *array;
77 } GstFileIndexId;
78
79 typedef struct _GstFileIndex GstFileIndex;
80 typedef struct _GstFileIndexClass GstFileIndexClass;
81
82 #define ARRAY_ROW_SIZE(_ii) \
83   (sizeof (gint32) + (_ii)->nformats * sizeof (gint64))
84 #define ARRAY_TOTAL_SIZE(_ii) \
85   (_ii->array->len * ARRAY_ROW_SIZE(_ii))
86
87 // don't forget to convert to/from BE byte-order
88 #define ARRAY_ROW_FLAGS(_row) \
89   (*((gint32*) (_row)))
90 #define ARRAY_ROW_VALUE(_row,_vx) \
91   (*(gint64*) (((gchar*)(_row)) + sizeof (gint32) + (_vx) * sizeof (gint64)))
92
93 struct _GstFileIndex {
94   GstIndex               parent;
95
96   gchar                 *location;
97   gboolean               is_loaded;
98   GSList                *unresolved;
99   GHashTable            *id_index;
100
101   GstIndexEntry         *ret_entry;  // hack to avoid leaking memory
102 };
103
104 struct _GstFileIndexClass {
105   GstIndexClass parent_class;
106 };
107
108 enum {
109   ARG_0,
110   ARG_LOCATION,
111 };
112
113 static void             gst_file_index_class_init       (GstFileIndexClass *klass);
114 static void             gst_file_index_init             (GstFileIndex *index);
115 static void             gst_file_index_dispose          (GObject *object);
116
117 static void
118 gst_file_index_set_property (GObject *object,
119                              guint prop_id,
120                              const GValue *value,
121                              GParamSpec *pspec);
122 static void
123 gst_file_index_get_property (GObject *object,
124                              guint prop_id,
125                              GValue *value,
126                              GParamSpec *pspec);
127
128 static gboolean
129 gst_file_index_resolve_writer (GstIndex *_index, GstObject *writer,
130                                gint *id, gchar **writer_string);
131
132 static void             gst_file_index_commit           (GstIndex *index, gint writer_id);
133 static void             gst_file_index_add_entry        (GstIndex *index, GstIndexEntry *entry);
134 static GstIndexEntry*   gst_file_index_get_assoc_entry  (GstIndex *index, gint id,
135                                                          GstIndexLookupMethod method,
136                                                          GstAssocFlags flags,
137                                                          GstFormat format, gint64 value,
138                                                          GCompareDataFunc func,
139                                                          gpointer user_data);
140
141 #define CLASS(file_index)  GST_FILE_INDEX_CLASS (G_OBJECT_GET_CLASS (file_index))
142
143 static GstIndex *parent_class = NULL;
144
145 GType
146 gst_file_index_get_type(void) {
147   static GType file_index_type = 0;
148
149   if (!file_index_type) {
150     static const GTypeInfo file_index_info = {
151       sizeof(GstFileIndexClass),
152       NULL,
153       NULL,
154       (GClassInitFunc)gst_file_index_class_init,
155       NULL,
156       NULL,
157       sizeof(GstFileIndex),
158       1,
159       (GInstanceInitFunc)gst_file_index_init,
160       NULL
161     };
162     file_index_type = g_type_register_static(GST_TYPE_INDEX, "GstFileIndex", &file_index_info, 0);
163   }
164   return file_index_type;
165 }
166
167 static void
168 gst_file_index_class_init (GstFileIndexClass *klass)
169 {
170   GObjectClass *gobject_class;
171   GstIndexClass *gstindex_class;
172
173   gobject_class = (GObjectClass*)klass;
174   gstindex_class = (GstIndexClass*)klass;
175
176   parent_class = g_type_class_ref(GST_TYPE_INDEX);
177
178   gobject_class->dispose        = gst_file_index_dispose;
179   gobject_class->set_property   = gst_file_index_set_property;
180   gobject_class->get_property   = gst_file_index_get_property;
181
182   gstindex_class->add_entry       = gst_file_index_add_entry;
183   gstindex_class->get_assoc_entry = gst_file_index_get_assoc_entry;
184   gstindex_class->commit          = gst_file_index_commit;
185   gstindex_class->resolve_writer  = gst_file_index_resolve_writer;
186
187   g_object_class_install_property (gobject_class, ARG_LOCATION,
188    g_param_spec_string ("location", "File Location",
189                         "Location of the index file",
190                         NULL, G_PARAM_READWRITE));
191 }
192
193 static void
194 gst_file_index_init (GstFileIndex *index)
195 {
196   GST_DEBUG(0, "created new file index");
197
198   index->id_index = g_hash_table_new (g_int_hash, g_int_equal);
199 }
200
201 static void
202 gst_file_index_dispose (GObject *object)
203 {
204   GstFileIndex *index = GST_FILE_INDEX (object);
205
206   if (index->location)
207     g_free (index->location);
208
209   // need to take care when destroying garrays with mmap'd segments
210
211   G_OBJECT_CLASS (parent_class)->dispose (object);
212 }
213
214 static gboolean
215 gst_file_index_resolve_writer (GstIndex *_index, GstObject *writer,
216                                gint *id, gchar **writer_string)
217 {
218   GstFileIndex *index = GST_FILE_INDEX (_index);
219   GSList *pending = index->unresolved;
220   gboolean match = FALSE;
221   GSList *elem;
222
223   if (!index->is_loaded)
224     return TRUE;
225
226   g_return_val_if_fail (id, FALSE);
227   g_return_val_if_fail (writer_string, FALSE);
228   g_return_val_if_fail (*writer_string, FALSE);
229
230   index->unresolved = NULL;
231
232   for (elem = pending; elem; elem = g_slist_next (elem)) {
233     GstFileIndexId *ii = elem->data;
234     if (strcmp (ii->id_desc, *writer_string) != 0) {
235       index->unresolved = g_slist_prepend (index->unresolved, ii);
236       continue;
237     }
238     
239     if (match) {
240       g_warning ("Duplicate matches for writer '%s'", *writer_string);
241       continue;
242     }
243
244     //g_warning ("resolve %d %s", *id, *writer_string);
245     ii->id = *id;
246     g_hash_table_insert (index->id_index, id, ii);
247     match = TRUE;
248   }
249
250   g_slist_free (pending);
251
252   return match;
253 }
254
255 static void
256 _fc_alloc_array (GstFileIndexId *id_index)
257 {
258   g_assert (!id_index->array);
259   id_index->array =
260     g_array_sized_new (FALSE, FALSE, ARRAY_ROW_SIZE (id_index), 0);
261 }
262
263 static void
264 gst_file_index_load (GstFileIndex *index)
265 {
266   xmlDocPtr doc;
267   xmlNodePtr root, part;
268   xmlChar *val;
269
270   g_assert (index->location);
271   g_return_if_fail (!index->is_loaded);
272
273   {
274     gchar *path = g_strdup_printf ("%s/gstindex.xml", index->location);
275     GError *err = NULL;
276     gchar *buf;
277     gsize len;
278     g_file_get_contents (path, &buf, &len, &err);
279     g_free (path);
280     if (err) g_error ("%s", err->message);
281
282     doc = xmlParseMemory (buf, len);
283     g_free (buf);
284   }
285
286   //xmlDocFormatDump (stderr, doc, TRUE);
287
288   root = doc->xmlRootNode;
289   if (strcmp (root->name, "gstfileindex") != 0)
290     g_error ("root node isn't a gstfileindex");
291   
292   val = xmlGetProp (root, "version");
293   if (!val || atoi (val) != 1)
294     g_error ("version != 1");
295   free (val);
296
297   for (part = root->children; part; part = part->next) {
298     if (strcmp (part->name, "writers") == 0) {
299       xmlNodePtr writer;
300       for (writer = part->children; writer; writer = writer->next) {
301         xmlChar *datafile = xmlGetProp (writer, "datafile");
302         gchar *path = g_strdup_printf ("%s/%s", index->location, datafile);
303         int fd;
304         GstFileIndexId *id_index;
305         xmlNodePtr wpart;
306         xmlChar *entries_str;
307         gpointer array_data;
308
309         free (datafile);
310
311         fd = open (path, O_RDONLY);
312         g_free (path);
313         if (fd < 0) {
314           g_warning ("Can't open '%s': %s", path, strerror (errno));
315           continue;
316         }
317
318         id_index = g_new0 (GstFileIndexId, 1);
319         id_index->id_desc = xmlGetProp (writer, "id");
320
321         for (wpart = writer->children; wpart; wpart = wpart->next) {
322           if (strcmp (wpart->name, "formats") == 0) {
323             xmlChar *count_str = xmlGetProp (wpart, "count");
324             gint fx=0;
325             xmlNodePtr format;
326
327             id_index->nformats = atoi (count_str);
328             free (count_str);
329
330             id_index->format = g_new (GstFormat, id_index->nformats);
331
332             for (format = wpart->children;
333                  format; format = format->next) {
334               xmlChar *nick = xmlGetProp (format, "nick");
335               GstFormat fmt = gst_format_get_by_nick (nick);
336               if (fmt == GST_FORMAT_UNDEFINED)
337                 g_error ("format '%s' undefined", nick);
338               g_assert (fx < id_index->nformats);
339               id_index->format[fx++] = fmt;
340               free (nick);
341             }
342           } else
343             g_warning ("unknown wpart '%s'", wpart->name);
344         }
345
346         g_assert (id_index->nformats > 0);
347         _fc_alloc_array (id_index);
348         g_assert (id_index->array->data == NULL);  // little bit risky
349
350         entries_str = xmlGetProp (writer, "entries");
351         id_index->array->len = atoi (entries_str);
352         free (entries_str);
353
354         array_data =
355           mmap (NULL, ARRAY_TOTAL_SIZE (id_index), PROT_READ, MAP_SHARED, fd, 0);
356         close (fd);
357         if (array_data == MAP_FAILED) {
358           g_error ("mmap %s failed: %s", path, strerror (errno));
359           continue;
360         }
361
362         id_index->array->data = array_data;
363
364         index->unresolved = g_slist_prepend (index->unresolved, id_index);
365       }
366     } else
367       g_warning ("unknown part '%s'", part->name);
368   }
369
370   xmlFreeDoc (doc);
371
372   GST_FLAG_UNSET (index, GST_INDEX_WRITABLE);
373   index->is_loaded = TRUE;
374 }
375
376 static void
377 gst_file_index_set_property (GObject *object,
378                              guint prop_id,
379                              const GValue *value,
380                              GParamSpec *pspec)
381 {
382   GstFileIndex *index = GST_FILE_INDEX (object);
383
384   switch (prop_id) {
385   case ARG_LOCATION:
386     if (index->location)
387       g_free (index->location);
388     index->location = g_value_dup_string (value);
389
390     if (index->location && !g_hash_table_size (index->id_index))
391       gst_file_index_load (index);
392     break;
393   }
394 }
395
396 static void
397 gst_file_index_get_property (GObject *object,
398                              guint prop_id,
399                              GValue *value,
400                              GParamSpec *pspec)
401 {
402   GstFileIndex *index = GST_FILE_INDEX (object);
403   
404   switch (prop_id) {
405   case ARG_LOCATION:
406     g_value_set_string (value, index->location);
407     break;
408   }
409 }
410
411 static void
412 _file_index_id_save_xml (gpointer _key, GstFileIndexId *ii, xmlNodePtr writers)
413 {
414   const gint bufsize = 16;
415   gchar buf[bufsize];
416   xmlNodePtr writer;
417   xmlNodePtr formats;
418   gint xx;
419   
420   writer = xmlNewChild (writers, NULL, "writer", NULL);
421   xmlSetProp (writer, "id", ii->id_desc);
422   g_snprintf (buf, bufsize, "%d", ii->array->len);
423   xmlSetProp (writer, "entries", buf);
424   g_snprintf (buf, bufsize, "%d", ii->id); // any unique number is OK
425   xmlSetProp (writer, "datafile", buf);
426
427   formats = xmlNewChild (writer, NULL, "formats", NULL);
428   g_snprintf (buf, bufsize, "%d", ii->nformats);
429   xmlSetProp (formats, "count", buf);
430
431   for (xx=0; xx < ii->nformats; xx++) {
432     xmlNodePtr format = xmlNewChild (formats, NULL, "format", NULL);
433     const GstFormatDefinition* def =
434       gst_format_get_details (ii->format[xx]);
435     xmlSetProp (format, "nick", def->nick);
436   }
437 }
438
439 //
440 // We must save the binary data in separate files because
441 // mmap wants getpagesize() alignment.  If we append all
442 // the data to one file then we don't know the appropriate
443 // padding since the page size isn't fixed.
444 //
445 static void
446 _file_index_id_save_entries (gpointer *_key,
447                              GstFileIndexId *ii,
448                              gchar *prefix)
449 {
450   GError *err = NULL;
451   gchar *path = g_strdup_printf ("%s/%d", prefix, ii->id);
452   GIOChannel *chan =
453     g_io_channel_new_file (path, "w", &err);
454   g_free (path);
455   if (err) g_error ("%s", err->message);
456   
457   g_io_channel_set_encoding (chan, NULL, &err);
458   if (err) g_error ("%s", err->message);
459
460   g_io_channel_write_chars (chan,
461                             ii->array->data,
462                             ARRAY_TOTAL_SIZE (ii),
463                             NULL,
464                             &err);
465   if (err) g_error ("%s", err->message);
466
467   g_io_channel_shutdown (chan, TRUE, &err);
468   if (err) g_error ("%s", err->message);
469
470   g_io_channel_unref (chan);
471 }
472
473 // We have to save the whole set of indexes into a single file
474 // so it doesn't make sense to commit only a single writer.
475 //
476 // i suggest:
477 //
478 // gst_index_commit (index, -1);
479
480 static void
481 gst_file_index_commit (GstIndex *_index, gint _writer_id)
482 {
483   GstFileIndex *index = GST_FILE_INDEX (_index);
484   xmlDocPtr doc;
485   xmlNodePtr writers;
486   GError *err = NULL;
487   gchar *path;
488   GIOChannel *tocfile;
489
490   g_return_if_fail (index->location);
491   g_return_if_fail (!index->is_loaded);
492
493   GST_FLAG_UNSET (index, GST_INDEX_WRITABLE);
494
495   doc = xmlNewDoc ("1.0");
496   doc->xmlRootNode = xmlNewDocNode (doc, NULL, "gstfileindex", NULL);
497   xmlSetProp (doc->xmlRootNode, "version", "1");
498
499   writers = xmlNewChild (doc->xmlRootNode, NULL, "writers", NULL);
500   g_hash_table_foreach (index->id_index,
501                         (GHFunc) _file_index_id_save_xml, writers);
502
503   if (mkdir (index->location, 0777) &&
504       errno != EEXIST)
505     g_error ("mkdir %s: %s", index->location, strerror (errno));
506
507   path = g_strdup_printf ("%s/gstindex.xml", index->location);
508   tocfile =
509     g_io_channel_new_file (path, "w", &err);
510   g_free (path);
511   if (err) g_error ("%s", err->message);
512
513   g_io_channel_set_encoding (tocfile, NULL, &err);
514   if (err) g_error ("%s", err->message);
515
516   {
517     xmlChar *xmlmem;
518     int xmlsize;
519     xmlDocDumpMemory (doc, &xmlmem, &xmlsize);
520     g_io_channel_write_chars (tocfile, xmlmem, xmlsize, NULL, &err);
521     if (err) g_error ("%s", err->message);
522     xmlFreeDoc (doc);
523     free (xmlmem);
524   }
525
526   g_io_channel_shutdown (tocfile, TRUE, &err);
527   if (err) g_error ("%s", err->message);
528
529   g_io_channel_unref (tocfile);
530
531   g_hash_table_foreach (index->id_index,
532                         (GHFunc) _file_index_id_save_entries,
533                         index->location);
534 }
535
536 static void
537 gst_file_index_add_id (GstIndex *index, GstIndexEntry *entry)
538 {
539   GstFileIndex *fileindex = GST_FILE_INDEX (index);
540   GstFileIndexId *id_index;
541
542   id_index = g_hash_table_lookup (fileindex->id_index, &entry->id);
543
544   if (!id_index) {
545     id_index = g_new0 (GstFileIndexId, 1);
546
547     id_index->id = entry->id;
548     id_index->id_desc = g_strdup (entry->data.id.description);
549
550     // It would be useful to know the GType of the writer so
551     // we can try to cope with changes in the id_desc path.
552
553     g_hash_table_insert (fileindex->id_index, &entry->id, id_index);
554   }
555 }
556
557 // This algorithm differs from libc bsearch in the handling
558 // of non-exact matches.
559
560 static gboolean
561 _fc_bsearch (GArray *          ary,
562              gint *            ret,
563              GCompareDataFunc  compare,
564              gconstpointer     sample,
565              gpointer          user_data)
566 {
567   gint first, last;
568   gint mid;
569   gint midsize;
570   gint cmp;
571   gint tx;
572
573   g_return_val_if_fail (compare, FALSE);
574
575   if (!ary->len)
576     {
577       if (ret) *ret = 0;
578       return FALSE;
579     }
580
581   first = 0;
582   last = ary->len - 1;
583
584   midsize = last - first;
585   
586   while (midsize > 1) {
587     mid = first + midsize / 2;
588     
589     cmp = (*compare) (sample, &g_array_index (ary, char, mid), user_data);
590     
591     if (cmp == 0)
592       {
593         // if there are multiple matches then scan for the first match
594         while (mid > 0 &&
595                (*compare) (sample,
596                            &g_array_index (ary, char, mid - 1),
597                            user_data) == 0)
598           --mid;
599
600         if (ret) *ret = mid;
601         return TRUE;
602       }
603     
604     if (cmp < 0)
605       last = mid-1;
606     else
607       first = mid+1;
608     
609     midsize = last - first;
610   }
611
612   for (tx = first; tx <= last; tx++)
613     {
614       cmp = (*compare) (sample, &g_array_index (ary, char, tx), user_data);
615
616       if (cmp < 0)
617         {
618           if (ret) *ret = tx;
619           return FALSE;
620         }
621       if (cmp == 0)
622         {
623           if (ret) *ret = tx;
624           return TRUE;
625         }
626     }
627
628   if (ret) *ret = last+1;
629   return FALSE;
630 }
631
632 static gint
633 file_index_compare (gconstpointer sample,
634                     gconstpointer row,
635                     gpointer user_data)
636 {
637   //GstFileIndexId *id_index = user_data;
638   const GstIndexAssociation *ca = sample;
639   gint64 val1 = ca->value;
640   gint64 val2 = GINT64_FROM_BE (ARRAY_ROW_VALUE (row, ca->format));
641   gint64 diff = val2 - val1;
642   return (diff == 0 ? 0 : (diff > 0 ? 1 : -1));
643 }
644
645 static void
646 gst_file_index_add_association (GstIndex *index, GstIndexEntry *entry)
647 {
648   GstFileIndex *fileindex = GST_FILE_INDEX (index);
649   GstFileIndexId *id_index;
650   gint mx;
651   GstIndexAssociation sample;
652   gboolean exact;
653   gint fx;
654
655   id_index = g_hash_table_lookup (fileindex->id_index, &entry->id);
656   if (!id_index)
657     return;
658
659   if (!id_index->nformats) {
660     gint fx;
661     id_index->nformats = GST_INDEX_NASSOCS (entry);
662     //g_warning ("%d: formats = %d", entry->id, id_index->nformats);
663     id_index->format = g_new (GstFormat, id_index->nformats);
664     for (fx=0; fx < id_index->nformats; fx++)
665       id_index->format[fx] = GST_INDEX_ASSOC_FORMAT (entry, fx);
666     _fc_alloc_array (id_index);
667   } else {
668     /* only sanity checking */
669     if (id_index->nformats != GST_INDEX_NASSOCS (entry))
670       g_warning ("fileindex arity change %d -> %d",
671                  id_index->nformats, GST_INDEX_NASSOCS (entry));
672     else {
673       gint fx;
674       for (fx=0; fx < id_index->nformats; fx++)
675         if (id_index->format[fx] != GST_INDEX_ASSOC_FORMAT (entry, fx))
676           g_warning ("fileindex format[%d] changed %d -> %d",
677                      fx,
678                      id_index->format[fx],
679                      GST_INDEX_ASSOC_FORMAT (entry, fx));
680     }
681   }
682
683   /* this is a hack, we should use a private structure instead */
684   sample.format = 0;
685   sample.value = GST_INDEX_ASSOC_VALUE (entry, 0);
686
687   exact =
688     _fc_bsearch (id_index->array, &mx, file_index_compare,
689                  &sample, id_index);
690
691   if (exact) {
692     // maybe overwrite instead?
693     g_warning ("ignoring duplicate index association at %lld",
694                GST_INDEX_ASSOC_VALUE (entry, 0));
695     return;
696   }
697
698   // should verify that all formats are ordered XXX
699
700   {
701     gchar row_data[ARRAY_ROW_SIZE (id_index)];
702
703     ARRAY_ROW_FLAGS (row_data) =
704       GINT32_TO_BE (GST_INDEX_ASSOC_FLAGS (entry));
705
706     for (fx = 0; fx < id_index->nformats; fx++)
707       ARRAY_ROW_VALUE (row_data, fx) =
708         GINT64_TO_BE (GST_INDEX_ASSOC_VALUE (entry, fx));
709
710     g_array_insert_val (id_index->array, mx, row_data);
711   }
712 }
713
714 /*
715 static void
716 show_entry (GstIndexEntry *entry)
717 {
718   switch (entry->type) {
719     case GST_INDEX_ENTRY_ID:
720       g_print ("id %d describes writer %s\n", entry->id, 
721                       GST_INDEX_ID_DESCRIPTION (entry));
722       break;
723     case GST_INDEX_ENTRY_FORMAT:
724       g_print ("%d: registered format %d for %s\n", entry->id, 
725                       GST_INDEX_FORMAT_FORMAT (entry),
726                       GST_INDEX_FORMAT_KEY (entry));
727       break;
728     case GST_INDEX_ENTRY_ASSOCIATION:
729     {
730       gint i;
731
732       g_print ("%d: %08x ", entry->id, GST_INDEX_ASSOC_FLAGS (entry));
733       for (i = 0; i < GST_INDEX_NASSOCS (entry); i++) {
734         g_print ("%d %lld ", GST_INDEX_ASSOC_FORMAT (entry, i), 
735                              GST_INDEX_ASSOC_VALUE (entry, i));
736       }
737       g_print ("\n");
738       break;
739     }
740     default:
741       break;
742   }
743 }
744 */
745
746 static void
747 gst_file_index_add_entry (GstIndex *index, GstIndexEntry *entry)
748 {
749   GstFileIndex *fileindex = GST_FILE_INDEX (index);
750
751   GST_DEBUG (0, "adding entry %p\n", fileindex);
752
753   //show_entry (entry);
754
755   switch (entry->type){
756      case GST_INDEX_ENTRY_ID:
757        gst_file_index_add_id (index, entry);
758        break;
759      case GST_INDEX_ENTRY_ASSOCIATION:
760        gst_file_index_add_association (index, entry);
761        break;
762      case GST_INDEX_ENTRY_OBJECT:
763        g_error ("gst_file_index_add_object not implemented");
764        break;
765      case GST_INDEX_ENTRY_FORMAT:
766        g_warning ("gst_file_index_add_format not implemented");
767        break;
768      default:
769        break;
770   }
771 }
772
773 static GstIndexEntry*
774 gst_file_index_get_assoc_entry (GstIndex *index,
775                                 gint id,
776                                 GstIndexLookupMethod method,
777                                 GstAssocFlags flags,
778                                 GstFormat format,
779                                 gint64 value,
780                                 GCompareDataFunc _ignore_func,
781                                 gpointer _ignore_user_data)
782 {
783   GstFileIndex *fileindex = GST_FILE_INDEX (index);
784   GstFileIndexId *id_index;
785   gint formatx = -1;
786   gint fx;
787   GstIndexAssociation sample;
788   gint mx;
789   gboolean exact;
790   gpointer row_data;
791   GstIndexEntry *entry;
792   gint xx;
793
794   id_index = g_hash_table_lookup (fileindex->id_index, &id);
795   if (!id_index)
796     return NULL;
797
798   for (fx=0; fx < id_index->nformats; fx++)
799     if (id_index->format[fx] == format)
800       { formatx = fx; break; }
801
802   if (formatx == -1) {
803     g_warning ("index does not contain format %d", format);
804     return NULL;
805   }
806
807   /* this is a hack, we should use a private structure instead */
808   sample.format = formatx;
809   sample.value = value;
810
811   exact =  _fc_bsearch (id_index->array, &mx, file_index_compare,
812                  &sample, id_index);
813
814   if (!exact) {
815     if (method == GST_INDEX_LOOKUP_EXACT)
816       return NULL;
817     else if (method == GST_INDEX_LOOKUP_BEFORE) {
818       if (mx == 0)
819         return NULL;
820       mx -= 1;
821     } else if (method == GST_INDEX_LOOKUP_AFTER) {
822       if (mx == id_index->array->len)
823         return NULL;
824     }
825   }
826
827   row_data = &g_array_index (id_index->array, char, mx);
828
829   // if exact then ignore flags (?)
830   if (method != GST_INDEX_LOOKUP_EXACT)
831     while ((GINT32_FROM_BE (ARRAY_ROW_FLAGS (row_data)) & flags) != flags) {
832       if (method == GST_INDEX_LOOKUP_BEFORE)
833         mx -= 1;
834       else if (method == GST_INDEX_LOOKUP_AFTER)
835         mx += 1;
836       if (mx < 0 || mx >= id_index->array->len)
837         return NULL;
838       row_data = &g_array_index (id_index->array, char, mx);
839     }
840
841   // entry memory management needs improvement
842   if (!fileindex->ret_entry)
843     fileindex->ret_entry = g_new0 (GstIndexEntry, 1);
844   entry = fileindex->ret_entry;
845   if (entry->data.assoc.assocs)
846     g_free (entry->data.assoc.assocs);
847
848   entry->type = GST_INDEX_ENTRY_ASSOCIATION;
849
850   GST_INDEX_NASSOCS (entry) = id_index->nformats;
851   entry->data.assoc.assocs =
852     g_new (GstIndexAssociation, id_index->nformats);
853
854   GST_INDEX_ASSOC_FLAGS (entry) =
855     GINT32_FROM_BE (ARRAY_ROW_FLAGS (row_data));
856
857   for (xx=0; xx < id_index->nformats; xx++) 
858     {
859       GST_INDEX_ASSOC_FORMAT (entry, xx) = id_index->format[xx];
860       GST_INDEX_ASSOC_VALUE (entry, xx) =
861         GINT64_FROM_BE (ARRAY_ROW_VALUE (row_data, xx));
862     }
863
864   return entry;
865 }
866
867 //////////////////////////////////////////////////////////////////////
868 // [UNTESTED] i don't understand the following plugin stuff [UNTESTED]
869 //////////////////////////////////////////////////////////////////////
870
871 static gboolean
872 plugin_init (GModule *module, GstPlugin *plugin)
873 {
874   GstIndexFactory *factory;
875
876   gst_plugin_set_longname (plugin, "A file index");
877
878   factory = gst_index_factory_new ("fileindex",
879                                    "A index that stores entries in file",
880                                    gst_file_index_get_type());
881
882   if (factory != NULL) {
883     gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
884   }
885   else {
886     g_warning ("could not register fileindex");
887   }
888   return TRUE;
889 }
890
891 GstPluginDesc plugin_desc2 = {
892   GST_VERSION_MAJOR,
893   GST_VERSION_MINOR,
894   "gstindexs",
895   plugin_init
896 };
897