C # bit compression list

Source: Internet
Author: User
ArticleDirectory
    • 2.1 Delete refers to positioning
    • 2.2 Delete multiple specified locations
    • 2.3 Insert a single bit
    • 2.4 Insert Multiple Digits
    • 2.5 fill Multiple Digits
    • 2.6 displacement operation

. Net comes with a single-bit compressed array bitarray, which is also used in the same way as an array. It can only operate on each bit, but cannot be added or deleted. The bitlist class introduced here is the bit compression list that can add or delete bits.

I. Basic operations

The bit compression list uses uint [] to store bool data. A bool data is stored in a single bit of uint, therefore, the occupied space is only 1/8 of the bool [] (a bool occupies 1 byte and can store 8 bits ). You can also perform operations in the unit of uint during batch operations, which greatly improves the efficiency.

Figure 1 storage mode of a bit compressed list

Note: Here I put the uint [] header on the right, instead of the normal left, and the index is also 0 on the far right, increasing from right to left, this is to make the following operations easier.

Binary operations are required for element access in the bit compression list. Assume that you want to access the first bool, then the corresponding uint index is I/32 (that is, I> 5 ), corresponds to the I % 32 (I & 0x1f) IN THE uint ). AccessCodeThat is

 
Items [I> 5] & (I & 0x1f)

If the result is greater than 0, it indicates that the I-th bool is true; if the result is equal to 0, it indicates false.

If you want to set the I-th bit, you need to use simple and operation (&) and or operation (| ):

 
Items [I> 5] | = (1 <(I & 0x1f) // set the I to trueitems [I> 5 ~ (1 <(I & 0x1f) // set the I to false

Bitlist binary operations (with, or, not, exclusive, or) are also very simple. You only need to execute a binary operation on each element of uint [] at a time.

Ii. Other operations

The delete, insert, fill, and displacement operations are listed here, because these operations are difficult to implement and need to be described in detail.

2.1 Delete refers to positioning

First, let's take a simple case -- delete a special location. Assume that each box in Figure 2 in the following figure represents a uint. The I-bit in figure 2 (a) is the bit to be deleted, the blue part on the right and the green part on the left are other data. The deleted result is shown in 2 (c). The bits on the right (blue) remain unchanged, and the bits on the left (green) are shifted to the right. However, the bits on the left (green) may be multiple uint values. Therefore, the bits at the boundary (blue) must be processed here. These bits are the original bits of the uint on the left, now it is the highest bit of the right uint.

Figure 2 delete a single digit

The operation to delete a bit is divided into two steps. The first step is to delete the first bit, as shown in 2 (B. The specific method is to extract the highbits and lowbits of the uint where the I-bit is located, and then highbits> 1 | lowbits can delete the I-bit. The code for Extracting High and Low levels is as follows:

 
Uint spliter = 1u <(I & 0x1f); uint highbits = Value &~ (Spliter <1)-1u); uint lowbits = Value & (spliter-1u ); // spliter = 0000000010000000 // spliter <1 = 0000000100000000 // (spliter <1)-1 = 0000000011111111 //~ (Spliter <1)-1) = 1111111100000000 // spliter-1 = 0000000001111111

Step 2: shift the remaining places on the left (Green and Blue) to the right. The specific method is to place the cursor bit of the current uint to the highest bit of the right uint (by moving the 31 bits left), and shift the current uint to the right one bit, as shown in 2 (c. Code:

 
Int idx = (index> 5) + 1; int end = This. count> 5; For (; idx <= end; idx ++) {This. items [idx-1] | = This. items [idx] <31; this. items [idx] >>= 1 ;}

Complete code for this method:

Void removeitem (INT index) {int idx = index> indexshift; uint value = This. items [idx]; uint spliter = 1u <(index & indexmask); uint highbits = Value &~ (Spliter <1)-1u); uint lowbits = Value & (spliter-1u); this. items [idx] = (highbits> 1) | lowbits; int end = This. count> indexshift; For (idx ++; idx <= end; idx ++) {This. items [idx-1] | = This. items [idx] <31; this. items [idx] >>=1;} This. count --;}
2.2 Delete multiple specified locations

Deleting multiple locations is more difficult than deleting one location. Assume that the orange part starting from the first position in 3 (a) is the bit to be deleted, and the blue part on the right and the green part on the left are all other data. The deleted result is shown in 3 (c). The bit on the right remains unchanged, and the bit on the left is shifted to multiple places on the right.

Figure 3 Delete Multiple Digits

The delete operation is also divided into two steps. The first step is to delete the red part (set its length to length) and right shift the data in the same uint as the deleted part, 3 (B. In this step, the green data in the same uint as the deleted data is marked as highbits and lowbits respectively, where highbits is exactly the same as the blue part (called tailbits) A complete uint, while lowbits is the rest.

Pay special attention to some special cases. The highbits shown in 4 (A) is 0, the highbits and lowbits shown in Figure 4 (B) are both 0, and figure 4 (c) as shown in, lowbits is 0, so many conditions are required.

Figure 4 Special Cases of deleting multiple locations

The Code is as follows:

 Public void removerange (INT index, int length) {int valueidx = (index + length)> indexshift; int idx = index> indexshift; int tailsize = index & indexmask; int rsize = (index + length) & indexmask; if (rsize> 0) {uint value = This. items [valueidx]; int highsize = tailsize = 0? 0: uint32size-tailsize; int restsize = uint32size-rsize; If (highsize> restsize) {highsize = restsize;} If (highsize> 0) {uint tailmask = getmask (tailsize ); this. items [idx] = (this. items [idx] & tailmask) | (value> rsize) 
  
    highsize) {tailsize = restsize-highsize; items [idx] = value >>> (uint32size-T Ailsize) ;}valueidx ++;} // calculate the length of the data to be copied. Int Len = This. count-(valueidx 
   
     0) {This. copyitems (this. items, valueidx, idx, tailsize, Len);} This. count-= length ;}
   
  

the second step is to move the remaining uint forward, as shown in 3 (c). This step is equivalent to copying the uint [] Starting from I3 to the starting position from I2, the practice is to divide each uint into highbits and lowbits for replication. Note that the bits exceeding the length cannot be affected (mainly because the existing data is not affected when multiple bits are inserted later ). This code is very long, because I2 may be greater than 0, so we must ensure that the data before I2 is not affected, the situation becomes more complex, so I took out the first loop for determination, resulting in a doubling of the number of lines of code. In addition to the data length to be copied normally, the length parameter also includes a copy of lowsize (that is why the preceding code's Len adds a tailsize ), this is simply to make it easier to calculate the length here.

Private void copyitems (ilist <uint> source, int sourceidx, int itemidx, int lowsize, int length) {int highsize = uint32size-lowsize; int CNT = source. count; // The first loop of special processing. If (length> = uint32size) {If (lowsize = 0) {This. items [itemidx] = source [sourceidx];} else {uint lowmask = getmask (lowsize); this. items [itemidx] = (source [sourceidx] <lowsize) | (this. items [itemidx] & lowmask);} length-= uint32size; sourceidx ++; itemidx ++;} else {uint value = 0u; If (lowsize = 0) {value = source [sourceidx];} else {If (sourceidx> 0) {value = source [sourceidx-1] >>> highsize;} If (sourceidx <CNT) {value | = source [sourceidx] <lowsize ;}} uint lenmask = getmask (length-lowsize) <lowsize; this. items [itemidx] = (this. items [itemidx] & ~ Lenmask) | (Value & lenmask); return;} while (length> 0) {If (length> = uint32size) {If (lowsize = 0) {This. items [itemidx] = source [sourceidx];} else {This. items [itemidx] = (source [sourceidx] <lowsize) | (source [sourceidx-1]> highsize);} length-= uint32size; sourceidx ++; itemidx ++;} else {uint value = 0u; If (lowsize = 0) {value = source [sourceidx];} else {If (sourceidx> 0) {value = source [sourceidx-1] >>Highsize;} If (sourceidx <CNT) {value | = source [sourceidx] <lowsize ;}} uint lenmask = getmask (length); this. items [itemidx] = (this. items [itemidx] & ~ Lenmask) | (Value & lenmask); break ;}}}
2.3 Insert a single bit

Next, insert a single bit. This operation is not very complicated. In Figure 5 (a), the I-th position is the bit to be inserted. The blue part on the right and the green part on the left are all other data. The inserted result is as shown in Figure 5 (c). The bit on the right remains unchanged, and the bit on the left is moved to index I, and the new bit (red) is inserted to index I. Here, you only need to handle the BITs (blue part) at the boundary. These bits are originally the highest bits of the right uint, and now they are the lowest bits of the edge uint.

Figure 5 insert a single digit

The insert operation is also divided into two steps. The first step is to shift the left bits (Green and Blue) to the left. The specific method is to traverse from left to right, shift the current uint to the left, and then place the highest bit of the right uint to the lowest Bit Of the current uint, as shown in 5 (B. Make sure that uint [] has enough space. Code:

 
Int CNT = (this. count> 5); If (CNT + 1> This. items. length) {ensurecapacity (this. count + 1);} int idx = index> 5; For (INT I = CNT; I> idx; I --) {This. items [I] <= 1; this. items [I] | = This. items [I-1]> 31 ;}

The second step is to add the new bit to the empty I bit, as shown in 5 (c. The specific method is to extract the high (highbits) and low (lowbits) of the uint where the I-bit is located, then highbits <1 | newbit | lowbits can be inserted into the new bit. The Code is as follows (next code ):

Uint value = This. items [idx]; uint spliter = (1u <(index & 0x1f); uint lowmask = spliter-1; uint lowbits = Value & lowmask; uint highbits = Value & (uint. maxvalue-lowmask); If (! ITEM) {spliter = 0u;} This. items [idx] = (highbits <1) | spliter | lowbits;
2.4 Insert Multiple Digits

Inserting Multiple Digits is the most difficult operation. Assume that Multiple Digits (set the length to length) are inserted to the nth position in 6 (a). The blue part on the right and the green part on the left are all other data. The inserted result is shown in 6 (c). The bit on the right remains unchanged, and the bit on the left is shifted to the length on the left.

Figure 6 insert Multiple Digits

Although this operation is difficult, there are clever solutions. The solution is also divided into two steps. The first step is to move the existing data left to empty the space, as shown in 6 (B. Now suppose that the leftmost uint of the green part is always filled (assuming that the part is light green, this is done to save some bit alignment operations and will not affect it ), so I2 is the highest bit of the green part, while I3 is the position that can ensure the maximum bit of the null length. This left shift operation can be seen as reversely copying the green part from I2 to the I3 position. The implementation is similar to positive replication, but it can be divided into highbits and lowbits to copy separately, but it will reverse the direction and cannot copy the excess bits at the end, because if few bits are inserted, the redundant digits at the end of the copy operation may overwrite the existing data incorrectly.

It doesn't matter if the bit prior to the starting position is overwritten, so you don't have to consider the first copy separately like the forward copy above. In addition, the length here also adds a lowsize for ease of calculation.

Private void copyitemsbackward (INT sourceidx, int itemidx, int lowsize, int length) {int highsize = uint32size-lowsize; while (length> 0) {If (length> = uint32size) {If (highsize = 0) {This. items [itemidx] = This. items [sourceidx];} else {This. items [itemidx] = (this. items [sourceidx + 1] <lowsize) | (this. items [sourceidx]> highsize);} length-= uint32size; sourceidx --; itemidx --;} else {uint value = 0u; if (Highsize = 0) {value = This. items [sourceidx];} else {value = This. items [sourceidx + 1] <lowsize; If (sourceidx> = 0) {value | = This. items [sourceidx]> highsize ;}} uint lenmask = getmask (uint32size-length); this. items [itemidx] = (this. items [itemidx] & lenmask) | (Value &~ Lenmask); break ;}}}

Step 2: Insert the new data into the empty space and use the copyitem method defined earlier. Therefore, copyitem must not overwrite any data.

2.5 fill Multiple Digits

Figure 7 Filling Multiple Digits

The process of filling multiple places is very simple. 7 shows that the blue part is other data, and the green and blue parts are the bit to be filled, the first step is to fill in the highbits part of the same uint as tailbits, because this section affects the previous data. The second step is to fill the remaining parts with loops.

 
Public void fill (INT index, int length, bool value) {If (length = 0) {return;} uint UV = value? Uint. maxvalue: 0u; int idx = index> indexshift; int tailsize = index & indexmask; If (tailsize> 0) {uint mask = getmask (tailsize); If (length <uint32size) {mask | = ~ Getmask (length) <tailsize;} This. items [idx] = (this. items [idx] & Mask) | (UV &~ Mask); Length-= uint32size-tailsize; idx ++;} while (length> 0) {If (length> = uint32size) {This. items [idx] = uv; Length-= uint32size; idx ++;} else {uint mask = getmask (length); this. items [idx] = (this. items [idx] & ~ Mask) | (UV & Mask); break ;}}}
2.6 displacement operation

With the previous Foundation, the implementation of displacement operations is very easy. It can be combined by existing operations. It should be noted that here the left and right shifts are respectively moving toward the index increase direction and the index decrease direction, which is the same as the integer displacement direction.

Set the length of the displacement to offset. The left-shift operation is equivalent to inserting the offset position at index 0 (both of which are 0), but does not change the length of the list. The right shift operation is equivalent to removing the offset from index 0, and then filling the last blank part with 0.

This is the main bitlist operation. Batch insert and batch delete operations are the core of the operation, and it is also the most difficult to write. For the complete code, see the bitlist class. This class can also compare a uint at a time during content comparison to achieve acceleration. Therefore, I added the bitlistequalitycomparer class to implement this.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.