1 /*
2  *  Copyright (c) 2018, Sam Kumar <samkumar@cs.berkeley.edu>
3  *  Copyright (c) 2018, University of California, Berkeley
4  *  All rights reserved.
5  *
6  *  Redistribution and use in source and binary forms, with or without
7  *  modification, are permitted provided that the following conditions are met:
8  *  1. Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  *  2. Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  *  3. Neither the name of the copyright holder nor the
14  *     names of its contributors may be used to endorse or promote products
15  *     derived from this software without specific prior written permission.
16  *
17  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  *  POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 /* BITMAP */
31 
32 #include <stdint.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 
37 #include "bitmap.h"
38 
bmp_init(uint8_t * buf,size_t numbytes)39 void bmp_init(uint8_t* buf, size_t numbytes) {
40     memset(buf, 0x00, numbytes);
41 }
42 
43 #define _bmp_getrangeinfo(buf, start, len, first_bit_id, first_byte_ptr, last_bit_id, last_byte_ptr) \
44     do { \
45         first_bit_id = (start & 0x7); \
46         first_byte_ptr = buf + (start >> 3); \
47         last_bit_id = (len & 0x7) + first_bit_id; \
48         last_byte_ptr = first_byte_ptr + (len >> 3) + (last_bit_id >> 3); \
49         last_bit_id &= 0x7; \
50     } while (0)
51 
bmp_setrange(uint8_t * buf,size_t start,size_t len)52 void bmp_setrange(uint8_t* buf, size_t start, size_t len) {
53     uint8_t first_bit_id;
54     uint8_t* first_byte_set;
55     uint8_t last_bit_id;
56     uint8_t* last_byte_set;
57     uint8_t first_byte_mask, last_byte_mask;
58     _bmp_getrangeinfo(buf, start, len, first_bit_id, first_byte_set,
59                       last_bit_id, last_byte_set);
60 
61     first_byte_mask = (uint8_t) (0xFF >> first_bit_id);
62     last_byte_mask = (uint8_t) (0xFF << (8 - last_bit_id));
63 
64     /* Set the bits. */
65     if (first_byte_set == last_byte_set) {
66         *first_byte_set |= (first_byte_mask & last_byte_mask);
67     } else {
68         *first_byte_set |= first_byte_mask;
69         memset(first_byte_set + 1, 0xFF, (size_t) (last_byte_set - first_byte_set - 1));
70         if (last_byte_mask != 0x00) {
71             *last_byte_set |= last_byte_mask;
72         }
73     }
74 }
75 
bmp_clrrange(uint8_t * buf,size_t start,size_t len)76 void bmp_clrrange(uint8_t* buf, size_t start, size_t len) {
77     uint8_t first_bit_id;
78     uint8_t* first_byte_clear;
79     uint8_t last_bit_id;
80     uint8_t* last_byte_clear;
81     uint8_t first_byte_mask, last_byte_mask;
82     _bmp_getrangeinfo(buf, start, len, first_bit_id, first_byte_clear,
83                       last_bit_id, last_byte_clear);
84 
85     first_byte_mask = (uint8_t) (0xFF << (8 - first_bit_id));
86     last_byte_mask = (uint8_t) (0xFF >> last_bit_id);
87 
88     /* Clear the bits. */
89     if (first_byte_clear == last_byte_clear) {
90         *first_byte_clear &= (first_byte_mask | last_byte_mask);
91     } else {
92         *first_byte_clear &= first_byte_mask;
93         memset(first_byte_clear + 1, 0x00, (size_t) (last_byte_clear - first_byte_clear - 1));
94         if (last_byte_mask != 0xFF) {
95             *last_byte_clear &= last_byte_mask;
96         }
97     }
98 }
99 
bmp_countset(uint8_t * buf,size_t buflen,size_t start,size_t limit)100 size_t bmp_countset(uint8_t* buf, size_t buflen, size_t start, size_t limit) {
101     uint8_t first_bit_id;
102     uint8_t first_byte;
103     uint8_t ideal_first_byte;
104     size_t numset;
105     uint8_t curr_byte;
106     size_t curr_index = start >> 3;
107     first_bit_id = start & 0x7;
108     first_byte = *(buf + curr_index);
109 
110     numset = 8 - first_bit_id; // initialize optimistically, assuming that the first byte will have all 1's in the part we care about
111     ideal_first_byte = (uint8_t) (0xFF >> first_bit_id);
112     first_byte &= ideal_first_byte;
113     if (first_byte == ideal_first_byte) {
114         // All bits in the first byte starting at first_bit_id are set
115         for (curr_index = curr_index + 1; curr_index < buflen && numset < limit; curr_index++) {
116             curr_byte = buf[curr_index];
117             if (curr_byte == (uint8_t) 0xFF) {
118                 numset += 8;
119             } else {
120                 while (curr_byte & (uint8_t) 0x80) { // we could add a numset < limit check here, but it probably isn't worth it
121                     curr_byte <<= 1;
122                     numset++;
123                 }
124                 break;
125             }
126         }
127     } else {
128         // The streak ends within the first byte
129         do {
130             first_byte >>= 1;
131             ideal_first_byte >>= 1;
132             numset--;
133         } while (first_byte != ideal_first_byte);
134     }
135     return numset;
136 }
137 
bmp_read_bit(uint8_t * buf,size_t i)138 static inline uint8_t bmp_read_bit(uint8_t* buf, size_t i) {
139     size_t byte_index = i >> 3;
140     size_t bit_index = i & 0x7; // Amount to left shift to get bit in MSB
141     return ((uint8_t) (buf[byte_index] << bit_index)) >> 7;
142 }
143 
bmp_write_bit(uint8_t * buf,size_t i,uint8_t bit)144 static inline void bmp_write_bit(uint8_t* buf, size_t i, uint8_t bit) {
145     size_t byte_index = i >> 3;
146     size_t bit_index = i & 0x7; // Amount to left shift to get bit in MSB
147     size_t bit_shift = 7 - bit_index; // Amount to right shift to get bit in LSB
148     buf[byte_index] = (buf[byte_index] & ~(1 << bit_shift)) | (bit << bit_shift);
149 }
150 
bmp_read_byte(uint8_t * buf,size_t i)151 static inline uint8_t bmp_read_byte(uint8_t* buf, size_t i) {
152     size_t byte_index = i >> 3;
153     size_t bit_index = i & 0x7; // Amount to left shift to get bit in MSB
154     if (bit_index == 0) {
155         return buf[byte_index];
156     }
157     return (buf[byte_index] << bit_index) | (buf[byte_index + 1] >> (8 - bit_index));
158 }
159 
bmp_write_byte(uint8_t * buf,size_t i,uint8_t byte)160 static inline void bmp_write_byte(uint8_t* buf, size_t i, uint8_t byte) {
161     size_t byte_index = i >> 3;
162     size_t bit_index = i & 0x7; // Amount to left shift to get bit in MSB
163     if (bit_index == 0) {
164         buf[byte_index] = byte;
165         return;
166     }
167     buf[byte_index] = (buf[byte_index] & (0xFF << (8 - bit_index))) | (byte >> bit_index);
168     buf[byte_index + 1] = (buf[byte_index + 1] & (0xFF >> bit_index)) | (byte << (8 - bit_index));
169 }
170 
bmp_swap(uint8_t * buf,size_t start_1,size_t start_2,size_t len)171 void bmp_swap(uint8_t* buf, size_t start_1, size_t start_2, size_t len) {
172     while ((len & 0x7) != 0) {
173         uint8_t bit_1 = bmp_read_bit(buf, start_1);
174         uint8_t bit_2 = bmp_read_bit(buf, start_2);
175         if (bit_1 != bit_2) {
176             bmp_write_bit(buf, start_1, bit_2);
177             bmp_write_bit(buf, start_2, bit_1);
178         }
179 
180         start_1++;
181         start_2++;
182         len--;
183     }
184 
185     while (len != 0) {
186         uint8_t byte_1 = bmp_read_byte(buf, start_1);
187         uint8_t byte_2 = bmp_read_byte(buf, start_2);
188         if (byte_1 != byte_2) {
189             bmp_write_byte(buf, start_1, byte_2);
190             bmp_write_byte(buf, start_2, byte_1);
191         }
192 
193         start_1 += 8;
194         start_2 += 8;
195         len -= 8;
196     }
197 }
198 
bmp_isempty(uint8_t * buf,size_t buflen)199 int bmp_isempty(uint8_t* buf, size_t buflen) {
200     uint8_t* bufend = buf + buflen;
201     while (buf < bufend) {
202         if (*(buf++)) {
203             return 0;
204         }
205     }
206     return 1;
207 }
208 
209 /*
210  * This function is unused, but I'm leaving it in the code as a comment in case
211  * it becomes useful for debugging later on.
212  */
213 #if 0
214 void bmp_print(uint8_t* buf, size_t buflen) {
215     size_t i;
216     for (i = 0; i < buflen; i++) {
217         printf("%02X", buf[i]);
218     }
219     printf("\n");
220 }
221 #endif
222