1 /* 2 * Copyright (c) 2018 Helmut Neemann 3 * Use of this source code is governed by the GPL v3 license 4 * that can be found in the LICENSE file. 5 */ 6 package de.neemann.digital.core; 7 8 import de.neemann.digital.lang.Lang; 9 10 /** 11 * Helper for bit manipulating 12 */ 13 public final class Bits { 14 Bits()15 private Bits() { 16 } 17 18 /** 19 * Shifts a value up 20 * 21 * @param val the value to shift 22 * @param bits the bit count to shift 23 * @return the shifted value 24 */ up(long val, int bits)25 public static long up(long val, int bits) { 26 if (bits < 64) 27 return val << bits; 28 else 29 return 0; 30 } 31 32 /** 33 * Shifts a value down 34 * 35 * @param val the value to shift 36 * @param bits the bit count to shift 37 * @return the shifted value 38 */ down(long val, int bits)39 public static long down(long val, int bits) { 40 if (bits < 64) 41 return val >>> bits; 42 else 43 return 0; 44 } 45 46 /** 47 * Creates a bit mask with the lowest [bits] bits set. 48 * 49 * @param bits the number of 1 bits 50 * @return a value with the lowest [bits] bits set. 51 */ mask(int bits)52 public static long mask(int bits) { 53 if (bits < 64) 54 return (1L << bits) - 1; 55 else 56 return -1; 57 } 58 59 /** 60 * calculates the signed flag. 61 * 62 * @param bits the bit count 63 * @return the last used bit ( 1<<(bits-1) ) 64 */ signedFlagMask(int bits)65 public static long signedFlagMask(int bits) { 66 return up(1, bits - 1); 67 } 68 69 /** 70 * Returns true if value is negative 71 * 72 * @param value the value 73 * @param bits the bit count 74 * @return true if the last relevant bit is set 75 */ isNegative(long value, int bits)76 public static boolean isNegative(long value, int bits) { 77 return (value & signedFlagMask(bits)) != 0; 78 } 79 80 /** 81 * Sign extension of the value. 82 * signExtend(3,2) returns -1. 83 * 84 * @param value the value 85 * @param bits number of bits 86 * @return the sign extended value 87 */ signExtend(long value, int bits)88 public static long signExtend(long value, int bits) { 89 if (bits >= 64) 90 return value; 91 else { 92 if ((value & signedFlagMask(bits)) == 0) 93 return value; 94 else 95 return value | ~mask(bits); 96 } 97 } 98 99 /** 100 * Calculates the number of bits needed to store the given value b. 101 * 102 * @param b number 103 * @return number of bits needed to store b 104 */ binLn2(long b)105 public static int binLn2(long b) { 106 int outBits = 1; 107 while ((1L << outBits) <= b) 108 outBits++; 109 return outBits; 110 } 111 112 /** 113 * Removes a bit from a value. 114 * This means it shifts the higher bits down. Behaves like removing an item from a list. 115 * 116 * @param value the value 117 * @param bit the bit to remove 118 * @return the new value 119 */ removeBitFromValue(int value, int bit)120 public static int removeBitFromValue(int value, int bit) { 121 if (bit > 0) { 122 return ((value & (~((1 << (bit + 1)) - 1))) >>> 1) | (value & ((1 << bit) - 1)); 123 } else { 124 return value >>> 1; 125 } 126 } 127 128 /** 129 * Decodes a string to a long. 130 * Supports decimal, octal, hex, binary and ascii 131 * 132 * @param str the string 133 * @return the long value 134 * @throws NumberFormatException invalid string 135 */ decode(String str)136 public static long decode(String str) throws NumberFormatException { 137 if (str == null) 138 return 0; 139 140 str = str.trim(); 141 142 if (str.length() == 0) 143 return 0; 144 145 if (str.indexOf(':') >= 0) 146 return decodeFixed(str); 147 if (str.indexOf('.') > -1) { 148 try { 149 if (str.endsWith("d") || str.endsWith("D")) 150 return Double.doubleToLongBits(Double.parseDouble(str.substring(0, str.length() - 1))); 151 else 152 return Float.floatToIntBits(Float.parseFloat(str)); 153 } catch (java.lang.NumberFormatException e) { 154 throw new NumberFormatException(str, 0); 155 } 156 } 157 158 int p = 0; 159 160 boolean neg = false; 161 if (str.charAt(p) == '-') { 162 neg = true; 163 p++; 164 } 165 166 if (p >= str.length()) 167 throw new NumberFormatException(str, p); 168 169 boolean wasZero = false; 170 while (str.length() > p && str.charAt(p) == '0') { 171 wasZero = true; 172 p++; 173 } 174 175 if (p >= str.length()) 176 return 0; 177 178 int radix; 179 if (wasZero) { 180 if (neg) throw new NumberFormatException(str, p); 181 switch (str.charAt(p)) { 182 case 'x': 183 case 'X': 184 radix = 16; 185 p++; 186 if (p == str.length()) throw new NumberFormatException(str, p); 187 break; 188 case 'b': 189 case 'B': 190 radix = 2; 191 p++; 192 if (p == str.length()) throw new NumberFormatException(str, p); 193 break; 194 default: 195 radix = 8; 196 } 197 } else { 198 if (str.charAt(p) == '\'') { 199 if (neg) throw new NumberFormatException(str, p); 200 p++; 201 if (p == str.length()) throw new NumberFormatException(str, p); 202 return str.charAt(p); 203 } else 204 radix = 10; 205 } 206 207 long val = decode(str, p, radix); 208 209 if (neg) 210 val = -val; 211 return val; 212 } 213 214 /** 215 * Decodes the given string starting at position p 216 * 217 * @param str the string to decode 218 * @param p the starting position 219 * @param radix the radix 220 * @return the value 221 * @throws NumberFormatException NumberFormatException 222 */ decode(String str, int p, int radix)223 public static long decode(String str, int p, int radix) throws NumberFormatException { 224 long val = 0; 225 while (p < str.length()) { 226 int d = Character.digit(str.charAt(p), radix); 227 if (d < 0) 228 throw new NumberFormatException(str, p); 229 val = val * radix + d; 230 p++; 231 } 232 return val; 233 } 234 decodeFixed(String str)235 private static long decodeFixed(String str) throws NumberFormatException { 236 int p = str.indexOf(':'); 237 try { 238 int frac = Math.abs(Integer.parseInt(str.substring(p + 1))); 239 double floating = Double.parseDouble(str.substring(0, p)); 240 return Math.round(floating * (1L << frac)); 241 } catch (java.lang.NumberFormatException e) { 242 throw new NumberFormatException(str, 0); 243 } 244 } 245 246 /** 247 * Indicates a invalid string. 248 * Its not a runtime exception! 249 */ 250 public final static class NumberFormatException extends Exception { NumberFormatException(String str, int p)251 private NumberFormatException(String str, int p) { 252 super(Lang.get("err_invalidNumberFormat_N_N", str, p + 1)); 253 } 254 } 255 } 256