1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
4 Copyright (C) 2006 Sebastien Granjoux
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "sparse_buffer.h"
23 #include "anjuta-marshal.h"
28 #include <libanjuta/anjuta-debug.h>
31 * This object works with the DmaSparseView which is a text view window allowing
32 * to display very big amount of data. Only the part of the data currently
33 * displayed is kept in memory.
35 * This object doesn't replace the GtkTextBuffer in the corresponding
36 * DmaSparseView. The GtkTextBuffer of each DmaSparseView contains only the data
37 * displayed by the view. So several view displaying the same DmaSparseBuffer
38 * will probably have GtkTextBuffer containing different data.
40 * The DmaSparseBuffer object contains the data necessary for each view and
41 * try to maintain a cache of recently used data. The data should be split in
42 * blocks having a address and a size. The buffer does not care about the
43 * exact content of each block.
45 * The DmaSparseBuffer does not have any graphical knowledge, like which data
46 * correspond to one line in the GtkTextBuffer.
47 *---------------------------------------------------------------------------*/
51 DMA_SPARSE_BUFFER_NODE_SIZE
= 512,
52 DMA_SPARSE_BUFFER_MAX_PAGE
= 60,
55 static GObjectClass
*parent_class
= NULL
;
63 static guint signals
[LAST_SIGNAL
] = {0};
66 *---------------------------------------------------------------------------*/
68 /* DmaBufferNode functions
69 *---------------------------------------------------------------------------*/
71 /* Transport functions
72 *---------------------------------------------------------------------------*/
74 DmaSparseBufferTransport
*
75 dma_sparse_buffer_alloc_transport (DmaSparseBuffer
*buffer
, guint lines
, guint chars
)
77 DmaSparseBufferTransport
*trans
;
79 trans
= g_slice_new0 (DmaSparseBufferTransport
);
81 trans
->buffer
= buffer
;
84 trans
->next
= buffer
->pending
;
85 buffer
->pending
= trans
;
91 dma_sparse_buffer_free_transport (DmaSparseBufferTransport
*trans
)
93 DmaSparseBufferTransport
**prev
;
95 g_return_if_fail (trans
!= NULL
);
97 for (prev
= &trans
->buffer
->pending
; *prev
!= trans
; prev
= &(*prev
)->next
)
101 g_warning ("transport structure is missing");
106 /* Remove transport structure and free it */
109 g_slice_free (DmaSparseBufferTransport
, trans
);
113 *---------------------------------------------------------------------------*/
115 static DmaSparseBufferNode
*
116 dma_sparse_buffer_find (DmaSparseBuffer
*buffer
, guint address
)
118 DmaSparseBufferNode
*node
= NULL
;
119 DmaSparseBufferNode
*next
;
121 /* Look in last node */
122 if (buffer
->cache
.head
!= NULL
)
124 gint gap
= buffer
->cache
.head
->lower
- address
+ DMA_SPARSE_BUFFER_NODE_SIZE
* 4;
126 if (gap
< DMA_SPARSE_BUFFER_NODE_SIZE
* 9)
128 /* node should be quite near */
129 node
= buffer
->cache
.head
;
143 if (node
== NULL
) break;
145 if (node
->lower
> address
)
147 /* Search backward */
150 else if (node
->upper
< address
)
155 if ((next
== NULL
) || (next
->lower
> address
))
157 /* Corresponding node doesn't exist */
164 /* Find current node */
173 on_dma_sparse_buffer_changed (const DmaSparseBuffer
*buffer
)
178 *---------------------------------------------------------------------------*/
181 dma_sparse_buffer_lookup (DmaSparseBuffer
*buffer
, guint address
)
183 return dma_sparse_buffer_find (buffer
, address
);
187 dma_sparse_buffer_first (DmaSparseBuffer
*buffer
)
193 dma_sparse_buffer_insert (DmaSparseBuffer
*buffer
, DmaSparseBufferNode
*node
)
195 /* New node should have been allocated by caller with g_new */
196 DmaSparseBufferNode
*prev
;
198 DEBUG_PRINT ("insert block %p %x %x", node
, node
->lower
, node
->upper
);
199 /* Look for previous node */
200 prev
= dma_sparse_buffer_find (buffer
, node
->lower
);
201 while ((prev
!= NULL
) && (node
->lower
<= prev
->upper
))
203 DmaSparseBufferNode
*tmp
;
205 DEBUG_PRINT ("remove previous block %x %x", prev
->lower
, prev
->upper
);
206 /* node overlap, remove it */
208 dma_sparse_buffer_remove (buffer
, prev
);
212 /* Insert node just after prev */
215 /* Insert at the beginning */
217 node
->next
= buffer
->head
;
223 node
->next
= prev
->next
;
226 if (node
->next
!= NULL
)
228 node
->next
->prev
= node
;
231 /* Check if new node overlap next one */
232 while ((node
->next
!= NULL
) && (node
->upper
>= node
->next
->lower
))
234 DEBUG_PRINT ("remove next block %p %x %x", node
->next
, node
->next
->lower
, node
->next
->upper
);
235 /* node overlap, remove it */
236 dma_sparse_buffer_remove (buffer
, node
->next
);
239 /* Insert node at the beginning of cache list */
240 node
->cache
.prev
= NULL
;
241 node
->cache
.next
= buffer
->cache
.head
;
242 if (buffer
->cache
.head
!= NULL
)
244 buffer
->cache
.head
->prev
= node
;
250 dma_sparse_buffer_remove (DmaSparseBuffer
*buffer
, DmaSparseBufferNode
*node
)
252 /* Remove node from node list */
253 if (node
->next
!= NULL
)
255 node
->next
->prev
= node
->prev
;
257 if (node
->prev
!= NULL
)
259 node
->prev
->next
= node
->next
;
261 if (buffer
->head
== node
)
263 buffer
->head
= node
->next
;
266 /* Remove node from cache list */
267 if (node
->cache
.next
!= NULL
)
269 node
->cache
.next
->prev
= node
->cache
.prev
;
271 if (node
->cache
.prev
!= NULL
)
273 node
->cache
.prev
->next
= node
->cache
.next
;
275 if (buffer
->cache
.head
== node
)
277 buffer
->cache
.head
= node
->cache
.next
;
279 if (buffer
->cache
.tail
== node
)
281 buffer
->cache
.tail
= node
->cache
.prev
;
290 dma_sparse_buffer_remove_all (DmaSparseBuffer
*buffer
)
292 DmaSparseBufferNode
*node
;
293 DmaSparseBufferNode
*next
;
295 for (node
= buffer
->head
; node
!= NULL
; node
= next
)
300 buffer
->cache
.head
= NULL
;
301 buffer
->cache
.tail
= NULL
;
307 dma_sparse_buffer_get_lower (const DmaSparseBuffer
*buffer
)
309 return buffer
->lower
;
313 dma_sparse_buffer_get_upper (const DmaSparseBuffer
*buffer
)
315 return buffer
->upper
;
319 dma_sparse_buffer_changed (const DmaSparseBuffer
*buffer
)
321 g_signal_emit (G_OBJECT (buffer
), signals
[CHANGED
], 0);
325 dma_sparse_buffer_add_mark (DmaSparseBuffer
*buffer
, guint address
, gint mark
)
329 if (buffer
->mark
== NULL
)
331 /* Create new hash table */
332 buffer
->mark
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
336 markers
= GPOINTER_TO_INT (g_hash_table_lookup (buffer
->mark
, GINT_TO_POINTER (address
)));
337 markers
|= 1 << mark
;
338 g_hash_table_replace (buffer
->mark
, GINT_TO_POINTER (address
), GINT_TO_POINTER (markers
));
342 dma_sparse_buffer_remove_mark (DmaSparseBuffer
*buffer
, guint address
, gint mark
)
346 if (buffer
->mark
== NULL
) return; /* No mark */
348 /* Remove one mark */
349 markers
= GPOINTER_TO_INT (g_hash_table_lookup (buffer
->mark
, GINT_TO_POINTER (address
)));
350 markers
&= ~ (1 << mark
);
354 g_hash_table_remove (buffer
->mark
, GINT_TO_POINTER (address
));
358 g_hash_table_replace (buffer
->mark
, GINT_TO_POINTER (address
), GINT_TO_POINTER (markers
));
362 struct RemoveMarkPacket
369 on_remove_mark (gpointer key
, gpointer value
, gpointer user_data
)
371 struct RemoveMarkPacket
* pack
= (struct RemoveMarkPacket
*)user_data
;
373 value
= GINT_TO_POINTER (GPOINTER_TO_INT (value
) & ~(1<< pack
->mark
));
374 g_hash_table_replace (pack
->hash
, key
, value
);
378 on_remove_empty_mark (gpointer key
, gpointer value
, gpointer user_data
)
380 return (value
== NULL
);
384 dma_sparse_buffer_remove_all_mark (DmaSparseBuffer
*buffer
, gint mark
)
386 /* marker hash table could be null, is no marks have been set */
387 if (buffer
->mark
!= NULL
)
389 struct RemoveMarkPacket pack
;
391 pack
.hash
= buffer
->mark
;
394 g_hash_table_foreach (buffer
->mark
, on_remove_mark
, &pack
);
395 g_hash_table_foreach_remove (buffer
->mark
, on_remove_empty_mark
, NULL
);
400 dma_sparse_buffer_get_marks (DmaSparseBuffer
*buffer
, guint address
)
402 if (buffer
->mark
== NULL
) return 0;
404 return GPOINTER_TO_INT (g_hash_table_lookup (buffer
->mark
, GINT_TO_POINTER (address
)));
407 /* Iterator private functions
408 *---------------------------------------------------------------------------*/
411 dma_sparse_iter_forward_line (DmaSparseIter
*iter
)
413 return DMA_GET_SPARSE_BUFFER_CLASS (iter
->buffer
)->forward_line (iter
);
417 dma_sparse_iter_backward_line (DmaSparseIter
*iter
)
419 return DMA_GET_SPARSE_BUFFER_CLASS (iter
->buffer
)->backward_line (iter
);
423 dma_sparse_iter_insert_line (DmaSparseIter
*iter
, GtkTextIter
*dst
)
425 DMA_GET_SPARSE_BUFFER_CLASS (iter
->buffer
)->insert_line (iter
, dst
);
428 /* Iterator public functions
429 *---------------------------------------------------------------------------*/
432 dma_sparse_buffer_get_iterator_at_address (DmaSparseBuffer
*buffer
, DmaSparseIter
*iter
, guint address
)
434 g_return_if_fail (iter
!= NULL
);
435 g_return_if_fail (DMA_IS_SPARSE_BUFFER (buffer
));
437 iter
->buffer
= buffer
;
438 iter
->node
= dma_sparse_buffer_find (buffer
, address
);
439 iter
->base
= address
;
441 iter
->stamp
= buffer
->stamp
;
444 DMA_GET_SPARSE_BUFFER_CLASS (iter
->buffer
)->refresh_iter (iter
);
448 dma_sparse_buffer_get_iterator_near_address (DmaSparseBuffer
*buffer
, DmaSparseIter
*iter
, guint address
)
450 g_return_if_fail (iter
!= NULL
);
451 g_return_if_fail (DMA_IS_SPARSE_BUFFER (buffer
));
453 iter
->buffer
= buffer
;
454 iter
->node
= dma_sparse_buffer_find (buffer
, address
);
455 iter
->base
= address
;
458 iter
->stamp
= buffer
->stamp
;
460 DMA_GET_SPARSE_BUFFER_CLASS (iter
->buffer
)->refresh_iter (iter
);
464 dma_sparse_iter_copy (DmaSparseIter
*dst
, const DmaSparseIter
*src
)
466 memcpy(dst
, src
, sizeof (DmaSparseIter
));
470 dma_sparse_iter_move_at (DmaSparseIter
*iter
, guint address
)
472 dma_sparse_buffer_get_iterator_at_address (iter
->buffer
, iter
, address
);
476 dma_sparse_iter_move_near (DmaSparseIter
*iter
, guint address
)
478 dma_sparse_buffer_get_iterator_near_address (iter
->buffer
, iter
, address
);
482 dma_sparse_iter_refresh (DmaSparseIter
*iter
)
484 if (iter
->buffer
->stamp
!= iter
->stamp
)
486 iter
->node
= dma_sparse_buffer_find (iter
->buffer
, iter
->base
);
487 iter
->stamp
= iter
->buffer
->stamp
;
488 DMA_GET_SPARSE_BUFFER_CLASS (iter
->buffer
)->refresh_iter (iter
);
493 dma_sparse_iter_round (DmaSparseIter
*iter
, gboolean round_up
)
495 if (iter
->buffer
->stamp
!= iter
->stamp
)
497 iter
->node
= dma_sparse_buffer_find (iter
->buffer
, iter
->base
);
498 iter
->stamp
= iter
->buffer
->stamp
;
500 DMA_GET_SPARSE_BUFFER_CLASS (iter
->buffer
)->round_iter (iter
, round_up
);
504 dma_sparse_iter_get_address (DmaSparseIter
*iter
)
506 return DMA_GET_SPARSE_BUFFER_CLASS (iter
->buffer
)->get_address (iter
);
510 dma_sparse_iter_forward_lines (DmaSparseIter
*iter
, gint count
)
514 dma_sparse_iter_refresh (iter
);
518 for (i
= 0; i
> count
; --i
)
520 if (!dma_sparse_iter_backward_line (iter
)) return FALSE
;
525 for (i
= 0; i
< count
; i
++)
527 if (!dma_sparse_iter_forward_line (iter
)) return FALSE
;
535 dma_sparse_iter_insert_lines (DmaSparseIter
*src
, GtkTextIter
*dst
, guint count
)
539 GtkTextBuffer
*buffer
;
541 buffer
= gtk_text_iter_get_buffer (dst
);
543 /* It is possible to get an iterator that doesn't point to any node
544 * if it has been move to a fixed address without rounding it to
545 * the nearest line */
547 dma_sparse_iter_copy (&iter
, src
);
548 dma_sparse_iter_refresh (&iter
);
551 for (; line
< count
; line
++)
553 dma_sparse_iter_insert_line (&iter
, dst
);
554 if (!dma_sparse_iter_forward_line (&iter
))
559 if (line
!= count
- 1) gtk_text_buffer_insert (buffer
, dst
, "\n", 1);
564 *---------------------------------------------------------------------------*/
566 /* Used in dispose and finalize */
568 /* dispose is the first destruction step. It is used to unref object created
569 * with instance_init in order to break reference counting cycles. This
570 * function could be called several times. All function should still work
571 * after this call. It has to called its parents.*/
574 dma_sparse_buffer_dispose (GObject
*object
)
576 /*DmaSparseBuffer *self = DMA_DATA_BUFFER (object);*/
578 G_OBJECT_CLASS (parent_class
)->dispose (object
);
581 /* finalize is the last destruction step. It must free all memory allocated
582 * with instance_init. It is called only one time just before releasing all
586 dma_sparse_buffer_finalize (GObject
*object
)
588 DmaSparseBuffer
*buffer
= DMA_SPARSE_BUFFER (object
);
589 DmaSparseBufferTransport
*trans
;
591 dma_sparse_buffer_remove_all (buffer
);
593 /* Free all remaining transport structure */
594 for (trans
= buffer
->pending
; trans
!= NULL
;)
596 DmaSparseBufferTransport
*next
= trans
->next
;
598 g_slice_free (DmaSparseBufferTransport
, trans
);
602 /* Free marker hash table */
603 if (buffer
->mark
!= NULL
)
605 g_hash_table_destroy (buffer
->mark
);
609 G_OBJECT_CLASS (parent_class
)->finalize (object
);
612 /* instance_init is the constructor. All functions should work after this
616 dma_sparse_buffer_instance_init (DmaSparseBuffer
*buffer
)
621 buffer
->cache
.head
= NULL
;
622 buffer
->cache
.tail
= NULL
;
625 buffer
->pending
= NULL
;
629 /* class_init intialize the class itself not the instance */
632 dma_sparse_buffer_class_init (DmaSparseBufferClass
* klass
)
634 GObjectClass
*object_class
;
636 g_return_if_fail (klass
!= NULL
);
638 parent_class
= g_type_class_peek_parent (klass
);
640 object_class
= G_OBJECT_CLASS (klass
);
642 object_class
->dispose
= dma_sparse_buffer_dispose
;
643 object_class
->finalize
= dma_sparse_buffer_finalize
;
645 klass
->changed
= on_dma_sparse_buffer_changed
;
647 signals
[CHANGED
] = g_signal_new ("changed",
648 G_OBJECT_CLASS_TYPE (object_class
),
650 G_STRUCT_OFFSET (DmaSparseBufferClass
, changed
),
652 anjuta_marshal_VOID__VOID
,
658 dma_sparse_buffer_get_type (void)
660 static GType type
= 0;
664 static const GTypeInfo type_info
=
666 sizeof (DmaSparseBufferClass
),
667 (GBaseInitFunc
) NULL
,
668 (GBaseFinalizeFunc
) NULL
,
669 (GClassInitFunc
) dma_sparse_buffer_class_init
,
670 (GClassFinalizeFunc
) NULL
,
671 NULL
, /* class_data */
672 sizeof (DmaSparseBuffer
),
674 (GInstanceInitFunc
) dma_sparse_buffer_instance_init
,
675 NULL
/* value_table */
678 type
= g_type_register_static (G_TYPE_OBJECT
,
679 "DmaSparseBuffer", &type_info
, 0);
685 /* Creation and Destruction
686 *---------------------------------------------------------------------------*/
689 dma_sparse_buffer_new (guint lower
, guint upper
)
691 DmaSparseBuffer
*buffer
;
693 buffer
= g_object_new (DMA_SPARSE_BUFFER_TYPE
, NULL
);
694 g_assert (buffer
!= NULL
);
696 buffer
->lower
= lower
;
697 buffer
->upper
= upper
;
703 dma_sparse_buffer_free (DmaSparseBuffer
*buffer
)
705 g_object_unref (buffer
);