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.core.element.ElementAttributes;
9 import de.neemann.digital.core.element.Keys;
10 
11 /**
12  * The available number formats
13  */
14 public enum IntFormat {
15     /**
16      * the default format
17      */
18     def(ValueFormatterDefault.INSTANCE),
19     /**
20      * decimal
21      */
22     dec(new ValueFormatterDecimal(false)),
23     /**
24      * decimal signed
25      */
26     decSigned(new ValueFormatterDecimal(true)),
27     /**
28      * hex
29      */
30     hex(ValueFormatterHex.INSTANCE),
31     /**
32      * binary
33      */
34     bin(new ValueFormatterBinary()),
35     /**
36      * octal
37      */
38     oct(new ValueFormatterOctal()),
39     /**
40      * ascii
41      */
42     ascii(new ValueFormatterAscii()),
43     /**
44      * fixed point
45      */
46     fixed(attributes -> new ValueFormatterFixedPoint(attributes, false)),
47     /**
48      * fixed point signed
49      */
50     fixedSigned(attributes -> new ValueFormatterFixedPoint(attributes, true)),
51     /**
52      * floating point
53      */
54     floating(new ValueFormatterFloat());
55 
56     /**
57      * The default formatter
58      */
59     public static final ValueFormatter DEFAULT_FORMATTER = ValueFormatterDefault.INSTANCE;
60     /**
61      * The hexadecimal formatter
62      */
63     public static final ValueFormatter HEX_FORMATTER = ValueFormatterHex.INSTANCE;
64 
65     private final Factory factory;
66     private final boolean dependsOnAttributes;
67 
IntFormat(ValueFormatter instance)68     IntFormat(ValueFormatter instance) {
69         this.factory = attributes -> instance;
70         this.dependsOnAttributes = false;
71     }
72 
IntFormat(Factory factory)73     IntFormat(Factory factory) {
74         this.factory = factory;
75         this.dependsOnAttributes = true;
76     }
77 
78     /**
79      * Creates a formatter which is able to format Values
80      *
81      * @param attributes the elements attributes
82      * @return the created {@link ValueFormatter}
83      */
createFormatter(ElementAttributes attributes)84     public ValueFormatter createFormatter(ElementAttributes attributes) {
85         return factory.create(attributes);
86     }
87 
88     /**
89      * @return true if this type depends on elements attributes
90      */
dependsOnAttributes()91     public boolean dependsOnAttributes() {
92         return dependsOnAttributes;
93     }
94 
95     private interface Factory {
create(ElementAttributes attributes)96         ValueFormatter create(ElementAttributes attributes);
97     }
98 
99     private static final char[] DIGITS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
100 
101     /**
102      * The default value formatter
103      */
104     private static final class ValueFormatterDefault implements ValueFormatter {
105         private static final ValueFormatter INSTANCE = new ValueFormatterDefault();
106 
107         @Override
formatToView(Value inValue)108         public String formatToView(Value inValue) {
109             if (inValue.isHighZ())
110                 return inValue.toString();
111             else
112                 return toShortHex(inValue.getValue(), false);
113         }
114 
115         @Override
formatToEdit(Value inValue)116         public String formatToEdit(Value inValue) {
117             if (inValue.isHighZ())
118                 return "Z";
119 
120             final long value = inValue.getValue();
121             if (value >= 0 && value < 10)
122                 return Long.toString(value);
123             else
124                 return "0x" + toShortHex(value, true);
125         }
126 
127         @Override
strLen(int bits)128         public int strLen(int bits) {
129             return (bits - 1) / 4 + 3;
130         }
131 
132         @Override
isSuitedForAddresses()133         public boolean isSuitedForAddresses() {
134             return false; // difficult to read in a table
135         }
136 
137         @Override
dragValue(long initial, int bits, double inc)138         public long dragValue(long initial, int bits, double inc) {
139             return dragValueSigned(initial, bits, inc, false);
140         }
141 
142         @Override
isSeparatorInFrontOf(int bits, int bit)143         public boolean isSeparatorInFrontOf(int bits, int bit) {
144             return bit % 4 == 0;
145         }
146     }
147 
dragValueSigned(long initial, int bits, double inc, boolean signed)148     private static long dragValueSigned(long initial, int bits, double inc, boolean signed) {
149         long max;
150         long min;
151         if (signed) {
152             long mask = Bits.mask(bits);
153             long signedFlag = Bits.signedFlagMask(bits);
154             if ((initial & signedFlag) != 0) initial |= ~mask;
155 
156             max = mask >>> 1;
157             min = -max - 1;
158         } else {
159             max = Bits.mask(bits);
160             min = 0;
161         }
162         return Math.max(min, Math.min(max, initial + Math.round(max * inc)));
163     }
164 
165     /**
166      * Base class of all formatters where the string to edit and the string to display are the same.
167      */
168     private static abstract class ValueFormatterViewEdit implements ValueFormatter {
169         private final boolean suitedForAddresses;
170 
ValueFormatterViewEdit(boolean suitedForAddresses)171         private ValueFormatterViewEdit(boolean suitedForAddresses) {
172             this.suitedForAddresses = suitedForAddresses;
173         }
174 
175         @Override
formatToView(Value inValue)176         public String formatToView(Value inValue) {
177             if (inValue.isHighZ())
178                 return inValue.toString();
179             else
180                 return format(inValue);
181         }
182 
183         @Override
formatToEdit(Value inValue)184         public String formatToEdit(Value inValue) {
185             if (inValue.isHighZ())
186                 return "Z";
187             else
188                 return format(inValue);
189         }
190 
191         @Override
isSuitedForAddresses()192         public boolean isSuitedForAddresses() {
193             return suitedForAddresses;
194         }
195 
format(Value value)196         protected abstract String format(Value value);
197 
198         @Override
dragValue(long initial, int bits, double inc)199         public long dragValue(long initial, int bits, double inc) {
200             return dragValueSigned(initial, bits, inc, false);
201         }
202     }
203 
204     /**
205      * the hexadecimal formatter
206      */
207     private static final class ValueFormatterHex extends ValueFormatterViewEdit {
208         private static final ValueFormatterHex INSTANCE = new ValueFormatterHex();
209 
ValueFormatterHex()210         private ValueFormatterHex() {
211             super(true);
212         }
213 
214         @Override
format(Value inValue)215         protected String format(Value inValue) {
216             final int bits = inValue.getBits();
217             final int numChars = (bits - 1) / 4 + 1;
218 
219             StringBuilder sb = new StringBuilder("0x");
220             final long value = inValue.getValue();
221             for (int i = numChars - 1; i >= 0; i--) {
222                 int c = (int) ((value >> (i * 4)) & 0xf);
223                 sb.append(DIGITS[c]);
224             }
225             return sb.toString();
226         }
227 
228         @Override
strLen(int bits)229         public int strLen(int bits) {
230             return (bits - 1) / 4 + 3;
231         }
232 
233         @Override
isSeparatorInFrontOf(int bits, int bit)234         public boolean isSeparatorInFrontOf(int bits, int bit) {
235             return bit % 4 == 0;
236         }
237     }
238 
239     /**
240      * Creates a short hex representation of the given value.
241      * Use only to represent a value.
242      * If confusion is excluded, the prefix '0x' is omitted.
243      * Thus 0x1A3 is converted to "1A3" which can not be parsed back to a long because "0x" is missing.
244      *
245      * @param value the value
246      * @return the hex string
247      */
toShortHex(long value)248     public static String toShortHex(long value) {
249         return toShortHex(value, false);
250     }
251 
252     private static final int BUF = 16;
253 
toShortHex(long value, boolean omitPrefix)254     private static String toShortHex(long value, boolean omitPrefix) {
255         if (value == 0)
256             return "0";
257 
258         boolean wasChar = false;
259         int p = BUF;
260         char[] data = new char[BUF];
261         while (value != 0) {
262             final int d = (int) (value & 0xf);
263             if (d >= 10) wasChar = true;
264             p--;
265             data[p] = DIGITS[d];
266             value >>>= 4;
267         }
268 
269         if (omitPrefix || wasChar || p == BUF - 1)
270             return new String(data, p, BUF - p);
271         else
272             return "0x" + new String(data, p, BUF - p);
273     }
274 
275     /**
276      * the octal formatter
277      */
278     private static final class ValueFormatterOctal extends ValueFormatterViewEdit {
279 
ValueFormatterOctal()280         private ValueFormatterOctal() {
281             super(true);
282         }
283 
284         @Override
strLen(int bits)285         public int strLen(int bits) {
286             return (bits - 1) / 3 + 3;
287         }
288 
289         @Override
format(Value inValue)290         protected String format(Value inValue) {
291             final int bits = inValue.getBits();
292             final int numChars = (bits - 1) / 3 + 1;
293 
294             StringBuilder sb = new StringBuilder("0");
295             final long value = inValue.getValue();
296             for (int i = numChars - 1; i >= 0; i--) {
297                 int c = (int) ((value >> (i * 3)) & 0x7);
298                 sb.append(DIGITS[c]);
299             }
300             return sb.toString();
301         }
302 
303         @Override
isSeparatorInFrontOf(int bits, int bit)304         public boolean isSeparatorInFrontOf(int bits, int bit) {
305             return bit % 3 == 0;
306         }
307     }
308 
309     /**
310      * the binary formatter
311      */
312     private static final class ValueFormatterBinary extends ValueFormatterViewEdit {
313 
ValueFormatterBinary()314         private ValueFormatterBinary() {
315             super(false); // column becomes to wide
316         }
317 
318         @Override
strLen(int bits)319         public int strLen(int bits) {
320             return bits + 2;
321         }
322 
323         @Override
format(Value inValue)324         protected String format(Value inValue) {
325             final int bits = inValue.getBits();
326             char[] data = new char[bits];
327             final long value = inValue.getValue();
328             long mask = 1;
329             for (int i = bits - 1; i >= 0; i--) {
330                 if ((value & mask) != 0)
331                     data[i] = '1';
332                 else
333                     data[i] = '0';
334                 mask <<= 1;
335             }
336             return "0b" + new String(data);
337         }
338     }
339 
340     /**
341      * The ascii formatter
342      */
343     private static final class ValueFormatterAscii extends ValueFormatterViewEdit {
344 
ValueFormatterAscii()345         private ValueFormatterAscii() {
346             super(false); // does not represent all values
347         }
348 
349         @Override
strLen(int bits)350         public int strLen(int bits) {
351             return 3;
352         }
353 
354         @Override
format(Value value)355         protected String format(Value value) {
356             return "'" + ((char) value.getValue()) + "'";
357         }
358     }
359 
360     /**
361      * The decimal value formatter
362      */
363     private static final class ValueFormatterDecimal extends ValueFormatterViewEdit {
364         private final boolean signed;
365 
ValueFormatterDecimal(boolean signed)366         private ValueFormatterDecimal(boolean signed) {
367             super(true);
368             this.signed = signed;
369         }
370 
371         @Override
strLen(int bits)372         public int strLen(int bits) {
373             if (signed)
374                 return decStrLen(bits - 1) + 1;
375             else
376                 return decStrLen(bits);
377         }
378 
379         @Override
format(Value value)380         protected String format(Value value) {
381             if (signed)
382                 return Long.toString(value.getValueSigned());
383             else
384                 return Long.toString(value.getValue());
385         }
386 
387         @Override
dragValue(long initial, int bits, double inc)388         public long dragValue(long initial, int bits, double inc) {
389             return dragValueSigned(initial, bits, inc, signed);
390         }
391     }
392 
decStrLen(int bits)393     private static int decStrLen(int bits) {
394         if (bits == 64)
395             return 20;
396         else if (bits == 63) {
397             return 19;
398         } else
399             return (int) Math.ceil(Math.log10(1L << bits));
400     }
401 
402 
403     /**
404      * Fixed point formatter
405      */
406     private static final class ValueFormatterFixedPoint implements ValueFormatter {
407         private static final int[] TABLE = new int[]{
408                 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 14, 15, 16, 17, 17,
409                 18, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21,
410                 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
411                 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22};
412 
413         private final int fixedPoint;
414         private final boolean signed;
415         private final double divisor;
416 
417         /**
418          * Creates a new generic instance
419          *
420          * @param attr   the defining elements attributes
421          * @param signed signed
422          */
ValueFormatterFixedPoint(ElementAttributes attr, boolean signed)423         private ValueFormatterFixedPoint(ElementAttributes attr, boolean signed) {
424             fixedPoint = attr.get(Keys.FIXED_POINT);
425             divisor = Bits.up(1, fixedPoint);
426             this.signed = signed;
427         }
428 
429         @Override
formatToView(Value inValue)430         public String formatToView(Value inValue) {
431             if (inValue.isHighZ())
432                 return inValue.toString();
433             return format(inValue);
434         }
435 
436         @Override
formatToEdit(Value inValue)437         public String formatToEdit(Value inValue) {
438             if (inValue.isHighZ())
439                 return "Z";
440             return format(inValue) + ":" + fixedPoint;
441         }
442 
443         @Override
strLen(int bits)444         public int strLen(int bits) {
445             int fp = fixedPoint;
446             if (fp >= TABLE.length) fp = TABLE.length - 1;
447             return decStrLen(Math.max(1, bits - fp)) + TABLE[fp];
448         }
449 
450         @Override
isSuitedForAddresses()451         public boolean isSuitedForAddresses() {
452             return false;
453         }
454 
format(Value inValue)455         private String format(Value inValue) {
456             if (signed)
457                 return Double.toString(inValue.getValueSigned() / divisor);
458             else
459                 return Double.toString(inValue.getValue() / divisor);
460         }
461 
462         @Override
dragValue(long initial, int bits, double inc)463         public long dragValue(long initial, int bits, double inc) {
464             return dragValueSigned(initial, bits, inc, signed);
465         }
466 
467         @Override
isSeparatorInFrontOf(int bits, int bit)468         public boolean isSeparatorInFrontOf(int bits, int bit) {
469             return bit == fixedPoint;
470         }
471     }
472 
473     /**
474      * Floating point formatter
475      */
476     private static final class ValueFormatterFloat implements ValueFormatter {
477         private static final int SIZE32 = Float.toString((float) -Math.PI).length();
478         private static final int SIZE64 = Double.toString(-Math.PI).length();
479 
480         @Override
formatToView(Value inValue)481         public String formatToView(Value inValue) {
482             if (inValue.isHighZ())
483                 return inValue.toString();
484 
485             switch (inValue.getBits()) {
486                 case 32:
487                     return Float.toString(Float.intBitsToFloat((int) inValue.getValue()));
488                 case 64:
489                     return Double.toString(Double.longBitsToDouble(inValue.getValue()));
490                 default:
491                     return HEX_FORMATTER.formatToView(inValue);
492             }
493         }
494 
495         @Override
formatToEdit(Value inValue)496         public String formatToEdit(Value inValue) {
497             if (inValue.isHighZ())
498                 return "Z";
499 
500             switch (inValue.getBits()) {
501                 case 32:
502                     float f = Float.intBitsToFloat((int) inValue.getValue());
503                     if (Float.isFinite(f))
504                         return Float.toString(f);
505                     else
506                         return HEX_FORMATTER.formatToEdit(inValue);
507                 case 64:
508                     double d = Double.longBitsToDouble(inValue.getValue());
509                     if (Double.isFinite(d))
510                         return d + "d";
511                     else
512                         return HEX_FORMATTER.formatToEdit(inValue);
513                 default:
514                     return HEX_FORMATTER.formatToEdit(inValue);
515             }
516         }
517 
518         @Override
strLen(int bits)519         public int strLen(int bits) {
520             switch (bits) {
521                 case 32:
522                     return SIZE32;
523                 case 64:
524                     return SIZE64;
525                 default:
526                     return HEX_FORMATTER.strLen(bits);
527             }
528         }
529 
530         @Override
isSuitedForAddresses()531         public boolean isSuitedForAddresses() {
532             return false;
533         }
534 
535         @Override
dragValue(long initialInt, int bits, double inc)536         public long dragValue(long initialInt, int bits, double inc) {
537             double initial;
538             if (bits == 32)
539                 initial = Float.intBitsToFloat((int) initialInt);
540             else if (bits == 64)
541                 initial = Double.longBitsToDouble(initialInt);
542             else
543                 return HEX_FORMATTER.dragValue(initialInt, bits, inc);
544 
545             if (!Double.isFinite(initial))
546                 initial = 0;
547 
548             double fac = Math.exp(Math.abs(inc) * 15) / 1000;
549             double delta = Math.abs(initial == 0 ? 1 : initial) * fac * Math.signum(inc);
550             double exp = Math.pow(10, Math.floor(Math.log10(Math.abs(delta))));
551             double val = Math.round((initial + delta) / exp) * exp;
552 
553             if (bits == 32)
554                 return Float.floatToIntBits((float) val);
555             else
556                 return Double.doubleToLongBits(val);
557         }
558 
559         @Override
isSeparatorInFrontOf(int bits, int bit)560         public boolean isSeparatorInFrontOf(int bits, int bit) {
561             switch (bits) {
562                 case 32:
563                     return bit == 31 || bit == 23;
564                 case 64:
565                     return bit == 63 || bit == 52;
566                 default:
567                     return HEX_FORMATTER.isSeparatorInFrontOf(bits, bit);
568             }
569         }
570     }
571 
572 }
573