mutter/cogl/cogl-list.h
Neil Roberts 7bf0fe9df8 Don't dereference an unitialised pointer in _cogl_container_of
The previous implementation was dereferencing the sample pointer in
order to get the offset to subtract from the member pointer. The
resulting value is then only used to get a pointer to the member in
order to calculate the offset so it doesn't actually read from the
memory location and shouldn't cause any problems. However this is
probably technically invalid and could have undefined behaviour. It
looks like clang takes advantage of this undefined behaviour and
doesn't actually offset the pointer. It also generates a warning when
it does this.

This patch splits the _cogl_container_of macro into two
implementations. Previously the macro was always used in the list
iterator macros like this:

SomeType *sample = _cogl_container_of(list_node, sample, link)

Instead of doing that there is now a new macro called
_cogl_list_set_iterator which explicitly assigns to the sample pointer
with an initial value before assigning to it again with the real
offset. This redundant initialisation gets optimised out by compiler.

The second macro is still called _cogl_container_of but instead of
taking a sample pointer it just directly takes the type name. That way
it can use the standard offsetof macro.

https://bugzilla.gnome.org/show_bug.cgi?id=723530

Reviewed-by: Robert Bragg <robert@linux.intel.com>
(cherry picked from commit 1efed1e0a2bce706eb4901979ed4e717bb13e4e2)
2014-02-20 13:38:43 +00:00

130 lines
5.1 KiB
C

/*
* Copyright © 2008 Kristian Høgsberg
* Copyright © 2012, 2013 Intel Corporation
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
/* This list implementation is based on the Wayland source code */
#ifndef COGL_LIST_H
#define COGL_LIST_H
#include <stddef.h>
/**
* CoglList - linked list
*
* The list head is of "CoglList" type, and must be initialized
* using cogl_list_init(). All entries in the list must be of the same
* type. The item type must have a "CoglList" member. This
* member will be initialized by cogl_list_insert(). There is no need to
* call cogl_list_init() on the individual item. To query if the list is
* empty in O(1), use cogl_list_empty().
*
* Let's call the list reference "CoglList foo_list", the item type as
* "item_t", and the item member as "CoglList link". The following code
*
* The following code will initialize a list:
*
* cogl_list_init (foo_list);
* cogl_list_insert (foo_list, item1); Pushes item1 at the head
* cogl_list_insert (foo_list, item2); Pushes item2 at the head
* cogl_list_insert (item2, item3); Pushes item3 after item2
*
* The list now looks like [item2, item3, item1]
*
* Will iterate the list in ascending order:
*
* item_t *item;
* cogl_list_for_each(item, foo_list, link) {
* Do_something_with_item(item);
* }
*/
typedef struct _CoglList CoglList;
struct _CoglList
{
CoglList *prev;
CoglList *next;
};
void
_cogl_list_init (CoglList *list);
void
_cogl_list_insert (CoglList *list,
CoglList *elm);
void
_cogl_list_remove (CoglList *elm);
int
_cogl_list_length (CoglList *list);
int
_cogl_list_empty (CoglList *list);
void
_cogl_list_insert_list (CoglList *list,
CoglList *other);
/* This assigns to iterator first so that taking a reference to it
* later in the second step won't be an undefined operation. It
* assigns the value of list_node rather than 0 so that it is possible
* have list_node be based on the previous value of iterator. In that
* respect iterator is just used as a convenient temporary variable.
* The compiler optimises all of this down to a single subtraction by
* a constant */
#define _cogl_list_set_iterator(list_node, iterator, member) \
((iterator) = (void *) (list_node), \
(iterator) = (void *) ((char *) (iterator) - \
(((char *) &(iterator)->member) - \
(char *) (iterator))))
#define _cogl_container_of(ptr, type, member) \
(type *) ((char *) (ptr) - offsetof (type, member))
#define _cogl_list_for_each(pos, head, member) \
for (_cogl_list_set_iterator ((head)->next, pos, member); \
&pos->member != (head); \
_cogl_list_set_iterator (pos->member.next, pos, member))
#define _cogl_list_for_each_safe(pos, tmp, head, member) \
for (_cogl_list_set_iterator ((head)->next, pos, member), \
_cogl_list_set_iterator ((pos)->member.next, tmp, member); \
&pos->member != (head); \
pos = tmp, \
_cogl_list_set_iterator (pos->member.next, tmp, member))
#define _cogl_list_for_each_reverse(pos, head, member) \
for (_cogl_list_set_iterator ((head)->prev, pos, member); \
&pos->member != (head); \
_cogl_list_set_iterator (pos->member.prev, pos, member))
#define _cogl_list_for_each_reverse_safe(pos, tmp, head, member) \
for (_cogl_list_set_iterator ((head)->prev, pos, member), \
_cogl_list_set_iterator ((pos)->member.prev, tmp, member); \
&pos->member != (head); \
pos = tmp, \
_cogl_list_set_iterator (pos->member.prev, tmp, member))
#endif /* COGL_LIST_H */