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 &lt;num&gt;
264with the specified &lt;separator&gt;. 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 &lt;num&gt;
291with the specified &lt;separator&gt;. 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 &lt;num&gt;
343with the specified &lt;separator&gt;. 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 &lt;num&gt;
370with the specified &lt;separator&gt;. 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