| /* |
| * Copyright (c) 2015 Cisco and/or its affiliates. |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at: |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| /* |
| Copyright (c) 2005 Eliot Dresselhaus |
| |
| Permission is hereby granted, free of charge, to any person obtaining |
| a copy of this software and associated documentation files (the |
| "Software"), to deal in the Software without restriction, including |
| without limitation the rights to use, copy, modify, merge, publish, |
| distribute, sublicense, and/or sell copies of the Software, and to |
| permit persons to whom the Software is furnished to do so, subject to |
| the following conditions: |
| |
| The above copyright notice and this permission notice shall be |
| included in all copies or substantial portions of the Software. |
| |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
| LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
| OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| |
| #ifndef included_sparse_vec_h |
| #define included_sparse_vec_h |
| |
| #include <vppinfra/clib.h> |
| #include <vppinfra/vec.h> |
| |
| /* Sparsely indexed vectors. Basic idea taken from Hacker's delight. |
| Eliot added ranges. */ |
| typedef struct |
| { |
| /* Bitmap one for each sparse index. */ |
| uword *is_member_bitmap; |
| |
| /* member_counts[i] = total number of members with j < i. */ |
| u16 *member_counts; |
| |
| #define SPARSE_VEC_IS_RANGE (1 << 0) |
| #define SPARSE_VEC_IS_VALID_RANGE (1 << 1) |
| u8 *range_flags; |
| } sparse_vec_header_t; |
| |
| always_inline sparse_vec_header_t * |
| sparse_vec_header (void *v) |
| { |
| return vec_header (v); |
| } |
| |
| /* Index 0 is always used to mark indices that are not valid in |
| sparse vector. For example, you look up V[0x1234] and 0x1234 is not |
| known you'll get 0 back as an index. */ |
| #define SPARSE_VEC_INVALID_INDEX (0) |
| |
| always_inline void * |
| sparse_vec_new (uword elt_bytes, uword sparse_index_bits) |
| { |
| void *v; |
| sparse_vec_header_t *h; |
| word n; |
| |
| ASSERT (sparse_index_bits <= 16); |
| |
| v = _vec_resize ((void *) 0, |
| /* length increment */ 8, |
| /* data bytes */ 8 * elt_bytes, |
| /* header bytes */ sizeof (h[0]), |
| /* data align */ 0); |
| |
| /* Make space for invalid entry (entry 0). */ |
| _vec_len (v) = 1; |
| |
| h = sparse_vec_header (v); |
| |
| n = sparse_index_bits - min_log2 (BITS (uword)); |
| if (n < 0) |
| n = 0; |
| n = 1ULL << n; |
| vec_resize (h->is_member_bitmap, n); |
| vec_resize (h->member_counts, n); |
| |
| return v; |
| } |
| |
| always_inline uword |
| sparse_vec_index_internal (void *v, |
| uword sparse_index, |
| uword maybe_range, u32 * insert) |
| { |
| sparse_vec_header_t *h; |
| uword i, b, d, w; |
| u8 is_member; |
| |
| h = sparse_vec_header (v); |
| i = sparse_index / BITS (h->is_member_bitmap[0]); |
| b = sparse_index % BITS (h->is_member_bitmap[0]); |
| |
| ASSERT (i < vec_len (h->is_member_bitmap)); |
| ASSERT (i < vec_len (h->member_counts)); |
| |
| w = h->is_member_bitmap[i]; |
| |
| /* count_trailing_zeros(0) == 0, take care of that case */ |
| if (PREDICT_FALSE (maybe_range == 0 && insert == 0 && w == 0)) |
| return 0; |
| |
| if (PREDICT_TRUE (maybe_range == 0 && insert == 0 && |
| count_trailing_zeros (w) == b)) |
| return h->member_counts[i] + 1; |
| |
| d = h->member_counts[i] + count_set_bits (w & ((1ULL << b) - 1)); |
| is_member = (w & (1ULL << b)) != 0; |
| |
| if (maybe_range) |
| { |
| u8 r = h->range_flags[d]; |
| u8 is_range, is_valid_range; |
| |
| is_range = maybe_range & (r & SPARSE_VEC_IS_RANGE); |
| is_valid_range = (r & SPARSE_VEC_IS_VALID_RANGE) != 0; |
| |
| is_member = is_range ? is_valid_range : is_member; |
| } |
| |
| if (insert) |
| { |
| *insert = !is_member; |
| if (!is_member) |
| { |
| uword j; |
| w |= 1ULL << b; |
| h->is_member_bitmap[i] = w; |
| for (j = i + 1; j < vec_len (h->member_counts); j++) |
| h->member_counts[j] += 1; |
| } |
| |
| return 1 + d; |
| } |
| |
| d = is_member ? d : 0; |
| |
| return is_member + d; |
| } |
| |
| always_inline uword |
| sparse_vec_index (void *v, uword sparse_index) |
| { |
| return sparse_vec_index_internal (v, sparse_index, |
| /* maybe range */ 0, |
| /* insert? */ 0); |
| } |
| |
| always_inline void |
| sparse_vec_index2 (void *v, |
| u32 si0, u32 si1, u32 * i0_return, u32 * i1_return) |
| { |
| sparse_vec_header_t *h; |
| uword b0, b1, w0, w1, v0, v1; |
| u32 i0, i1, d0, d1; |
| u8 is_member0, is_member1; |
| |
| h = sparse_vec_header (v); |
| |
| i0 = si0 / BITS (h->is_member_bitmap[0]); |
| i1 = si1 / BITS (h->is_member_bitmap[0]); |
| |
| b0 = si0 % BITS (h->is_member_bitmap[0]); |
| b1 = si1 % BITS (h->is_member_bitmap[0]); |
| |
| ASSERT (i0 < vec_len (h->is_member_bitmap)); |
| ASSERT (i1 < vec_len (h->is_member_bitmap)); |
| |
| ASSERT (i0 < vec_len (h->member_counts)); |
| ASSERT (i1 < vec_len (h->member_counts)); |
| |
| w0 = h->is_member_bitmap[i0]; |
| w1 = h->is_member_bitmap[i1]; |
| |
| if (PREDICT_TRUE ((count_trailing_zeros (w0) == b0) + |
| (count_trailing_zeros (w1) == b1) == 2)) |
| { |
| *i0_return = h->member_counts[i0] + 1; |
| *i1_return = h->member_counts[i1] + 1; |
| return; |
| } |
| |
| v0 = w0 & ((1ULL << b0) - 1); |
| v1 = w1 & ((1ULL << b1) - 1); |
| |
| /* Speculate that masks will have zero or one bits set. */ |
| d0 = h->member_counts[i0] + (v0 != 0); |
| d1 = h->member_counts[i1] + (v1 != 0); |
| |
| /* Validate speculation. */ |
| if (PREDICT_FALSE (!is_pow2 (v0) || !is_pow2 (v1))) |
| { |
| d0 += count_set_bits (v0) - (v0 != 0); |
| d1 += count_set_bits (v1) - (v1 != 0); |
| } |
| |
| is_member0 = (w0 & (1ULL << b0)) != 0; |
| is_member1 = (w1 & (1ULL << b1)) != 0; |
| |
| d0 = is_member0 ? d0 : 0; |
| d1 = is_member1 ? d1 : 0; |
| |
| *i0_return = is_member0 + d0; |
| *i1_return = is_member1 + d1; |
| } |
| |
| #define sparse_vec_free(V) \ |
| do \ |
| { \ |
| if (V) \ |
| { \ |
| clib_mem_free (sparse_vec_header (V)); \ |
| V = 0; \ |
| } \ |
| } \ |
| while (0) |
| |
| #define sparse_vec_elt_at_index(v,i) \ |
| vec_elt_at_index ((v), sparse_vec_index ((v), (i))) |
| |
| #define sparse_vec_validate(v,i) \ |
| ({ \ |
| uword _i; \ |
| u32 _insert; \ |
| \ |
| if (! (v)) \ |
| (v) = sparse_vec_new (sizeof ((v)[0]), BITS (u16)); \ |
| \ |
| _i = sparse_vec_index_internal ((v), (i), \ |
| /* maybe range */ 0, \ |
| /* insert? */ &_insert); \ |
| if (_insert) \ |
| vec_insert_ha ((v), 1, _i, \ |
| /* header size */ sizeof (sparse_vec_header_t), \ |
| /* align */ 0); \ |
| \ |
| /* Invalid index is 0. */ \ |
| ASSERT (_i > 0); \ |
| \ |
| (v) + _i; \ |
| }) |
| |
| #endif /* included_sparse_vec_h */ |
| |
| /* |
| * fd.io coding-style-patch-verification: ON |
| * |
| * Local Variables: |
| * eval: (c-set-style "gnu") |
| * End: |
| */ |