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