"Initial commit to Gerrit"
[profile/ivi/cogl.git] / cogl / cogl-bitmask.c
1 /*
2  * Cogl
3  *
4  * An object oriented GL/GLES Abstraction/Utility Layer
5  *
6  * Copyright (C) 2010 Intel Corporation.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20  *
21  *
22  *
23  * Authors:
24  *   Neil Roberts <neil@linux.intel.com>
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include <glib.h>
32 #include <string.h>
33
34 #include "cogl-bitmask.h"
35 #include "cogl-util.h"
36 #include "cogl-flags.h"
37
38 /* This code assumes that we can cast an unsigned long to a pointer
39    and back without losing any data */
40 G_STATIC_ASSERT (sizeof (unsigned long) <= sizeof (void *));
41
42 #define ARRAY_INDEX(bit_num) \
43   ((bit_num) / (sizeof (unsigned long) * 8))
44 #define BIT_INDEX(bit_num) \
45   ((bit_num) & (sizeof (unsigned long) * 8 - 1))
46 #define BIT_MASK(bit_num) \
47   (1UL << BIT_INDEX (bit_num))
48
49 gboolean
50 _cogl_bitmask_get_from_array (const CoglBitmask *bitmask,
51                               unsigned int bit_num)
52 {
53   GArray *array = (GArray *) *bitmask;
54
55   /* If the index is off the end of the array then assume the bit is
56      not set */
57   if (bit_num >= sizeof (unsigned long) * 8 * array->len)
58     return FALSE;
59   else
60     return !!(g_array_index (array, unsigned long, ARRAY_INDEX (bit_num)) &
61               BIT_MASK (bit_num));
62 }
63
64 static void
65 _cogl_bitmask_convert_to_array (CoglBitmask *bitmask)
66 {
67   GArray *array;
68   /* Fetch the old values */
69   unsigned long old_values = _cogl_bitmask_to_bits (bitmask);
70
71   array = g_array_new (FALSE, /* not zero-terminated */
72                        TRUE, /* do clear new entries */
73                        sizeof (unsigned long));
74   /* Copy the old values back in */
75   g_array_append_val (array, old_values);
76
77   *bitmask = (struct _CoglBitmaskImaginaryType *) array;
78 }
79
80 void
81 _cogl_bitmask_set_in_array (CoglBitmask *bitmask,
82                             unsigned int bit_num,
83                             gboolean value)
84 {
85   GArray *array;
86   unsigned int array_index;
87   unsigned long new_value_mask;
88
89   /* If the bitmask is not already an array then we need to allocate one */
90   if (!_cogl_bitmask_has_array (bitmask))
91     _cogl_bitmask_convert_to_array (bitmask);
92
93   array = (GArray *) *bitmask;
94
95   array_index = ARRAY_INDEX (bit_num);
96   /* Grow the array if necessary. This will clear the new data */
97   if (array_index >= array->len)
98     g_array_set_size (array, array_index + 1);
99
100   new_value_mask = BIT_MASK (bit_num);
101
102   if (value)
103     g_array_index (array, unsigned long, array_index) |= new_value_mask;
104   else
105     g_array_index (array, unsigned long, array_index) &= ~new_value_mask;
106 }
107
108 void
109 _cogl_bitmask_set_bits (CoglBitmask *dst,
110                         const CoglBitmask *src)
111 {
112   if (_cogl_bitmask_has_array (src))
113     {
114       GArray *src_array, *dst_array;
115       int i;
116
117       if (!_cogl_bitmask_has_array (dst))
118         _cogl_bitmask_convert_to_array (dst);
119
120       dst_array = (GArray *) *dst;
121       src_array = (GArray *) *src;
122
123       if (dst_array->len < src_array->len)
124         g_array_set_size (dst_array, src_array->len);
125
126       for (i = 0; i < src_array->len; i++)
127         g_array_index (dst_array, unsigned long, i) |=
128           g_array_index (src_array, unsigned long, i);
129     }
130   else if (_cogl_bitmask_has_array (dst))
131     {
132       GArray *dst_array;
133
134       dst_array = (GArray *) *dst;
135
136       g_array_index (dst_array, unsigned long, 0) |=
137         _cogl_bitmask_to_bits (src);
138     }
139   else
140     *dst = _cogl_bitmask_from_bits (_cogl_bitmask_to_bits (dst) |
141                                     _cogl_bitmask_to_bits (src));
142 }
143
144 void
145 _cogl_bitmask_set_range_in_array (CoglBitmask *bitmask,
146                                   unsigned int n_bits,
147                                   gboolean value)
148 {
149   GArray *array;
150   unsigned int array_index, bit_index;
151
152   if (n_bits == 0)
153     return;
154
155   /* If the bitmask is not already an array then we need to allocate one */
156   if (!_cogl_bitmask_has_array (bitmask))
157     _cogl_bitmask_convert_to_array (bitmask);
158
159   array = (GArray *) *bitmask;
160
161   /* Get the array index of the top most value that will be touched */
162   array_index = ARRAY_INDEX (n_bits - 1);
163   /* Get the bit index of the top most value */
164   bit_index = BIT_INDEX (n_bits - 1);
165   /* Grow the array if necessary. This will clear the new data */
166   if (array_index >= array->len)
167     g_array_set_size (array, array_index + 1);
168
169   if (value)
170     {
171       /* Set the bits that are touching this index */
172       g_array_index (array, unsigned long, array_index) |=
173         ~0UL >> (sizeof (unsigned long) * 8 - 1 - bit_index);
174
175       /* Set all of the bits in any lesser indices */
176       memset (array->data, 0xff, sizeof (unsigned long) * array_index);
177     }
178   else
179     {
180       /* Clear the bits that are touching this index */
181       g_array_index (array, unsigned long, array_index) &= ~1UL << bit_index;
182
183       /* Clear all of the bits in any lesser indices */
184       memset (array->data, 0x00, sizeof (unsigned long) * array_index);
185     }
186 }
187
188 void
189 _cogl_bitmask_xor_bits (CoglBitmask *dst,
190                         const CoglBitmask *src)
191 {
192   if (_cogl_bitmask_has_array (src))
193     {
194       GArray *src_array, *dst_array;
195       int i;
196
197       if (!_cogl_bitmask_has_array (dst))
198         _cogl_bitmask_convert_to_array (dst);
199
200       dst_array = (GArray *) *dst;
201       src_array = (GArray *) *src;
202
203       if (dst_array->len < src_array->len)
204         g_array_set_size (dst_array, src_array->len);
205
206       for (i = 0; i < src_array->len; i++)
207         g_array_index (dst_array, unsigned long, i) ^=
208           g_array_index (src_array, unsigned long, i);
209     }
210   else if (_cogl_bitmask_has_array (dst))
211     {
212       GArray *dst_array;
213
214       dst_array = (GArray *) *dst;
215
216       g_array_index (dst_array, unsigned long, 0) ^=
217         _cogl_bitmask_to_bits (src);
218     }
219   else
220     *dst = _cogl_bitmask_from_bits (_cogl_bitmask_to_bits (dst) ^
221                                     _cogl_bitmask_to_bits (src));
222 }
223
224 void
225 _cogl_bitmask_clear_all_in_array (CoglBitmask *bitmask)
226 {
227   GArray *array = (GArray *) *bitmask;
228
229   memset (array->data, 0, sizeof (unsigned long) * array->len);
230 }
231
232 void
233 _cogl_bitmask_foreach (const CoglBitmask *bitmask,
234                        CoglBitmaskForeachFunc func,
235                        void *user_data)
236 {
237   if (_cogl_bitmask_has_array (bitmask))
238     {
239       GArray *array = (GArray *) *bitmask;
240       const unsigned long *values = &g_array_index (array, unsigned long, 0);
241       int bit_num;
242
243       COGL_FLAGS_FOREACH_START (values, array->len, bit_num)
244         {
245           if (!func (bit_num, user_data))
246             return;
247         }
248       COGL_FLAGS_FOREACH_END;
249     }
250   else
251     {
252       unsigned long mask = _cogl_bitmask_to_bits (bitmask);
253       int bit_num;
254
255       COGL_FLAGS_FOREACH_START (&mask, 1, bit_num)
256         {
257           if (!func (bit_num, user_data))
258             return;
259         }
260       COGL_FLAGS_FOREACH_END;
261     }
262 }
263
264 void
265 _cogl_bitmask_set_flags_array (const CoglBitmask *bitmask,
266                                unsigned long *flags)
267 {
268   const GArray *array = (const GArray *) *bitmask;
269   int i;
270
271   for (i = 0; i < array->len; i++)
272     flags[i] |= g_array_index (array, unsigned long, i);
273 }
274
275 int
276 _cogl_bitmask_popcount_in_array (const CoglBitmask *bitmask)
277 {
278   const GArray *array = (const GArray *) *bitmask;
279   int pop = 0;
280   int i;
281
282   for (i = 0; i < array->len; i++)
283     pop += _cogl_util_popcountl (g_array_index (array, unsigned long, i));
284
285   return pop;
286 }
287
288 int
289 _cogl_bitmask_popcount_upto_in_array (const CoglBitmask *bitmask,
290                                       int upto)
291 {
292   const GArray *array = (const GArray *) *bitmask;
293
294   if (upto >= array->len * sizeof (unsigned long) * 8)
295     return _cogl_bitmask_popcount_in_array (bitmask);
296   else
297     {
298       unsigned long top_mask;
299       int array_index = ARRAY_INDEX (upto);
300       int bit_index = BIT_INDEX (upto);
301       int pop = 0;
302       int i;
303
304       for (i = 0; i < array_index; i++)
305         pop += _cogl_util_popcountl (g_array_index (array, unsigned long, i));
306
307       top_mask = g_array_index (array, unsigned long, array_index);
308
309       return pop + _cogl_util_popcountl (top_mask & ((1UL << bit_index) - 1));
310     }
311 }