001// Copyright (c) 2001 Hursh Jain (http://www.mollypages.org) 002// The Molly framework is freely distributable under the terms of an 003// MIT-style license. For details, see the molly pages web site at: 004// http://www.mollypages.org/. Use, modify, have fun ! 005 006package fc.util; 007 008import java.util.*; 009 010/** 011Misc Binary/bit operation related utility methods. 012 013All bit positions are 0-based. For example, to set a 8th logical bit (highest bit in a byte), say 014<code>setBit(byte, 7)</code>. 015<p> 016Of course, since most methods take primitive types, the return value must be assigned back to the 017variable that is being set, i.e. 018<code> 019int a = 0; 020<b>a = </b>BitSet.setBit(a, 1); 021</code> 022*/ 023 024public class BitUtil 025{ 026/* 027we could only use the long version (all types get widened to long) but we won't be able 028to error check for the bit position number relevant to the type - so different methods. 029The spec says jvm will shift more than 31 or 63 bits anyway (even if larger value specified) 030but this allows us to catch bugs. 031*/ 032 033/** 034All indexes are <b>zero based</b> (the right most bit is bit <code>0</code>). 035 036returns <code>true</code> is specified bit in the specified byte is set. The num parameter is the bit 037position (0-based index). Set means the bit has value 1. 038 039@throws IllegalArgumentException if the bit position is out of range 040*/ 041public static boolean isBitSet(byte i, int num) 042 { 043 Argcheck.istrue(num >= 0 && num <= 7, "bit number must be between [0-7] for int, I got num=[" + num + "]"); 044 045 return ((i >> num) & 0x1) == 1; 046 } 047 048 049public static boolean isBitSet(short i, int num) 050 { 051 Argcheck.istrue(num >= 0 && num <= 15, "bit number must be between [0-15] for short, I got num=[" + num + "]"); 052 053 return ((i >> num) & 0x1) == 1; 054 } 055 056 057public static boolean isBitSet(char i, int num) 058 { 059 Argcheck.istrue(num >= 0 && num <= 15, "bit number must be between [0-15] for char, I got num=[" + num + "]"); 060 061 return ((i >> num) & 0x1) == 1; 062 } 063 064 065public static boolean isBitSet(int i, int num) 066 { 067 Argcheck.istrue(num >= 0 && num <= 31, "bit number must be between [0-31] for int, I got num=[" + num + "]"); 068 069 return ((i >> num) & 0x1) == 1; 070 } 071 072 073public static boolean isBitSet(long i, int num) 074 { 075 Argcheck.istrue(num >= 0 && num <= 63, "bit number must be between [0-63] for char, I got num=[" + num + "]"); 076 077 return ((i >> num) & 0x1L) == 1; 078 } 079 080 081public static byte setBit(byte i, int num) 082 { 083 Argcheck.istrue(num >= 0 && num <= 7, "bit number must be between [0-7] for byte, I got num=[" + num + "]"); 084 085 return (byte) (i | (1 << num)); 086 } 087 088 089public static short setBit(short i, int num) 090 { 091 Argcheck.istrue(num >= 0 && num <= 15, "bit number must be between [0-15] for short, I got num=[" + num + "]"); 092 093 return (short) (i | (1 << num)); 094 } 095 096 097public static char setBit(char i, int num) 098 { 099 Argcheck.istrue(num >= 0 && num <= 15, "bit number must be between [0-15] for char, I got num=[" + num + "]"); 100 101 return (char) (i | (1 << num)); 102 } 103 104 105public static int setBit(int i, int num) 106 { 107 Argcheck.istrue(num >= 0 && num <= 31, "bit number must be between [0-31] for int, I got num=[" + num + "]"); 108 109 return i | (1 << num); 110 } 111 112 113public static long setBit(long i, long num) 114 { 115 Argcheck.istrue(num >= 0 && num <= 63, "bit number must be between [0-63] for long, I got num=[" + num + "]"); 116 117 return i | (0x1L << num); //HAVE TO SAY 1L !!! 1(no L) << num will only shift a max of 32 bits 118 //since num is constrained by spec to 0-31 max if lhs is int 119 //by default, all literals are int unless L specified. 120 //spent all day chasing this bug down. 121 } 122 123 124public static byte clearBit(byte i, int num) 125 { 126 Argcheck.istrue(num >= 0 && num <= 7, "bit number must be between [0-7] for byte, I got num=[" + num + "]"); 127 128 return (byte) (i & ~(1 << num)); 129 } 130 131 132public static short clearBit(short i, int num) 133 { 134 Argcheck.istrue(num >= 0 && num <= 15, "bit number must be between [0-15] for short, I got num=[" + num + "]"); 135 136 return (short) (i & ~(1 << num)); 137 } 138 139 140public static char clearBit(char i, int num) 141 { 142 Argcheck.istrue(num >= 0 && num <= 15, "bit number must be between [0-15] for char, I got num=[" + num + "]"); 143 144 return (char) (i & ~(1 << num)); 145 } 146 147 148public static int clearBit(int i, int num) 149 { 150 Argcheck.istrue(num >= 0 && num <= 31, "bit number must be between [0-31] for int, I got num=[" + num + "]"); 151 152 return i & ~(1 << num); 153 } 154 155 156public static long clearBit(long i, int num) 157 { 158 Argcheck.istrue(num >= 0 && num <= 63, "bit number must be between [0-63] for long, I got num=[" + num + "]"); 159 160 return i & ~(1L << num); 161 } 162 163 164//Integer.toBinaryString is doesn't left pad and the logic to do it is stupid so 165//just roll our own (faster this way anyway) 166 167public static String toBinaryString(byte i) 168 { 169 char[] buf = new char[8]; 170 for (int n = 0; n < 8; n++) { 171 buf[n] = ((i >> (7-n)) & 0x1) == 1 ? '1' : '0'; 172 } 173 return new String (buf); 174 } 175 176 177/** 178returns a 16 char binary digit string 179*/ 180public static String toBinaryString(short i) 181 { 182 char[] buf = new char[16]; 183 for (int n = 0; n < 16; n++) { 184 buf[n] = ((i >> (15-n)) & 0x1) == 1 ? '1' : '0'; 185 } 186 return new String (buf); 187 } 188 189 190/** 191returns a 16 char binary digit string 192*/ 193public static String toBinaryString(char i) 194 { 195 char[] buf = new char[16]; 196 for (int n = 0; n < 16; n++) { 197 buf[n] = ((i >> (15-n)) & 0x1) == 1 ? '1' : '0'; 198 } 199 return new String (buf); 200 } 201 202 203/** 204returns a 32 char binary digit string (Integer.toBinaryString is not padded, can be less than 32 205chars) 206*/ 207public static String toBinaryString(int i) 208 { 209 char[] buf = new char[32]; 210 for (int n = 0; n < 32; n++) { 211 buf[n] = ((i >> (31-n)) & 0x1) == 1 ? '1' : '0'; 212 } 213 return new String (buf); 214 } 215 216 217/** 218returns a 64 char binary digit string (Long.toBinaryString is not padded, can be 219less than 64 chars)) 220*/ 221public static String toBinaryString(long i) 222 { 223 char[] buf = new char[64]; 224 for (int n = 0; n < 64; n++) { 225 buf[n] = ((i >> (63-n)) & 0x1L) == 1 ? '1' : '0'; 226 } 227 return new String (buf); 228 } 229 230 231static final int DEF_SEP_NUM = 4; 232static final char DEF_SEP_CHAR = ' '; 233 234private static Map getFormatMap(String formatstr) 235 { 236 int sep_num = DEF_SEP_NUM; 237 char sep_char = DEF_SEP_CHAR; 238 239 char[] arr = null; 240 241 if (formatstr != null) { 242 arr = formatstr.toCharArray(); 243 if (arr.length > 0) { 244 try { 245 sep_num = Integer.parseInt(String.valueOf(arr[0])); 246 } 247 catch (NumberFormatException e) { 248 sep_char = arr[0]; 249 } 250 } 251 if (arr.length > 1) { 252 sep_char = arr[1]; 253 } 254 } 255 Map m = new HashMap(); 256 m.put("sep_num", sep_num); 257 m.put("sep_char", sep_char); 258 return m; 259 } 260 261/** 262returns a 8 char binary digit string. 263<p>The format string has a number and separator, like "4 " which will print chunks of size <num> 264with the specified <separator>. The number must be between [1-9] and separator must be a single 265character, like: | or space. Errors in the format string are ignored and default values are used 266instead (4 and space). 267*/ 268public static String toBinaryString(byte i, String format) 269 { 270 String result = toBinaryString(i); 271 272 Map m = getFormatMap(format); 273 int sep_num = (Integer) m.get("sep_num"); 274 char sep_char = (Character) m.get("sep_char"); 275 276 StringBuilder builder = new StringBuilder(); 277 for (int n = 8, pos = 1; n > 0; n--, pos++) 278 { 279 builder.append(result.charAt(n-1)); 280 if ((pos % sep_num == 0) && n > 1) { 281 builder.append(sep_char); 282 } 283 } 284 return builder.reverse().toString(); 285 } 286 287 288/** 289returns a 16 char binary digit string. 290<p>The format string has a number and separator, like "4 " which will print chunks of size <num> 291with the specified <separator>. The number must be between [1-9] and separator must be a single 292character, like: | or space. Errors in the format string are ignored and default values are used 293instead (4 and space). 294*/ 295public static String toBinaryString(short i, String format) 296 { 297 String result = toBinaryString(i); 298 299 Map m = getFormatMap(format); 300 int sep_num = (Integer) m.get("sep_num"); 301 char sep_char = (Character) m.get("sep_char"); 302 303 StringBuilder builder = new StringBuilder(); 304 for (int n = 16, pos = 1; n > 0; n--, pos++) 305 { 306 builder.append(result.charAt(n-1)); 307 if ((pos % sep_num == 0) && n > 1) { 308 builder.append(sep_char); 309 } 310 } 311 return builder.reverse().toString(); 312 } 313 314 315/** 316returns a 16 char binary digit string. 317<p>The format string has a number and separator, like "4 " which will print chunks of size num with 318the specified separator. 319*/ 320public static String toBinaryString(char i, String format) 321 { 322 String result = toBinaryString(i); 323 324 Map m = getFormatMap(format); 325 int sep_num = (Integer) m.get("sep_num"); 326 char sep_char = (Character) m.get("sep_char"); 327 328 StringBuilder builder = new StringBuilder(); 329 for (int n = 16, pos = 1; n > 0; n--, pos++) 330 { 331 builder.append(result.charAt(n-1)); 332 if ((pos % sep_num == 0) && n > 1) { 333 builder.append(sep_char); 334 } 335 } 336 return builder.reverse().toString(); 337 } 338 339 340/** returns a 32char binary digit string (Long.toBinaryString is not padded, can be less than 34132 chars). 342<p>The format string has a number and separator, like "4 " which will print chunks of size <num> 343with the specified <separator>. The number must be between [1-9] and separator must be a single 344character, like: | or space. Errors in the format string are ignored and default values are used 345instead (4 and space). 346*/ 347public static String toBinaryString(int i, String format) 348 { 349 String result = toBinaryString(i); 350 351 Map m = getFormatMap(format); 352 int sep_num = (Integer) m.get("sep_num"); 353 char sep_char = (Character) m.get("sep_char"); 354 355 StringBuilder builder = new StringBuilder(); 356 for (int n = 32, pos = 1; n > 0; n--, pos++) 357 { 358 builder.append(result.charAt(n-1)); 359 if ((pos % sep_num == 0) && n > 1) { 360 builder.append(sep_char); 361 } 362 } 363 return builder.reverse().toString(); 364 } 365 366 367/** returns a 64 char binary digit string (Long.toBinaryString is not padded, can be less than 36864 chars). 369<p>The format string has a number and separator, like "4 " which will print chunks of size <num> 370with the specified <separator>. The number must be between [1-9] and separator must be a single 371character, like: | or space. Errors in the format string are ignored and default values are used 372instead (4 and space). 373*/ 374public static String toBinaryString(long i, String format) 375 { 376 String result = toBinaryString(i); 377 378 Map m = getFormatMap(format); 379 int sep_num = (Integer) m.get("sep_num"); 380 char sep_char = (Character) m.get("sep_char"); 381 382 StringBuilder builder = new StringBuilder(); 383 for (int n = 64, pos = 1; n > 0; n--, pos++) 384 { 385 builder.append(result.charAt(n-1)); 386 if ((pos % sep_num == 0) && n > 1) { 387 builder.append(sep_char); 388 } 389 } 390 return builder.reverse().toString(); 391 } 392 393 394public static void main (String args[]) 395 { 396 int i = Integer.parseInt("1000", 2); 397 System.out.println("[int]isBitSet('1000'/"+i+",3):"+isBitSet(i,3)); 398 399 i = Integer.parseInt("0000", 2); 400 System.out.println("[int]isBitSet('0000'/"+i+",3):"+isBitSet(i,3)); 401 402 i = Integer.parseInt("1001", 2); 403 System.out.println("[int]isBitSet('1001'/"+i+",2):"+isBitSet(i,2)); 404 405 i = Integer.parseInt("1001", 2); 406 System.out.println("[int]isBitSet('1001'/"+i+",0):"+isBitSet(i,0)); 407 408 i = Integer.parseInt("1000000000000000000000000000000", 2); 409 System.out.println("[int]isBitSet('1000000000000000000000000000000'/"+i+",2):"+isBitSet(i,2)); 410 411 i = Integer.parseInt("1001", 2); 412 try { 413 System.out.println("[int]isBitSet('1001'/"+i+",31):"+isBitSet(i,31)); 414 } 415 catch (Exception e) { 416 System.out.println(e); 417 } 418 419 long lng = Long.parseLong("11001100", 2); 420 System.out.println("[long]isBitSet('11001100'/"+lng+",3):"+isBitSet(lng,3)); 421 422 char c = (char) Integer.parseInt("1001000010010000", 2); 423 System.out.println("[char]isBitSet('1001000010010000'/"+c+",1):"+isBitSet(c,1)); 424 System.out.println("[char]isBitSet('1001000010010000'/"+c+",15):"+isBitSet(c,15)); 425 426 byte b = (byte) Integer.parseInt("00000100", 2); 427 428 System.out.println("[byte]isBitSet('00000100'/"+b+",1):"+isBitSet(b,1)); 429 System.out.println("[byte]isBitSet('00000100'/"+b+",2):"+isBitSet(b,2)); 430 431 short s = (short) Integer.parseInt("1000100010001000", 2); 432 System.out.println("[short]isBitSet('1000100010001000'/"+s+",1):"+isBitSet(s,1)); 433 System.out.println("[short]isBitSet('1000100010001000'/"+s+",3):"+isBitSet(s,3)); 434 435 System.out.println("------- set and clear bits ----------"); 436 System.out.println("------- char ----------"); 437 System.out.print("c="+toBinaryString(c)); 438 c = setBit(c, 0); 439 System.out.println("->setBit(c, 0)->"+toBinaryString(c)); 440 System.out.println("isBitSet(c, 0):"+isBitSet(c, 0)); 441 442 System.out.print("c="+toBinaryString(c)); 443 c = setBit(c, 1); 444 System.out.println("->setBit(c, 1)->"+toBinaryString(c)); 445 System.out.println("isBitSet(c, 1):"+isBitSet(c, 1)); 446 447 System.out.print("c="+toBinaryString(c)); 448 c = clearBit(c, 1); 449 System.out.println("->clearBit(c, 1)->"+toBinaryString(c)); 450 System.out.println("isBitSet(c, 1):"+isBitSet(c, 1)); 451 452 System.out.println("------- long ----------"); 453 System.out.println("lng="+toBinaryString(lng,",")); 454 lng = setBit(lng, 63); 455 System.out.println("->setBit(lng, 63)->\n "+toBinaryString(lng,",")); 456 System.out.println("isBitSet(lng, 63):"+isBitSet(lng, 63)); 457 lng = clearBit(lng, 63); 458 System.out.println("->clearBit(lng, 63)->\n "+toBinaryString(lng,",")); 459 System.out.println("isBitSet(lng, 63):"+isBitSet(lng, 63)); 460 461 System.out.println("------- int ----------"); 462 System.out.print("i="+toBinaryString(i,"|")); 463 i = setBit(i, 20); 464 System.out.println("->setBit(i, 20)-> "+toBinaryString(i,"|")); 465 System.out.println("isBitSet(i, 20):"+isBitSet(i, 20)); 466 System.out.print("i="+toBinaryString(i,"|")); 467 i = clearBit(i, 20); 468 System.out.println("->clearBit(i,20)->"+toBinaryString(i,"|")); 469 System.out.println("isBitSet(i, 20):"+isBitSet(i, 20)); 470 471 System.out.println("------- int ----------"); 472 System.out.print("i="+toBinaryString(i,"|")); 473 i = setBit(i, 31); 474 System.out.println("->setBit(i, 31)-> "+toBinaryString(i,"|")); 475 System.out.println("isBitSet(i, 31):"+isBitSet(i, 31)); 476 System.out.print("i="+toBinaryString(i,"|")); 477 i = clearBit(i, 31); 478 System.out.println("->clearBit(i,31)->"+toBinaryString(i,"|")); 479 System.out.println("isBitSet(i, 31):"+isBitSet(i, 31)); 480 481 System.out.println("------- byte ----------"); 482 System.out.println("b="+toBinaryString(b)); 483 b = setBit(b, 7); 484 System.out.println("->setBit(b, 7)->" + toBinaryString(b)); 485 System.out.println("isBitSet(b, 7):"+isBitSet(b, 7)); 486 System.out.print("b="+toBinaryString(b)); 487 b = clearBit(b, 7); 488 System.out.println("->clearBit(b, 7)->"+toBinaryString(b)); 489 System.out.println("isBitSet(b, 7):"+isBitSet(b, 7)); 490 491 System.out.println("------- short ----------"); 492 System.out.print("s="+toBinaryString(s,"|")); 493 s = setBit(s, 10); 494 System.out.println("->setBit(s, 10)->" + toBinaryString(s,"|")); 495 System.out.println("isBitSet(s, 10):"+isBitSet(s, 10)); 496 System.out.print("s="+toBinaryString(s,"|")); 497 s = clearBit(s, 10); 498 System.out.println("->clearBit(s, 10)->"+toBinaryString(s,"|")); 499 System.out.println("isBitSet(s, 10):"+isBitSet(s, 10)); 500 501 System.out.println("------- argument checks ----------"); 502 try { setBit(b, 8); } catch (RuntimeException e) { } 503 try { setBit(c, 16); } catch (RuntimeException e) { } 504 try { setBit(s, 16); } catch (RuntimeException e) { } 505 try { setBit(i, 32); } catch (RuntimeException e) { } 506 try { setBit(lng, 64); } catch (RuntimeException e) { } 507 508 try { setBit(b, 7); } catch (RuntimeException e) { } 509 try { setBit(c, 15); } catch (RuntimeException e) { } 510 try { setBit(s, 15); } catch (RuntimeException e) { } 511 try { setBit(i, 31); } catch (RuntimeException e) { } 512 try { setBit(lng, 63); } catch (RuntimeException e) { } 513 514 try { setBit(b, -1); } catch (RuntimeException e) { } 515 try { setBit(c, -1); } catch (RuntimeException e) { } 516 try { setBit(s, -1); } catch (RuntimeException e) { } 517 try { setBit(i, -1); } catch (RuntimeException e) { } 518 try { setBit(lng, -1); } catch (RuntimeException e) { } 519 520 System.out.println("done..."); 521 } 522} 523 524