Initial commit of vpp code.
[vpp.git] / vppinfra / vppinfra / sparse_vec.h
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /*
16   Copyright (c) 2005 Eliot Dresselhaus
17
18   Permission is hereby granted, free of charge, to any person obtaining
19   a copy of this software and associated documentation files (the
20   "Software"), to deal in the Software without restriction, including
21   without limitation the rights to use, copy, modify, merge, publish,
22   distribute, sublicense, and/or sell copies of the Software, and to
23   permit persons to whom the Software is furnished to do so, subject to
24   the following conditions:
25
26   The above copyright notice and this permission notice shall be
27   included in all copies or substantial portions of the Software.
28
29   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
33   LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
34   OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35   WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 */
37
38 #ifndef included_sparse_vec_h
39 #define included_sparse_vec_h
40
41 #include <vppinfra/vec.h>
42 #include <vppinfra/bitops.h>
43
44 /* Sparsely indexed vectors.  Basic idea taken from Hacker's delight.
45    Eliot added ranges. */
46 typedef struct {
47   /* Bitmap one for each sparse index. */
48   uword * is_member_bitmap;
49
50   /* member_counts[i] = total number of members with j < i. */
51   u16 * member_counts;
52
53 #define SPARSE_VEC_IS_RANGE (1 << 0)
54 #define SPARSE_VEC_IS_VALID_RANGE (1 << 1)
55   u8 * range_flags;
56 } sparse_vec_header_t;
57
58 always_inline sparse_vec_header_t *
59 sparse_vec_header (void * v)
60 { return vec_header (v, sizeof (sparse_vec_header_t)); }
61
62 /* Index 0 is always used to mark indices that are not valid in
63    sparse vector.  For example, you look up V[0x1234] and 0x1234 is not
64    known you'll get 0 back as an index. */
65 #define SPARSE_VEC_INVALID_INDEX (0)
66
67 always_inline void *
68 sparse_vec_new (uword elt_bytes, uword sparse_index_bits)
69 {
70   void * v;
71   sparse_vec_header_t * h;
72   word n;
73
74   ASSERT (sparse_index_bits <= 16);
75
76   v = _vec_resize (0,
77                    /* length increment */ 8,
78                    /* data bytes */ 8*elt_bytes,
79                    /* header bytes */ sizeof (h[0]),
80                    /* data align */ 0);
81
82   /* Make space for invalid entry (entry 0). */
83   _vec_len (v) = 1;
84
85   h = sparse_vec_header (v);
86
87   n = sparse_index_bits - min_log2 (BITS (uword));
88   if (n < 0)
89     n = 0;
90   n = 1 << n;
91   vec_resize (h->is_member_bitmap, n);
92   vec_resize (h->member_counts, n);
93
94   return v;
95 }
96
97 always_inline uword
98 sparse_vec_index_internal (void * v,
99                            uword sparse_index,
100                            uword maybe_range,
101                            u32 * insert)
102 {
103   sparse_vec_header_t * h;
104   uword i, b, d, w;
105   u8 is_member;
106
107   h = sparse_vec_header (v);
108   i = sparse_index / BITS (h->is_member_bitmap[0]);
109   b = (uword) 1 << (uword) (sparse_index % BITS (h->is_member_bitmap[0]));
110
111   ASSERT (i < vec_len (h->is_member_bitmap));
112   ASSERT (i < vec_len (h->member_counts));
113
114   w = h->is_member_bitmap[i];
115   d = h->member_counts[i] + count_set_bits (w & (b - 1));
116
117   is_member = (w & b) != 0;
118   if (maybe_range)
119     {
120       u8 r = h->range_flags[d];
121       u8 is_range, is_valid_range;
122
123       is_range = maybe_range & (r & SPARSE_VEC_IS_RANGE);
124       is_valid_range = (r & SPARSE_VEC_IS_VALID_RANGE) != 0;
125
126       is_member = is_range ? is_valid_range : is_member;
127     }
128
129   if (insert)
130     {
131       *insert = ! is_member;
132       if (! is_member)
133         {
134           uword j;
135           w |= b;
136           h->is_member_bitmap[i] = w;
137           for (j = i + 1; j < vec_len (h->member_counts); j++)
138             h->member_counts[j] += 1;
139         }
140
141       return 1 + d;
142     }
143
144   d = is_member ? d : 0;
145
146   return is_member + d;
147 }
148
149 always_inline uword
150 sparse_vec_index (void * v, uword sparse_index)
151 {
152   return sparse_vec_index_internal (v, sparse_index,
153                                     /* maybe range */ 0,
154                                     /* insert? */ 0);
155 }
156                                     
157 always_inline void
158 sparse_vec_index2 (void * v,
159                    u32 si0, u32 si1,
160                    u32 * i0_return, u32 * i1_return)
161 {
162   sparse_vec_header_t * h;
163   uword b0, b1, w0, w1, v0, v1;
164   u32 i0, i1, d0, d1;
165   u8 is_member0, is_member1;
166
167   h = sparse_vec_header (v);
168
169   i0 = si0 / BITS (h->is_member_bitmap[0]);
170   i1 = si1 / BITS (h->is_member_bitmap[0]);
171
172   b0 = (uword) 1 << (uword) (si0 % BITS (h->is_member_bitmap[0]));
173   b1 = (uword) 1 << (uword) (si1 % BITS (h->is_member_bitmap[0]));
174
175   ASSERT (i0 < vec_len (h->is_member_bitmap));
176   ASSERT (i1 < vec_len (h->is_member_bitmap));
177
178   ASSERT (i0 < vec_len (h->member_counts));
179   ASSERT (i1 < vec_len (h->member_counts));
180
181   w0 = h->is_member_bitmap[i0];
182   w1 = h->is_member_bitmap[i1];
183
184   v0 = w0 & (b0 - 1);
185   v1 = w1 & (b1 - 1);
186
187   /* Speculate that masks will have zero or one bits set. */
188   d0 = h->member_counts[i0] + (v0 != 0);
189   d1 = h->member_counts[i1] + (v1 != 0);
190
191   /* Validate speculation. */
192   if (PREDICT_FALSE (! is_pow2 (v0) || ! is_pow2 (v1)))
193     {
194       d0 += count_set_bits (v0) - (v0 != 0);
195       d1 += count_set_bits (v1) - (v1 != 0);
196     }
197
198   is_member0 = (w0 & b0) != 0;
199   is_member1 = (w1 & b1) != 0;
200
201   d0 = is_member0 ? d0 : 0;
202   d1 = is_member1 ? d1 : 0;
203
204   *i0_return = is_member0 + d0;
205   *i1_return = is_member1 + d1;
206 }
207
208 #define sparse_vec_free(v) vec_free(v)
209
210 #define sparse_vec_elt_at_index(v,i) \
211   vec_elt_at_index ((v), sparse_vec_index ((v), (i)))
212
213 #define sparse_vec_validate(v,i)                                        \
214 ({                                                                      \
215   uword _i;                                                             \
216   u32 _insert;                                                          \
217                                                                         \
218   if (! (v))                                                            \
219     (v) = sparse_vec_new (sizeof ((v)[0]), BITS (u16));                 \
220                                                                         \
221   _i = sparse_vec_index_internal ((v), (i),                             \
222                                   /* maybe range */ 0,                  \
223                                   /* insert? */ &_insert);              \
224   if (_insert)                                                          \
225     vec_insert_ha ((v), 1, _i,                                          \
226                    /* header size */ sizeof (sparse_vec_header_t),      \
227                    /* align */ 0);                                      \
228                                                                         \
229   /* Invalid index is 0. */                                             \
230   ASSERT (_i > 0);                                                      \
231                                                                         \
232   (v) + _i;                                                             \
233 })
234
235 #endif /* included_sparse_vec_h */