mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-31 19:06:14 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			709 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			709 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|    Copyright (c) 2001, 2011, Oracle and/or its affiliates.
 | |
|    Copyright (C) 2009- 2011 Monty Program Ab
 | |
| 
 | |
|    This program is free software; you can redistribute it and/or modify
 | |
|    it under the terms of the GNU General Public License as published by
 | |
|    the Free Software Foundation; version 2 of the License.
 | |
| 
 | |
|    This program is distributed in the hope that it will be useful,
 | |
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|    GNU General Public License for more details.
 | |
| 
 | |
|    You should have received a copy of the GNU General Public License
 | |
|    along with this program; if not, write to the Free Software
 | |
|    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335
 | |
|    USA
 | |
|  */
 | |
| 
 | |
| /*
 | |
|   Handling of my_bitmap_map (ulonglong) arrays as large bitmaps.
 | |
| 
 | |
|   API limitations (or, rather asserted safety assumptions,
 | |
|   to encourage correct programming)
 | |
| 
 | |
|     * the internal storage is a set of 64 bit words
 | |
|     * the number of bits specified in creation can be any number > 0
 | |
| 
 | |
|   Implementation notes:
 | |
|     * MY_BITMAP includes a pointer, last_word_ptr, to the last word.
 | |
|      The implication is that if one copies bitmaps to another memory
 | |
|      location, one has to call create_last_bit_mask() on the bitmap to
 | |
|      fix the internal pointer.
 | |
|    * The not used part of a the last word should always be 0.
 | |
|      This avoids special handling of the last bitmap in several cases.
 | |
|      This is checked for most calls to bitmap functions.
 | |
| 
 | |
|   TODO:
 | |
|   Make assembler thread safe versions of these using test-and-set instructions
 | |
| 
 | |
|   Original version created by Sergei Golubchik 2001 - 2004.
 | |
|   New version written and test program added and some changes to the interface
 | |
|   was made by Mikael Ronstrom 2005, with assistance of Tomas Ulin and Mats
 | |
|   Kindahl.
 | |
|   Updated to 64 bits and use my_find_first_bit() to speed up
 | |
|   bitmap_get_next_set() by Monty in 2024
 | |
| */
 | |
| 
 | |
| #include "mysys_priv.h"
 | |
| #include <my_bitmap.h>
 | |
| #include <m_string.h>
 | |
| #include <my_bit.h>
 | |
| #include <my_byteorder.h>
 | |
| 
 | |
| 
 | |
| /* Defines to check bitmaps */
 | |
| 
 | |
| #define DBUG_ASSERT_BITMAP(M) \
 | |
|   DBUG_ASSERT((M)->bitmap); \
 | |
|   DBUG_ASSERT((M)->n_bits > 0); \
 | |
|   DBUG_ASSERT((M)->last_word_ptr == (M)->bitmap + no_words_in_map(M)-1); \
 | |
|   DBUG_ASSERT((*(M)->last_word_ptr & (M)->last_bit_mask) == 0);
 | |
| 
 | |
| #define DBUG_ASSERT_BITMAP_AND_BIT(M,B) \
 | |
|   DBUG_ASSERT_BITMAP(M); \
 | |
|   DBUG_ASSERT((B) < (M)->n_bits);
 | |
| 
 | |
| #define DBUG_ASSERT_DIFFERENT_BITMAPS(M,N) \
 | |
|   DBUG_ASSERT_BITMAP(M); \
 | |
|   DBUG_ASSERT_BITMAP(N);
 | |
| 
 | |
| #define DBUG_ASSERT_IDENTICAL_BITMAPS(M,N) \
 | |
|   DBUG_ASSERT_BITMAP(M); \
 | |
|   DBUG_ASSERT_BITMAP(N); \
 | |
|   DBUG_ASSERT((M)->n_bits == (N)->n_bits);
 | |
| 
 | |
| /*
 | |
|   Create a mask for the usable bits on the LAST my_bitmap_map position for
 | |
|   a bitmap with 'bits' number of bits.
 | |
| 
 | |
|   The lowest 'bits' bits are set to zero and the rest bits are set to 1.
 | |
|   For (bits & 63) == 0 , 0 is returned as in this case all bits in the
 | |
|   my_bitmap_position are significant. (This example assumes the
 | |
|   storage is ulonglong).
 | |
| 
 | |
|   For 'bits & 63' it will return values from the series
 | |
|   0, 0xfffffffffffffffe,.... 0x8000000000000000
 | |
| */
 | |
| 
 | |
| static inline my_bitmap_map last_bit_mask(uint bits)
 | |
| {
 | |
|   uint bits_in_last_map= (bits & (my_bitmap_map_bits-1));
 | |
|   return bits_in_last_map ? ~((1ULL << bits_in_last_map)-1) : 0ULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Get a mask of the bits that are to be considered as 'on' at location
 | |
|   starting with 'bits'.
 | |
|   This function has _inv in it's name as it's usage is invers compared
 | |
|   to last_bit_mask().
 | |
| 
 | |
|   For (bits & 63) it will return values from the series
 | |
|   0xffffffffffffffff, 0xfffffffffffffffe,.... 0x8000000000000000
 | |
| */
 | |
| 
 | |
| static inline my_bitmap_map first_bit_mask_inv(uint bits)
 | |
| {
 | |
|   uint bits_in_last_map= (bits & (my_bitmap_map_bits-1));
 | |
|   return ~((1ULL << bits_in_last_map)-1);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Update the bitmap's last_word_ptr and last_bit_mask
 | |
|   Also ensure that the last world is all zero to make it
 | |
|   easy to find the next set bit.
 | |
| 
 | |
|   Note that if n_bits is 0, then last_word_ptr will point to
 | |
|   bitmap (safely). The bitmap will not be usable for almost any operation.
 | |
| */
 | |
| 
 | |
| void create_last_bit_mask(MY_BITMAP *map)
 | |
| {
 | |
|   my_bitmap_map mask= last_bit_mask(map->n_bits);
 | |
|   map->last_bit_mask= mask;
 | |
|   map->last_word_ptr= map->bitmap + MY_MAX(no_words_in_map(map),1) -1;
 | |
|   if (map->n_bits > 0)
 | |
|   {
 | |
|     *map->last_word_ptr&= ~mask;           /* Set not used bits to 0 */
 | |
|     DBUG_ASSERT_BITMAP(map);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Initialize a bitmap object. All bits will be set to zero
 | |
| */
 | |
| 
 | |
| my_bool my_bitmap_init(MY_BITMAP *map, my_bitmap_map *buf, uint n_bits)
 | |
| {
 | |
|   DBUG_ENTER("my_bitmap_init");
 | |
| 
 | |
|   if (!buf)
 | |
|   {
 | |
|     uint size_in_bytes= bitmap_buffer_size(n_bits);
 | |
|     if (!(buf= (my_bitmap_map*) my_malloc(key_memory_MY_BITMAP_bitmap,
 | |
|                                           size_in_bytes, MYF(MY_WME))))
 | |
|     {
 | |
|       map->bitmap= 0;
 | |
|       DBUG_RETURN(1);
 | |
|     }
 | |
|     map->bitmap_allocated= 1;
 | |
|   }
 | |
|   else
 | |
|     map->bitmap_allocated= 0;
 | |
| 
 | |
|   map->bitmap= buf;
 | |
|   map->n_bits= n_bits;
 | |
|   create_last_bit_mask(map);
 | |
|   bitmap_clear_all(map);
 | |
|   DBUG_RETURN(0);
 | |
| }
 | |
| 
 | |
| 
 | |
| void my_bitmap_free(MY_BITMAP *map)
 | |
| {
 | |
|   DBUG_ENTER("my_bitmap_free");
 | |
|   if (map->bitmap)
 | |
|   {
 | |
|     if (map->bitmap_allocated)
 | |
|       my_free(map->bitmap);
 | |
|     map->bitmap=0;
 | |
|   }
 | |
|   DBUG_VOID_RETURN;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   test if bit already set and set it if it was not (thread unsafe method)
 | |
| 
 | |
|   SYNOPSIS
 | |
|     bitmap_fast_test_and_set()
 | |
|     MAP   bit map struct
 | |
|     BIT   bit number
 | |
| 
 | |
|   RETURN
 | |
|     0    bit was not set
 | |
|     !=0  bit was set
 | |
| */
 | |
| 
 | |
| my_bool bitmap_fast_test_and_set(MY_BITMAP *map, uint bitmap_bit)
 | |
| {
 | |
|   my_bitmap_map *value, bit, res;
 | |
|   DBUG_ASSERT_BITMAP_AND_BIT(map, bitmap_bit);
 | |
| 
 | |
|   value= map->bitmap + (bitmap_bit/my_bitmap_map_bits);
 | |
|   bit= 1ULL << (bitmap_bit & (my_bitmap_map_bits-1));
 | |
|   res= *value & bit;
 | |
|   *value|= bit;
 | |
|   return MY_TEST(res);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   test if bit already set and set it if it was not (thread safe method)
 | |
| 
 | |
|   SYNOPSIS
 | |
|     bitmap_fast_test_and_set()
 | |
|     map          bit map struct
 | |
|     bitmap_bit   bit number
 | |
| 
 | |
|   RETURN
 | |
|     0    bit was not set
 | |
|     !=0  bit was set
 | |
| */
 | |
| 
 | |
| my_bool bitmap_test_and_set(MY_BITMAP *map, uint bitmap_bit)
 | |
| {
 | |
|   DBUG_ASSERT_BITMAP_AND_BIT(map, bitmap_bit);
 | |
|   return bitmap_fast_test_and_set(map, bitmap_bit);
 | |
| }
 | |
| 
 | |
| /*
 | |
|   test if bit already set and clear it if it was set(thread unsafe method)
 | |
| 
 | |
|   SYNOPSIS
 | |
|     bitmap_fast_test_and_set()
 | |
|     MAP   bit map struct
 | |
|     BIT   bit number
 | |
| 
 | |
|   RETURN
 | |
|     0    bit was not set
 | |
|     !=0  bit was set
 | |
| */
 | |
| 
 | |
| my_bool bitmap_fast_test_and_clear(MY_BITMAP *map, uint bitmap_bit)
 | |
| {
 | |
|   my_bitmap_map *value, bit, res;
 | |
|   DBUG_ASSERT_BITMAP_AND_BIT(map, bitmap_bit);
 | |
| 
 | |
|   value= map->bitmap + (bitmap_bit/my_bitmap_map_bits);
 | |
|   bit= 1ULL << (bitmap_bit & (my_bitmap_map_bits-1));
 | |
|   res= *value & bit;
 | |
|   *value&= ~bit;
 | |
|   return MY_TEST(res);
 | |
| }
 | |
| 
 | |
| 
 | |
| my_bool bitmap_test_and_clear(MY_BITMAP *map, uint bitmap_bit)
 | |
| {
 | |
|   DBUG_ASSERT_BITMAP_AND_BIT(map, bitmap_bit);
 | |
|   return bitmap_fast_test_and_clear(map, bitmap_bit);
 | |
| }
 | |
| 
 | |
| 
 | |
| uint bitmap_set_next(MY_BITMAP *map)
 | |
| {
 | |
|   uint bit_found;
 | |
|   DBUG_ASSERT_BITMAP(map);
 | |
|   if ((bit_found= bitmap_get_first_clear(map)) != MY_BIT_NONE)
 | |
|     bitmap_set_bit(map, bit_found);
 | |
|   return bit_found;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Set the specified number of bits in the bitmap buffer.
 | |
| 
 | |
|   @param map         [IN]       Bitmap
 | |
|   @param prefix_size [IN]       Number of bits to be set or (uint) ~0 for all
 | |
| */
 | |
| 
 | |
| void bitmap_set_prefix(MY_BITMAP *map, uint prefix_size)
 | |
| {
 | |
|   uint prefix, prefix_bits;
 | |
|   my_bitmap_map *value= map->bitmap;
 | |
|   DBUG_ASSERT_BITMAP(map);
 | |
|   DBUG_ASSERT(prefix_size <= map->n_bits || prefix_size == (uint) ~0);
 | |
|   set_if_smaller(prefix_size, map->n_bits);
 | |
| 
 | |
|   if ((prefix= prefix_size / my_bitmap_map_bits))
 | |
|   {
 | |
|     my_bitmap_map *end= value+prefix;
 | |
|     do
 | |
|     {
 | |
|       *value++= ~(my_bitmap_map) 0;
 | |
|     } while (value < end);
 | |
|   }
 | |
|   if ((prefix_bits= prefix_size & (my_bitmap_map_bits-1)))
 | |
|     *value++= (1ULL << prefix_bits)-1;
 | |
|   while (value <= map->last_word_ptr)
 | |
|     *value++= 0;
 | |
|   DBUG_ASSERT_BITMAP(map);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|    Check if bitmap is a bitmap of prefix bits set in the beginning
 | |
| 
 | |
|    @param map          bitmap
 | |
|    @param prefix_size  number of bits that should be set. 0 is allowed.
 | |
| 
 | |
|    @return 1           Yes, prefix bits where set or prefix_size == 0.
 | |
|    @return 0           No
 | |
| */
 | |
| 
 | |
| my_bool bitmap_is_prefix(const MY_BITMAP *map, uint prefix_size)
 | |
| {
 | |
|   my_bitmap_map *value= map->bitmap;
 | |
|   my_bitmap_map *end=  value+ (prefix_size/my_bitmap_map_bits);
 | |
|   uint prefix_bits;
 | |
| 
 | |
|   /* Empty prefix is always true */
 | |
|   if (!prefix_size)
 | |
|     return 1;
 | |
| 
 | |
|   DBUG_ASSERT_BITMAP_AND_BIT(map, prefix_size-1);
 | |
| 
 | |
|   while (value < end)
 | |
|     if (*value++ != ~(my_bitmap_map) 0)
 | |
|       return 0;
 | |
| 
 | |
|   if ((prefix_bits= prefix_size & (my_bitmap_map_bits-1)))
 | |
|   {
 | |
|     if (*value++ != (1ULL << prefix_bits)-1)
 | |
|       return 0;
 | |
|   }
 | |
|   end= map->last_word_ptr;
 | |
|   while (value <= end)
 | |
|     if (*value++ != 0)
 | |
|       return 0;
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| 
 | |
| my_bool bitmap_is_set_all(const MY_BITMAP *map)
 | |
| {
 | |
|   my_bitmap_map *data_ptr= map->bitmap;
 | |
|   my_bitmap_map *end= map->last_word_ptr;
 | |
|   DBUG_ASSERT_BITMAP(map);
 | |
| 
 | |
|   for (; data_ptr < end; data_ptr++)
 | |
|     if (*data_ptr != ~(my_bitmap_map)0)
 | |
|       return FALSE;
 | |
|   return (*data_ptr | map->last_bit_mask) == ~(my_bitmap_map)0;
 | |
| }
 | |
| 
 | |
| 
 | |
| my_bool bitmap_is_clear_all(const MY_BITMAP *map)
 | |
| {
 | |
|   my_bitmap_map *data_ptr= map->bitmap;
 | |
|   my_bitmap_map *end= map->last_word_ptr;
 | |
|   DBUG_ASSERT_BITMAP(map);
 | |
| 
 | |
|   for (; data_ptr <= end; data_ptr++)
 | |
|     if (*data_ptr)
 | |
|       return FALSE;
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Return TRUE if map1 is a subset of map2 */
 | |
| 
 | |
| my_bool bitmap_is_subset(const MY_BITMAP *map1, const MY_BITMAP *map2)
 | |
| {
 | |
|   my_bitmap_map *m1= map1->bitmap, *m2= map2->bitmap, *end= map1->last_word_ptr;
 | |
|   DBUG_ASSERT_IDENTICAL_BITMAPS(map1,map2);
 | |
| 
 | |
|   while (m1 <= end)
 | |
|   {
 | |
|     if ((*m1++) & ~(*m2++))
 | |
|       return 0;
 | |
|   }
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| /* True if bitmaps has any common bits */
 | |
| 
 | |
| my_bool bitmap_is_overlapping(const MY_BITMAP *map1, const MY_BITMAP *map2)
 | |
| {
 | |
|   my_bitmap_map *m1= map1->bitmap, *m2= map2->bitmap, *end= map1->last_word_ptr;
 | |
|   DBUG_ASSERT_IDENTICAL_BITMAPS(map1,map2);
 | |
| 
 | |
|   while (m1 <= end)
 | |
|   {
 | |
|     if ((*m1++) & (*m2++))
 | |
|       return 1;
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Create intersection of two bitmaps
 | |
| 
 | |
|   @param map    map1. Result is stored here
 | |
|   @param map2   map2
 | |
| */
 | |
| 
 | |
| void bitmap_intersect(MY_BITMAP *map, const MY_BITMAP *map2)
 | |
| {
 | |
|   my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end;
 | |
|   uint len= no_words_in_map(map), len2 = no_words_in_map(map2);
 | |
|   DBUG_ASSERT_DIFFERENT_BITMAPS(map,map2);
 | |
| 
 | |
|   end= to+MY_MIN(len,len2);
 | |
|   while (to < end)
 | |
|     *to++ &= *from++;
 | |
| 
 | |
|   if (len2 <= len)
 | |
|   {
 | |
|     to[-1]&= ~map2->last_bit_mask; /* Clear last not relevant bits */
 | |
|     end+= len-len2;
 | |
|     while (to < end)
 | |
|       *to++= 0;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Check if there is some bit index between start_bit and end_bit, such that
 | |
|   this is at least on bit that set for all bitmaps in bitmap_list.
 | |
| 
 | |
|   SYNOPSIS
 | |
|     bitmap_exists_intersection()
 | |
|     bitmpap_array [in]  a set of MY_BITMAPs
 | |
|     bitmap_count  [in]  number of elements in bitmap_array
 | |
|     start_bit     [in]  beginning (inclusive) of the range of bits to search
 | |
|     end_bit       [in]  end (inclusive) of the range of bits to search, must be
 | |
|                         no bigger than the bits of the shortest bitmap.
 | |
| 
 | |
|   RETURN
 | |
|     TRUE  if an intersecion exists
 | |
|     FALSE no intersection
 | |
| */
 | |
| 
 | |
| my_bool bitmap_exists_intersection(MY_BITMAP **bitmap_array,
 | |
|                                    uint bitmap_count,
 | |
|                                    uint start_bit, uint end_bit)
 | |
| {
 | |
|   uint i, j, start_idx, end_idx;
 | |
|   my_bitmap_map cur_res, first_map;
 | |
| 
 | |
|   DBUG_ASSERT(bitmap_count);
 | |
|   DBUG_ASSERT(end_bit >= start_bit);
 | |
|   for (j= 0; j < bitmap_count; j++)
 | |
|   {
 | |
|     DBUG_ASSERT_BITMAP_AND_BIT(bitmap_array[j], end_bit);
 | |
|   }
 | |
| 
 | |
|   start_idx= start_bit/8/sizeof(my_bitmap_map);
 | |
|   end_idx= end_bit/8/sizeof(my_bitmap_map);
 | |
| 
 | |
|   first_map= first_bit_mask_inv(start_bit);
 | |
|   cur_res= first_map;
 | |
|   for (i= start_idx; i < end_idx; i++)
 | |
|   {
 | |
|     for (j= 0; cur_res && j < bitmap_count; j++)
 | |
|       cur_res &= bitmap_array[j]->bitmap[i];
 | |
|     if (cur_res)
 | |
|       return TRUE;
 | |
|     cur_res= ~(my_bitmap_map) 0;
 | |
|   }
 | |
|   cur_res= ~last_bit_mask(end_bit+1);
 | |
|   if (start_idx == end_idx)
 | |
|     cur_res&= first_map;
 | |
|   for (j= 0; cur_res && j < bitmap_count; j++)
 | |
|     cur_res &= bitmap_array[j]->bitmap[end_idx];
 | |
|   return cur_res != 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* True if union of bitmaps have all bits set */
 | |
| 
 | |
| my_bool bitmap_union_is_set_all(const MY_BITMAP *map1, const MY_BITMAP *map2)
 | |
| {
 | |
|   my_bitmap_map *m1= map1->bitmap, *m2= map2->bitmap, *end= map1->last_word_ptr;
 | |
|   DBUG_ASSERT_IDENTICAL_BITMAPS(map1,map2);
 | |
| 
 | |
|   while ( m1 < end)
 | |
|     if ((*m1++ | *m2++) != ~(my_bitmap_map)0)
 | |
|       return FALSE;
 | |
|   /* here both maps have the same number of bits - see assert above */
 | |
|   return ((*m1 | *m2 | map1->last_bit_mask) != ~(my_bitmap_map)0);
 | |
| }
 | |
| 
 | |
| 
 | |
| void bitmap_subtract(MY_BITMAP *map, const MY_BITMAP *map2)
 | |
| {
 | |
|   my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end= map->last_word_ptr;
 | |
|   DBUG_ASSERT_IDENTICAL_BITMAPS(map,map2);
 | |
| 
 | |
|   while (to <= end)
 | |
|     *to++ &= ~(*from++);
 | |
| }
 | |
| 
 | |
| 
 | |
| void bitmap_union(MY_BITMAP *map, const MY_BITMAP *map2)
 | |
| {
 | |
|   my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end= map->last_word_ptr;
 | |
|   DBUG_ASSERT_IDENTICAL_BITMAPS(map,map2);
 | |
| 
 | |
|   while (to <= end)
 | |
|     *to++ |= *from++;
 | |
| }
 | |
| 
 | |
| 
 | |
| void bitmap_xor(MY_BITMAP *map, const MY_BITMAP *map2)
 | |
| {
 | |
|   my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end= map->last_word_ptr;
 | |
|   DBUG_ASSERT_IDENTICAL_BITMAPS(map,map2);
 | |
| 
 | |
|   while (to <= end)
 | |
|     *to++ ^= *from++;
 | |
| }
 | |
| 
 | |
| 
 | |
| void bitmap_invert(MY_BITMAP *map)
 | |
| {
 | |
|   my_bitmap_map *to= map->bitmap, *end= map->last_word_ptr;
 | |
|   DBUG_ASSERT_BITMAP(map);
 | |
| 
 | |
|   while (to < end)
 | |
|     *to++ ^= ~(my_bitmap_map)0;
 | |
| 
 | |
|   *to ^= (~(my_bitmap_map)0 & ~map->last_bit_mask);
 | |
|   DBUG_ASSERT_BITMAP(map);
 | |
| }
 | |
| 
 | |
| 
 | |
| uint bitmap_bits_set(const MY_BITMAP *map)
 | |
| {
 | |
|   my_bitmap_map *data_ptr= map->bitmap;
 | |
|   my_bitmap_map *end= map->last_word_ptr;
 | |
|   uint res= 0;
 | |
|   DBUG_ASSERT_BITMAP(map);
 | |
| 
 | |
|   for (; data_ptr <= end; data_ptr++)
 | |
|     res+= my_count_bits(*data_ptr);
 | |
| 
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Copy bitmaps (if source bigger tail is left unchanged)
 | |
| 
 | |
|   @param map    to-bitmap
 | |
|   @param map2   from-bitmap
 | |
| 
 | |
|   @notes
 | |
|   Code will work even of the bitmaps are of different size.
 | |
|   In this case, only up to to->n_bits will be copied.
 | |
| */
 | |
| 
 | |
| void bitmap_copy(MY_BITMAP *map, const MY_BITMAP *map2)
 | |
| {
 | |
|   my_bitmap_map *to= map->bitmap, *from= map2->bitmap, *end;
 | |
|   uint len= no_words_in_map(map), len2 = no_words_in_map(map2);
 | |
|   DBUG_ASSERT_DIFFERENT_BITMAPS(map, map2);
 | |
| 
 | |
|   end= to + MY_MIN(len, len2 - 1);
 | |
|   while (to < end)
 | |
|     *to++ = *from++;
 | |
| 
 | |
|   if (len2 <= len)
 | |
|     *to= (*from & ~map2->last_bit_mask) | (*to & map2->last_bit_mask);
 | |
|   *(map->last_word_ptr)&= ~map->last_bit_mask;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Find first set bit in the bitmap
 | |
| */
 | |
| 
 | |
| uint bitmap_get_first_set(const MY_BITMAP *map)
 | |
| {
 | |
|   my_bitmap_map *data_ptr= map->bitmap, *end= map->last_word_ptr;
 | |
|   DBUG_ASSERT_BITMAP(map);
 | |
| 
 | |
|   for (uint i=0; data_ptr <= end; data_ptr++, i++)
 | |
|     if (*data_ptr)
 | |
|       return my_find_first_bit(*data_ptr) + i * sizeof(my_bitmap_map)*8;
 | |
|   return MY_BIT_NONE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Get the next set bit.
 | |
| 
 | |
|   @param  map         Bitmap
 | |
|   @param  bitmap_bit  Bit to start search from
 | |
| 
 | |
|   @return Index to first bit set after bitmap_bit
 | |
| */
 | |
| 
 | |
| uint bitmap_get_next_set(const MY_BITMAP *map, uint bitmap_bit)
 | |
| {
 | |
|   uint word_pos;
 | |
|   my_bitmap_map first_word, *data_ptr, *end= map->last_word_ptr;
 | |
|   DBUG_ASSERT_BITMAP(map);
 | |
| 
 | |
|   /* Look for the next bit */
 | |
|   bitmap_bit++;
 | |
|   if (bitmap_bit >= map->n_bits)
 | |
|     return MY_BIT_NONE;
 | |
| 
 | |
|   word_pos= bitmap_bit / 64;
 | |
|   data_ptr= map->bitmap + word_pos;
 | |
| 
 | |
|   first_word= *data_ptr & first_bit_mask_inv(bitmap_bit);
 | |
| 
 | |
|   if (first_word)
 | |
|   {
 | |
|     /* Optimize common case when most bits are set */
 | |
|     if (first_word & (1ULL << ((bitmap_bit & (my_bitmap_map_bits-1)))))
 | |
|       return bitmap_bit;
 | |
|     return my_find_first_bit(first_word) + (bitmap_bit & ~(my_bitmap_map_bits-1));
 | |
|   }
 | |
| 
 | |
|   for (data_ptr++; data_ptr <= end; data_ptr++)
 | |
|   {
 | |
|     bitmap_bit+= 64;
 | |
|     if (*data_ptr)
 | |
|       return my_find_first_bit(*data_ptr) + (bitmap_bit & ~(my_bitmap_map_bits-1));
 | |
|   }
 | |
|   return MY_BIT_NONE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Get first clear bit */
 | |
| 
 | |
| uint bitmap_get_first_clear(const MY_BITMAP *map)
 | |
| {
 | |
|   uint i;
 | |
|   my_bitmap_map *data_ptr= map->bitmap, *end= map->last_word_ptr;
 | |
|   DBUG_ASSERT_BITMAP(map);
 | |
| 
 | |
|   for (i= 0; data_ptr < end; data_ptr++, i++)
 | |
|     if (*data_ptr != ~(my_bitmap_map)0)
 | |
|       goto found;
 | |
|   if ((*data_ptr | map->last_bit_mask) == ~(my_bitmap_map)0)
 | |
|     return MY_BIT_NONE;
 | |
| found:
 | |
|   /* find first zero bit by reverting all bits and find first bit */
 | |
|   return my_find_first_bit(~*data_ptr) + i * sizeof(my_bitmap_map)*8;
 | |
| }
 | |
| /*
 | |
|   Functions to export/import bitmaps to an architecture independent format
 | |
|   (low_byte_first)
 | |
| */
 | |
| 
 | |
| #ifdef WORDS_BIGENDIAN
 | |
| /* Big endian machines, like powerpc or s390x */
 | |
| 
 | |
| void bitmap_export(uchar *to, MY_BITMAP *map)
 | |
| {
 | |
|   my_bitmap_map *value;
 | |
|   uint length;
 | |
|   uchar buff[my_bitmap_map_bytes];
 | |
| 
 | |
|   for (value= map->bitmap ; value < map->last_word_ptr ; value++)
 | |
|   {
 | |
|     int8store(to, *value);
 | |
|     to+= 8;
 | |
|   }
 | |
|   int8store(buff, *value);
 | |
| 
 | |
|   /* We want length & 7 to return a serie 8,2,3,4,5,6,7, 8,2,3,... */
 | |
|   length= 1+ ((no_bytes_in_export_map(map) + 7) & 7);
 | |
|   memcpy(to, buff, length);
 | |
| }
 | |
| 
 | |
| 
 | |
| void bitmap_import(MY_BITMAP *map, uchar *from)
 | |
| {
 | |
|   my_bitmap_map *value;
 | |
|   uint length;
 | |
|   uchar buff[my_bitmap_map_bytes];
 | |
| 
 | |
|   for (value= map->bitmap ; value < map->last_word_ptr ; value++)
 | |
|   {
 | |
|     *value= uint8korr(from);
 | |
|     from+= 8;
 | |
|   }
 | |
|   bzero(buff, sizeof(buff));
 | |
| 
 | |
|   /* We want length & 7 to return a serie 8,2,3,4,5,6,7, 8,2,3,... */
 | |
|   length= 1+ ((no_bytes_in_export_map(map) + 7) & 7);
 | |
|   memcpy(buff, from, length);
 | |
|   *value= uint8korr(buff) & ~map->last_bit_mask;
 | |
| }
 | |
| 
 | |
| #else
 | |
| 
 | |
| /* Little endian machines, like intel and amd */
 | |
| 
 | |
| void bitmap_export(uchar *to, MY_BITMAP *map)
 | |
| {
 | |
|   memcpy(to, (uchar*) map->bitmap, no_bytes_in_export_map(map));
 | |
| }
 | |
| 
 | |
| void bitmap_import(MY_BITMAP *map, uchar *from)
 | |
| {
 | |
|   memcpy((uchar*) map->bitmap, from, no_bytes_in_export_map(map));
 | |
|   *map->last_word_ptr&= ~map->last_bit_mask;
 | |
| }
 | |
| #endif /* WORDS_BIGENDIAN */
 | 
