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