mirror of
https://github.com/BorysLevytskyi/BitwiseCmd.git
synced 2025-12-23 05:02:48 +01:00
Do not pad negative numbers (#50)
This commit is contained in:
@@ -98,7 +98,7 @@ export class Integer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toString(base?:number) {
|
toString(base?:number) {
|
||||||
return formatter.numberToString(this, base || 10);
|
return formatter.numberToString(this, base || 10, this.maxBitSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
num() {
|
num() {
|
||||||
|
|||||||
@@ -74,14 +74,20 @@ describe('calc.addSpace', () => {
|
|||||||
describe('calc.numberOfBitsDisplayed', () => {
|
describe('calc.numberOfBitsDisplayed', () => {
|
||||||
it('calculates number of bits', () => {
|
it('calculates number of bits', () => {
|
||||||
expect(calc.numberOfBitsDisplayed(1)).toBe(1);
|
expect(calc.numberOfBitsDisplayed(1)).toBe(1);
|
||||||
expect(calc.numberOfBitsDisplayed(BigInt(-1))).toBe(32);
|
expect(calc.numberOfBitsDisplayed(BigInt(-1))).toBe(1);
|
||||||
expect(calc.numberOfBitsDisplayed(2)).toBe(2);
|
expect(calc.numberOfBitsDisplayed(2)).toBe(2);
|
||||||
expect(calc.numberOfBitsDisplayed(3)).toBe(2);
|
expect(calc.numberOfBitsDisplayed(3)).toBe(2);
|
||||||
expect(calc.numberOfBitsDisplayed(68719476735)).toBe(36);
|
expect(calc.numberOfBitsDisplayed(68719476735)).toBe(36);
|
||||||
expect(calc.numberOfBitsDisplayed(INT32_MIN_VALUE-1)).toBe(64);
|
expect(calc.numberOfBitsDisplayed(INT32_MIN_VALUE-1)).toBe(32);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('calc.xor', () => {
|
||||||
|
it('positive and negative nubmer', () => {
|
||||||
|
expect(calc.xor(Integer.int(-1), Integer.int(10)).num()).toBe(-11);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
describe('calc.lshift', () => {
|
describe('calc.lshift', () => {
|
||||||
|
|
||||||
it("respects bit size", () => {
|
it("respects bit size", () => {
|
||||||
@@ -167,10 +173,12 @@ describe("calc misc", () => {
|
|||||||
|
|
||||||
it('promoteTo64Bit', () => {
|
it('promoteTo64Bit', () => {
|
||||||
const n = asInteger(-1);
|
const n = asInteger(-1);
|
||||||
expect(calc.toBinaryString(calc.promoteTo64Bit(n))).toBe("11111111111111111111111111111111");
|
expect(calc.toBinaryString(calc.promoteTo64Bit(n))).toBe("1");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('binaryRepresentation', () => {
|
it('binaryRepresentation', () => {
|
||||||
|
|
||||||
|
expect(calc.toBinaryString(Integer.int(-2147483647))).toBe("0000000000000000000000000000001");
|
||||||
expect(calc.toBinaryString(asInteger(2147483647))).toBe("1111111111111111111111111111111");
|
expect(calc.toBinaryString(asInteger(2147483647))).toBe("1111111111111111111111111111111");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
addSpace(number: Integer, requiredSpace: number) : Integer {
|
addSpace(number: Integer, requiredSpace: number) : Integer {
|
||||||
|
|
||||||
|
if(requiredSpace < 0)
|
||||||
|
throw new Error("Required space cannot be negative");
|
||||||
|
|
||||||
const totalSpaceRequired = number.maxBitSize + requiredSpace;
|
const totalSpaceRequired = number.maxBitSize + requiredSpace;
|
||||||
return new Integer(number.value, nextPowOfTwo(totalSpaceRequired));
|
return new Integer(number.value, nextPowOfTwo(totalSpaceRequired));
|
||||||
},
|
},
|
||||||
@@ -45,10 +49,10 @@ export default {
|
|||||||
throw new Error(`Binary represenation '${bin}' is bigger than the given bit size ${bitSize}`)
|
throw new Error(`Binary represenation '${bin}' is bigger than the given bit size ${bitSize}`)
|
||||||
|
|
||||||
const r = num.value < 0
|
const r = num.value < 0
|
||||||
? this.engine.applyTwosComplement(bin.padStart(bitSize, '0'))
|
? this.engine.applyTwosComplement(bin)
|
||||||
: bin;
|
: bin;
|
||||||
|
|
||||||
return r;
|
return bin.length != bitSize ? r.substring(r.length-bin.length) : r;
|
||||||
},
|
},
|
||||||
|
|
||||||
lshift (num: Integer, numBytes : JsNumber) : Integer {
|
lshift (num: Integer, numBytes : JsNumber) : Integer {
|
||||||
@@ -105,7 +109,7 @@ export default {
|
|||||||
|
|
||||||
_applySingle(num: Integer, operation: (bin:string) => string) : Integer {
|
_applySingle(num: Integer, operation: (bin:string) => string) : Integer {
|
||||||
|
|
||||||
let bin = this.toBinaryString(num).padStart(num.maxBitSize, '0');
|
let bin = this.toBinaryString(num).padStart(num.maxBitSize, num.value < 0 ? '1' : '0');
|
||||||
|
|
||||||
bin = operation(bin);
|
bin = operation(bin);
|
||||||
|
|
||||||
@@ -127,8 +131,8 @@ export default {
|
|||||||
|
|
||||||
const [num1, num2] = equalizeSize(op1, op2);
|
const [num1, num2] = equalizeSize(op1, op2);
|
||||||
|
|
||||||
let bin1 = this.toBinaryString(num1).padStart(num1.maxBitSize, '0');
|
let bin1 = this.toBinaryString(num1).padStart(num1.maxBitSize, num1.value < 0 ? '1' : '0');
|
||||||
let bin2 = this.toBinaryString(num2).padStart(num2.maxBitSize, '0');
|
let bin2 = this.toBinaryString(num2).padStart(num2.maxBitSize, num2.value < 0 ? '1' : '0');
|
||||||
|
|
||||||
let resultBin = operation(bin1, bin2);
|
let resultBin = operation(bin1, bin2);
|
||||||
|
|
||||||
@@ -225,7 +229,9 @@ export default {
|
|||||||
flipped.unshift(bin.charAt(i) == "1" ? "0" : "1");
|
flipped.unshift(bin.charAt(i) == "1" ? "0" : "1");
|
||||||
}
|
}
|
||||||
|
|
||||||
return flipped.join('') + bin.substring(lastIndex) ;
|
const result = flipped.join('') + bin.substring(lastIndex);
|
||||||
|
//logLines(bin + " " + bin.length, result + " " + result.length);
|
||||||
|
return result;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export type BinaryStringViewProps = {
|
|||||||
className?: string;
|
className?: string;
|
||||||
disableHighlight?: boolean,
|
disableHighlight?: boolean,
|
||||||
signBitIndex?: number,
|
signBitIndex?: number,
|
||||||
integerBitSize?: number
|
valueBitSize?: number
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FlipBitEventArg = {
|
export type FlipBitEventArg = {
|
||||||
@@ -17,7 +17,7 @@ export type FlipBitEventArg = {
|
|||||||
binaryStringLength: number;
|
binaryStringLength: number;
|
||||||
$event: any;
|
$event: any;
|
||||||
newBinaryString: any;
|
newBinaryString: any;
|
||||||
isTypeExtend: boolean
|
isExtraBit: boolean
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class BinaryStringView extends React.Component<BinaryStringViewProps> {
|
export default class BinaryStringView extends React.Component<BinaryStringViewProps> {
|
||||||
@@ -39,7 +39,7 @@ export default class BinaryStringView extends React.Component<BinaryStringViewPr
|
|||||||
binaryStringLength: this.props.binaryString.length,
|
binaryStringLength: this.props.binaryString.length,
|
||||||
newBinaryString: newBinaryString,
|
newBinaryString: newBinaryString,
|
||||||
$event: e,
|
$event: e,
|
||||||
isTypeExtend: isExtra
|
isExtraBit: isExtra
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,8 +58,8 @@ export default class BinaryStringView extends React.Component<BinaryStringViewPr
|
|||||||
const css = allowFlipBits ? ' flipable' : ''
|
const css = allowFlipBits ? ' flipable' : ''
|
||||||
|
|
||||||
const disableHighlight = this.props.disableHighlight || false;
|
const disableHighlight = this.props.disableHighlight || false;
|
||||||
const firstBitIndex = this.props.integerBitSize != null
|
const firstBitIndex = this.props.valueBitSize != null
|
||||||
? bitChars.length - this.props.integerBitSize
|
? bitChars.length - this.props.valueBitSize
|
||||||
: -1;
|
: -1;
|
||||||
|
|
||||||
return bitChars.map((c, i) => {
|
return bitChars.map((c, i) => {
|
||||||
|
|||||||
@@ -12,8 +12,10 @@ describe("formatter", () => {
|
|||||||
const minusOne = BigInt(-1);
|
const minusOne = BigInt(-1);
|
||||||
const n32 = new Integer(minusOne, 32);
|
const n32 = new Integer(minusOne, 32);
|
||||||
const n64 = new Integer(minusOne, 64);
|
const n64 = new Integer(minusOne, 64);
|
||||||
expect(formatter.bin(n32)).toBe("11111111111111111111111111111111");
|
expect(formatter.bin(n32)).toBe("1");
|
||||||
expect(formatter.bin(n64)).toBe("1111111111111111111111111111111111111111111111111111111111111111");
|
expect(formatter.bin(n64)).toBe("1");
|
||||||
|
expect(formatter.fullBin(n32)).toBe("11111111111111111111111111111111");
|
||||||
|
expect(formatter.fullBin(n64)).toBe("1111111111111111111111111111111111111111111111111111111111111111");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('formats large binary number correctly', () => {
|
it('formats large binary number correctly', () => {
|
||||||
@@ -25,9 +27,10 @@ describe("formatter", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('formats negative binary numbers', () => {
|
it('formats negative binary numbers', () => {
|
||||||
expect(formatter.numberToString(-1, 'bin')).toBe("11111111111111111111111111111111");
|
expect(formatter.numberToString(-1, 'bin')).toBe("1");
|
||||||
|
expect(formatter.numberToString(-1, 'bin', 32)).toBe("11111111111111111111111111111111");
|
||||||
expect(formatter.numberToString(-0, 'bin')).toBe("0");
|
expect(formatter.numberToString(-0, 'bin')).toBe("0");
|
||||||
expect(formatter.numberToString(-2147483647, 'bin')).toBe("10000000000000000000000000000001");
|
expect(formatter.numberToString(-2147483647, 'bin')).toBe("0000000000000000000000000000001");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('pads left', () => {
|
it('pads left', () => {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Integer, JsNumber, asInteger } from "./Integer";
|
|||||||
export type NumberBase = 'dec' | 'hex' | 'bin';
|
export type NumberBase = 'dec' | 'hex' | 'bin';
|
||||||
|
|
||||||
const formatter = {
|
const formatter = {
|
||||||
numberToString: function(num: Integer | JsNumber, base: NumberBase | number) : string {
|
numberToString: function(num: Integer | JsNumber, base: NumberBase | number, padLength?: number) : string {
|
||||||
|
|
||||||
num = asInteger(num);
|
num = asInteger(num);
|
||||||
base = typeof base == "string" ? getBase(base) : base;
|
base = typeof base == "string" ? getBase(base) : base;
|
||||||
@@ -13,7 +13,13 @@ const formatter = {
|
|||||||
var hexVal = calc.abs(num).value.toString(16);
|
var hexVal = calc.abs(num).value.toString(16);
|
||||||
return num.value >= 0 ? '0x' + hexVal : '-0x' + hexVal;
|
return num.value >= 0 ? '0x' + hexVal : '-0x' + hexVal;
|
||||||
case 2:
|
case 2:
|
||||||
return calc.toBinaryString(num);
|
const bin = calc.toBinaryString(num);
|
||||||
|
|
||||||
|
if(padLength == null)
|
||||||
|
return bin;
|
||||||
|
|
||||||
|
const padChar = num.value >= 0 ? '0' : '1';
|
||||||
|
return bin.padStart(padLength, padChar);
|
||||||
case 10:
|
case 10:
|
||||||
return num.value.toString(10);
|
return num.value.toString(10);
|
||||||
default:
|
default:
|
||||||
@@ -36,6 +42,9 @@ const formatter = {
|
|||||||
bin(number: Integer | JsNumber) {
|
bin(number: Integer | JsNumber) {
|
||||||
return this.numberToString(number, 'bin');
|
return this.numberToString(number, 'bin');
|
||||||
},
|
},
|
||||||
|
fullBin(number: Integer) {
|
||||||
|
return this.numberToString(number, 'bin', number.maxBitSize);
|
||||||
|
},
|
||||||
emBin(number: Integer | JsNumber) {
|
emBin(number: Integer | JsNumber) {
|
||||||
return this.padLeft(this.bin(number), 8, '0');
|
return this.padLeft(this.bin(number), 8, '0');
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,13 +7,15 @@ import { Operator, Operand, ListOfNumbers } from '../expression';
|
|||||||
import calc from '../../core/calc';
|
import calc from '../../core/calc';
|
||||||
import { Integer } from '../../core/Integer';
|
import { Integer } from '../../core/Integer';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { faUndo } from '@fortawesome/free-solid-svg-icons';
|
import { faInfoCircle, faTriangleExclamation, faUndo } from '@fortawesome/free-solid-svg-icons';
|
||||||
import loglevel from 'loglevel';
|
import loglevel from 'loglevel';
|
||||||
|
import IconWithToolTip from '../../shell/components/IconWithTooltip';
|
||||||
|
|
||||||
type BitwiseResultViewProps = {
|
type BitwiseResultViewProps = {
|
||||||
expression: Expression;
|
expression: Expression;
|
||||||
emphasizeBytes: boolean;
|
emphasizeBytes: boolean;
|
||||||
annotateTypes: boolean
|
annotateTypes: boolean,
|
||||||
|
dimExtrBits: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
type BitwiseResultViewState = {
|
type BitwiseResultViewState = {
|
||||||
@@ -31,31 +33,37 @@ export default class BitwiseResultView extends React.Component<BitwiseResultView
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
||||||
let model : BitwiseResultViewModel | null = null
|
let model: BitwiseResultViewModel | null = null
|
||||||
const allowSignChange = this.props.expression instanceof ListOfNumbers;
|
const allowSignChange = this.props.expression instanceof ListOfNumbers;
|
||||||
|
|
||||||
|
|
||||||
try
|
try {
|
||||||
{
|
model = BitwiseResultViewModel.createModel(this.props.expression, this.props.emphasizeBytes, this.props.annotateTypes);
|
||||||
model = BitwiseResultViewModel.createModel(this.props.expression, this.props.emphasizeBytes);
|
|
||||||
}
|
}
|
||||||
catch(err) {
|
catch (err) {
|
||||||
const text = (err as any).message;
|
const text = (err as any).message;
|
||||||
return <div className='error'>Error: {text}</div>
|
return <div className='error'>Error: {text}</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
var rows = this.getRows(model!, allowSignChange);
|
const rows = this.getRows(model!, allowSignChange);
|
||||||
|
|
||||||
return <table className="expression">
|
let css = "expression";
|
||||||
<tbody>
|
|
||||||
{rows}
|
if(this.props.dimExtrBits)
|
||||||
</tbody>
|
css += " dim-extra-bits";
|
||||||
</table>
|
|
||||||
|
return <React.Fragment>
|
||||||
|
<table className={css}>
|
||||||
|
<tbody>
|
||||||
|
{rows}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
|
|
||||||
getRows(model: BitwiseResultViewModel, allowSignChange : boolean): JSX.Element[] {
|
getRows(model: BitwiseResultViewModel, allowSignChange: boolean): JSX.Element[] {
|
||||||
|
|
||||||
this.maxSeenLengthNumberOfBits = Math.max(model.maxNumberOfBits, this.maxSeenLengthNumberOfBits);
|
this.maxSeenLengthNumberOfBits = model.maxNumberOfBits; //Math.max(model.maxNumberOfBits, this.maxSeenLengthNumberOfBits);
|
||||||
|
|
||||||
return model.items.map((itm, i) =>
|
return model.items.map((itm, i) =>
|
||||||
<ExpressionElementTableRow
|
<ExpressionElementTableRow
|
||||||
@@ -68,7 +76,7 @@ export default class BitwiseResultView extends React.Component<BitwiseResultView
|
|||||||
expressionItem={itm.expressionElement}
|
expressionItem={itm.expressionElement}
|
||||||
emphasizeBytes={this.props.emphasizeBytes}
|
emphasizeBytes={this.props.emphasizeBytes}
|
||||||
maxNumberOfBits={this.maxSeenLengthNumberOfBits}
|
maxNumberOfBits={this.maxSeenLengthNumberOfBits}
|
||||||
showInfoColumn={this.props.annotateTypes}
|
annotateTypes={this.props.annotateTypes}
|
||||||
onValueChanged={() => this.onValueChanged()} />);
|
onValueChanged={() => this.onValueChanged()} />);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,8 +96,8 @@ type ExpressionElementRowProps = {
|
|||||||
allowSignChange: boolean,
|
allowSignChange: boolean,
|
||||||
expressionItem: ExpressionElement,
|
expressionItem: ExpressionElement,
|
||||||
onValueChanged: any,
|
onValueChanged: any,
|
||||||
showInfoColumn: boolean,
|
annotateTypes: boolean
|
||||||
}
|
};
|
||||||
|
|
||||||
class ExpressionElementTableRow extends React.Component<ExpressionElementRowProps> {
|
class ExpressionElementTableRow extends React.Component<ExpressionElementRowProps> {
|
||||||
|
|
||||||
@@ -105,11 +113,11 @@ class ExpressionElementTableRow extends React.Component<ExpressionElementRowProp
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { sign, css, maxNumberOfBits, emphasizeBytes, allowFlipBits } = this.props;
|
const { sign, css, maxNumberOfBits, emphasizeBytes, allowFlipBits, annotateTypes } = this.props;
|
||||||
const scalar = this.props.expressionItem.evaluate();
|
const scalar = this.props.expressionItem.evaluate();
|
||||||
const padChar = scalar.value.value >= 0 ? '0' : '1';
|
const bin = formatter.numberToString(scalar.value, 'bin', maxNumberOfBits);
|
||||||
const bin = formatter.numberToString(scalar.value, 'bin').padStart(maxNumberOfBits, padChar);
|
|
||||||
const signBitIndex = scalar.value.signed && bin.length >= scalar.value.maxBitSize ? bin.length - scalar.value.maxBitSize : -1;
|
const signBitIndex = scalar.value.signed && bin.length >= scalar.value.maxBitSize ? bin.length - scalar.value.maxBitSize : -1;
|
||||||
|
const valueSize = annotateTypes ? scalar.value.maxBitSize : calc.numberOfBitsDisplayed(scalar.value);
|
||||||
|
|
||||||
return <tr className={"row-with-bits " + css}>
|
return <tr className={"row-with-bits " + css}>
|
||||||
<td className="sign">{sign}</td>
|
<td className="sign">{sign}</td>
|
||||||
@@ -122,25 +130,37 @@ class ExpressionElementTableRow extends React.Component<ExpressionElementRowProp
|
|||||||
binaryString={bin}
|
binaryString={bin}
|
||||||
allowFlipBits={allowFlipBits}
|
allowFlipBits={allowFlipBits}
|
||||||
signBitIndex={signBitIndex}
|
signBitIndex={signBitIndex}
|
||||||
integerBitSize={this.scalar.value.maxBitSize}
|
valueBitSize={valueSize}
|
||||||
onBitClicked={args => this.onBitClicked(args)} />
|
onBitClicked={args => this.onBitClicked(args)} />
|
||||||
</td>
|
</td>
|
||||||
<td className="other">{this.getAlternative()}</td>
|
<td className="other">{this.getAlternative()}</td>
|
||||||
<td className="info accent1" data-test-name='ignore'>{this.props.showInfoColumn ? this.getInfo() : null}</td>
|
<td className="info accent1" data-test-name='ignore'>{this.props.annotateTypes ? this.getInfo() : null}</td>
|
||||||
<td className='undo' data-test-name='ignore'>
|
<td className='undo' data-test-name='ignore'>
|
||||||
{this.getUndoButton()}
|
{this.getControlButtons()}
|
||||||
</td>
|
</td>
|
||||||
</tr>;
|
</tr>;
|
||||||
}
|
}
|
||||||
|
|
||||||
getUndoButton(): React.ReactNode {
|
getControlButtons(): React.ReactNode {
|
||||||
|
|
||||||
return !this.originalValue.isTheSame(this.scalar.value)
|
const buttons = [];
|
||||||
? <button title='Undo all changes' className='undo' data-control="undo" onClick={() => this.undo()}><FontAwesomeIcon icon={faUndo}/></button>
|
|
||||||
: null;
|
if (this.scalar.value.value < 0)
|
||||||
|
buttons.push(<IconWithToolTip icon={faInfoCircle}>
|
||||||
|
<div className='accent1 tooltip-header'>Two's Complement</div>
|
||||||
|
<p>
|
||||||
|
This is a negative number. It's binary representation is <u>inverted</u> using <strong>Two's Complement</strong> operation.
|
||||||
|
</p>
|
||||||
|
{this.props.annotateTypes ? null : <p>To see full in-memory binary representation, go to <b>Settings</b> and enable <b>Annotate Data Types</b> toggle. </p>}
|
||||||
|
</IconWithToolTip>)
|
||||||
|
|
||||||
|
if (!this.originalValue.isTheSame(this.scalar.value))
|
||||||
|
buttons.push(<button title='Undo all changes' className='undo' data-control="undo" onClick={() => this.undo()}><FontAwesomeIcon icon={faUndo} /></button>);
|
||||||
|
|
||||||
|
return <React.Fragment>{buttons}</React.Fragment>
|
||||||
}
|
}
|
||||||
|
|
||||||
getLabel(): string {
|
getLabel(): string {
|
||||||
|
|
||||||
// For expressions like |~2
|
// For expressions like |~2
|
||||||
// TODO: find a better way...
|
// TODO: find a better way...
|
||||||
@@ -177,23 +197,26 @@ getLabel(): string {
|
|||||||
|
|
||||||
onBitClicked(args: BitClickedEventArg) {
|
onBitClicked(args: BitClickedEventArg) {
|
||||||
|
|
||||||
const { bitIndex: index, binaryStringLength: totalLength } = args;
|
const { bitIndex, binaryStringLength: binaryStringLength } = args;
|
||||||
|
|
||||||
const maxBitSize = this.scalar.value.maxBitSize;
|
const maxBitSize = this.scalar.value.maxBitSize;
|
||||||
|
|
||||||
if(!args.isTypeExtend)
|
const rightIndex = binaryStringLength - bitIndex;
|
||||||
{
|
|
||||||
const pad = this.scalar.value.maxBitSize - totalLength;
|
if (rightIndex <= maxBitSize) {
|
||||||
const newValue = calc.flipBit(this.scalar.value, pad + index);
|
const pad = this.scalar.value.maxBitSize - binaryStringLength;
|
||||||
|
const newValue = calc.flipBit(this.scalar.value, pad + bitIndex);
|
||||||
this.changeValue(newValue);
|
this.changeValue(newValue);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const space = (totalLength - index - maxBitSize);
|
|
||||||
|
const space = (binaryStringLength - bitIndex - maxBitSize);
|
||||||
this.changeValue(calc.addSpace(this.scalar.value, space));
|
this.changeValue(calc.addSpace(this.scalar.value, space));
|
||||||
|
loglevel.debug("Operand size changed");
|
||||||
}
|
}
|
||||||
|
|
||||||
onChangeSign () {
|
onChangeSign() {
|
||||||
|
|
||||||
var op = this.props.expressionItem.getUnderlyingOperand();
|
var op = this.props.expressionItem.getUnderlyingOperand();
|
||||||
|
|
||||||
@@ -222,27 +245,18 @@ getLabel(): string {
|
|||||||
const signedOther = op.value.signed ? 'unsigned' : 'signed';
|
const signedOther = op.value.signed ? 'unsigned' : 'signed';
|
||||||
const signedButtonTitle = `Click to change to ${signedOther} preserving the same bits`;
|
const signedButtonTitle = `Click to change to ${signedOther} preserving the same bits`;
|
||||||
|
|
||||||
if(op.label.length > 0)
|
if (op.label.length > 0) {
|
||||||
{
|
|
||||||
text += " (converted)";
|
text += " (converted)";
|
||||||
title += ". This number was converted to facilitate bitwise operation with an operand of a different type.";
|
title += ". This number was converted to facilitate bitwise operation with an operand of a different type.";
|
||||||
}
|
}
|
||||||
|
|
||||||
children.push(<span title={title} style={{cursor:"help"}}>{text.trim()}</span>);
|
children.push(<span title={title} style={{ cursor: "help" }}>{text.trim()}</span>);
|
||||||
|
|
||||||
if(allowSignChange)
|
if (allowSignChange)
|
||||||
children.push(<button className='accent1' title={signedButtonTitle} onClick={() => this.onChangeSign()}>{signedStr}</button>);
|
children.push(<button className='accent1 link-button' title={signedButtonTitle} onClick={() => this.onChangeSign()}>{signedStr}</button>);
|
||||||
else
|
else
|
||||||
children.push(<span className='accent1'> {signedStr}</span>)
|
children.push(<span className='accent1'> {signedStr}</span>)
|
||||||
|
|
||||||
return <React.Fragment>{children}</React.Fragment>
|
return <React.Fragment>{children}</React.Fragment>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function willInfoColumnBeVisible(expr: ExpressionElement, maxNumberOfBits: number, allowSignChange : boolean) {
|
|
||||||
|
|
||||||
const op = expr.getUnderlyingOperand();
|
|
||||||
const allBitsDisplayed = op.value.maxBitSize != 32 || op.value.maxBitSize <= maxNumberOfBits;
|
|
||||||
const hasLabel = op.label.length > 0;
|
|
||||||
return allBitsDisplayed || hasLabel;
|
|
||||||
}
|
|
||||||
@@ -7,6 +7,7 @@ import exp from 'constants';
|
|||||||
type Config = {
|
type Config = {
|
||||||
emphasizeBytes: boolean;
|
emphasizeBytes: boolean;
|
||||||
allowFlipBits: boolean;
|
allowFlipBits: boolean;
|
||||||
|
annotateDataTypes: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExpressionRowModel = {
|
type ExpressionRowModel = {
|
||||||
@@ -24,9 +25,11 @@ export default class BitwiseResultViewModel {
|
|||||||
items: ExpressionRowModel[];
|
items: ExpressionRowModel[];
|
||||||
maxNumberOfBits: number;
|
maxNumberOfBits: number;
|
||||||
allowFlipBits: boolean;
|
allowFlipBits: boolean;
|
||||||
|
annotateDataTypes: boolean;
|
||||||
|
|
||||||
constructor({ emphasizeBytes = false, allowFlipBits = false} : Config) {
|
constructor({ emphasizeBytes = false, allowFlipBits = false, annotateDataTypes = false} : Config) {
|
||||||
this.emphasizeBytes = emphasizeBytes;
|
this.emphasizeBytes = emphasizeBytes;
|
||||||
|
this.annotateDataTypes = annotateDataTypes;
|
||||||
this.items = [];
|
this.items = [];
|
||||||
this.maxNumberOfBits = 0;
|
this.maxNumberOfBits = 0;
|
||||||
this.allowFlipBits = allowFlipBits === true;
|
this.allowFlipBits = allowFlipBits === true;
|
||||||
@@ -80,7 +83,7 @@ export default class BitwiseResultViewModel {
|
|||||||
};
|
};
|
||||||
|
|
||||||
addScalarRow(expr: Operand) {
|
addScalarRow(expr: Operand) {
|
||||||
const bits = calc.numberOfBitsDisplayed(expr.value);
|
const bits = this.calcMaxNumberOfBits(expr);
|
||||||
this.maxNumberOfBits = Math.max(bits, this.maxNumberOfBits);
|
this.maxNumberOfBits = Math.max(bits, this.maxNumberOfBits);
|
||||||
this.items.push({
|
this.items.push({
|
||||||
sign:'',
|
sign:'',
|
||||||
@@ -95,7 +98,7 @@ export default class BitwiseResultViewModel {
|
|||||||
addOperatorRow(expr: Operator) {
|
addOperatorRow(expr: Operator) {
|
||||||
|
|
||||||
const resultNumber = expr.isNotExpression ? expr.evaluate() : expr.getUnderlyingOperand();
|
const resultNumber = expr.isNotExpression ? expr.evaluate() : expr.getUnderlyingOperand();
|
||||||
const bits = calc.numberOfBitsDisplayed(resultNumber.value);
|
const bits = this.calcMaxNumberOfBits(resultNumber);
|
||||||
|
|
||||||
this.maxNumberOfBits = Math.max(bits, this.maxNumberOfBits);
|
this.maxNumberOfBits = Math.max(bits, this.maxNumberOfBits);
|
||||||
|
|
||||||
@@ -110,7 +113,7 @@ export default class BitwiseResultViewModel {
|
|||||||
};
|
};
|
||||||
|
|
||||||
addShiftExpressionResultRow(expr : Operator, resultExpr : Operand) {
|
addShiftExpressionResultRow(expr : Operator, resultExpr : Operand) {
|
||||||
const bits = calc.numberOfBitsDisplayed(resultExpr.value);
|
const bits = this.calcMaxNumberOfBits(resultExpr);
|
||||||
this.maxNumberOfBits = Math.max(bits, this.maxNumberOfBits);
|
this.maxNumberOfBits = Math.max(bits, this.maxNumberOfBits);
|
||||||
const child = expr.operand.getUnderlyingOperand();
|
const child = expr.operand.getUnderlyingOperand();
|
||||||
this.items.push({
|
this.items.push({
|
||||||
@@ -124,8 +127,10 @@ export default class BitwiseResultViewModel {
|
|||||||
};
|
};
|
||||||
|
|
||||||
addExpressionResultRow(expr : Operand) {
|
addExpressionResultRow(expr : Operand) {
|
||||||
const bits = calc.numberOfBitsDisplayed(expr.value);
|
|
||||||
|
const bits = this.calcMaxNumberOfBits(expr);
|
||||||
this.maxNumberOfBits = Math.max(bits, this.maxNumberOfBits);
|
this.maxNumberOfBits = Math.max(bits, this.maxNumberOfBits);
|
||||||
|
|
||||||
this.items.push({
|
this.items.push({
|
||||||
sign:'=',
|
sign:'=',
|
||||||
css: 'expression-result',
|
css: 'expression-result',
|
||||||
@@ -136,6 +141,10 @@ export default class BitwiseResultViewModel {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
calcMaxNumberOfBits (op: Operand) {
|
||||||
|
return this.annotateDataTypes ? op.value.maxBitSize : calc.numberOfBitsDisplayed(op.value);
|
||||||
|
}
|
||||||
|
|
||||||
getLabel (op: Operand) : string {
|
getLabel (op: Operand) : string {
|
||||||
|
|
||||||
return formatter.numberToString(op.value, op.base === 'bin' ? 'dec' : op.base)
|
return formatter.numberToString(op.value, op.base === 'bin' ? 'dec' : op.base)
|
||||||
@@ -155,19 +164,21 @@ export default class BitwiseResultViewModel {
|
|||||||
return bits;
|
return bits;
|
||||||
};
|
};
|
||||||
|
|
||||||
static createModel(expr : Expression, emphasizeBytes: boolean) : BitwiseResultViewModel {
|
static createModel(expr : Expression, emphasizeBytes: boolean, annotateDataTypes: boolean) : BitwiseResultViewModel {
|
||||||
|
|
||||||
if(expr instanceof ListOfNumbers) {
|
if(expr instanceof ListOfNumbers) {
|
||||||
return BitwiseResultViewModel.buildListOfNumbers(expr, {
|
return BitwiseResultViewModel.buildListOfNumbers(expr, {
|
||||||
emphasizeBytes: emphasizeBytes,
|
emphasizeBytes: emphasizeBytes,
|
||||||
allowFlipBits: true
|
allowFlipBits: true,
|
||||||
|
annotateDataTypes: annotateDataTypes
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if(expr instanceof BitwiseOperation) {
|
if(expr instanceof BitwiseOperation) {
|
||||||
return BitwiseResultViewModel.buildBitwiseOperation(expr, {
|
return BitwiseResultViewModel.buildBitwiseOperation(expr, {
|
||||||
emphasizeBytes: emphasizeBytes,
|
emphasizeBytes: emphasizeBytes,
|
||||||
allowFlipBits: true
|
allowFlipBits: true,
|
||||||
|
annotateDataTypes: annotateDataTypes
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const expressionAppModule = {
|
|||||||
canHandle: (input:string) => parser.canParse(input),
|
canHandle: (input:string) => parser.canParse(input),
|
||||||
handle: function(c: CommandInput) {
|
handle: function(c: CommandInput) {
|
||||||
var expr = parser.parse(c.input);
|
var expr = parser.parse(c.input);
|
||||||
appState.addCommandResult(c.input, () => <BitwiseResultView expression={expr!} emphasizeBytes={appState.emphasizeBytes} annotateTypes={appState.annotateTypes} />);
|
appState.addCommandResult(c.input, () => <BitwiseResultView expression={expr!} emphasizeBytes={appState.emphasizeBytes} annotateTypes={appState.annotateTypes} dimExtrBits={appState.dimExtraBits} />);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,15 @@ a.hashLink { font-size: 1.1em;}
|
|||||||
|
|
||||||
.hex .prefix { display: inline; }
|
.hex .prefix { display: inline; }
|
||||||
|
|
||||||
.indicator { padding: 0px 5px; background: transparent; border: none; cursor: pointer; vertical-align: middle; margin-left: -3em; color:rgba(0, 0, 0, 0.25) }
|
.indicator { padding: 0px 5px; background: transparent; border: none; cursor: pointer; vertical-align: middle; color:rgba(0, 0, 0, 0.25) }
|
||||||
|
|
||||||
|
|
||||||
|
.tooltip-holder { position: relative; display: inline}
|
||||||
|
.tooltip-holder .tooltip {display: none; font-size: 0.9em; position: absolute; margin-left: 10px; width: 300px; padding: 10px; border-radius: 5px;}
|
||||||
|
.tooltip-holder .tooltip p { margin-bottom: 0;}
|
||||||
|
.tooltip-holder .tooltip .tooltip-header { font-weight: bold; margin-bottom: 5px;}
|
||||||
|
.tooltip:hover { display: inline;}
|
||||||
|
.tooltip-holder:hover .tooltip { display: inline; }
|
||||||
|
|
||||||
.error { color: maroon; }
|
.error { color: maroon; }
|
||||||
|
|
||||||
@@ -86,9 +94,10 @@ a.hashLink { font-size: 1.1em;}
|
|||||||
|
|
||||||
.cur { color: lightgray; }
|
.cur { color: lightgray; }
|
||||||
|
|
||||||
.extra-bit { opacity: 0.3;}
|
.dim-extra-bits .extra-bit { opacity: 0.3;}
|
||||||
|
|
||||||
button { border: none; text-decoration: underline;}
|
button { border: none; }
|
||||||
|
button.link-button {text-decoration: underline;}
|
||||||
|
|
||||||
.settings-button {
|
.settings-button {
|
||||||
margin-left: -20px;
|
margin-left: -20px;
|
||||||
@@ -97,10 +106,15 @@ button { border: none; text-decoration: underline;}
|
|||||||
.undo button {
|
.undo button {
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.solid-border { border: solid 1px rgba(255, 255, 255, 0.8);}
|
||||||
|
|
||||||
|
|
||||||
/* Light */
|
/* Light */
|
||||||
.light { background: #fafafa; }
|
.light { background: #fafafa; }
|
||||||
|
.light .solid-background {background: #fafafa;}
|
||||||
.light .header-cmd { color: #919191 }
|
.light .header-cmd { color: #919191 }
|
||||||
.light a, .light a:visited, .light button { color: #222; }
|
.light a, .light a:visited, .light button { color: #222; }
|
||||||
.light .one { color: black; }
|
.light .one { color: black; }
|
||||||
@@ -120,9 +134,12 @@ button { border: none; text-decoration: underline;}
|
|||||||
.light .expressionInput { border-bottom: solid 1px rgba(0, 0, 0, 0.5);}
|
.light .expressionInput { border-bottom: solid 1px rgba(0, 0, 0, 0.5);}
|
||||||
.light .button { border: solid 1px gray; }
|
.light .button { border: solid 1px gray; }
|
||||||
.light .button:hover { background: rgba(0, 0, 0, 0.2);}
|
.light .button:hover { background: rgba(0, 0, 0, 0.2);}
|
||||||
|
.light .solid-border { border: solid 1px gray;}
|
||||||
|
.light .donate-result-view .paypal-button { border: solid 1px gray; }
|
||||||
|
|
||||||
/* Dark */
|
/* Dark */
|
||||||
.dark { background: #121212; color: white;}
|
.dark { background: #121212; color: white;}
|
||||||
|
.dark .solid-background {background: #121212;}
|
||||||
.dark .expression { color: white;}
|
.dark .expression { color: white;}
|
||||||
.dark .expressionInput { color: white; }
|
.dark .expressionInput { color: white; }
|
||||||
.dark a, .dark a:visited, .dark button { color: white; }
|
.dark a, .dark a:visited, .dark button { color: white; }
|
||||||
@@ -144,6 +161,7 @@ button { border: none; text-decoration: underline;}
|
|||||||
Midnight Theme
|
Midnight Theme
|
||||||
*/
|
*/
|
||||||
.midnight { background: #2c3e50; color: white }
|
.midnight { background: #2c3e50; color: white }
|
||||||
|
.midnight .solid-background {background: #2c3e50;}
|
||||||
.midnight .header-cmd { color: #7ea3b5 !important }
|
.midnight .header-cmd { color: #7ea3b5 !important }
|
||||||
.midnight .expression { color: white;}
|
.midnight .expression { color: white;}
|
||||||
.midnight .expressionInput { color: white;}
|
.midnight .expressionInput { color: white;}
|
||||||
|
|||||||
@@ -5,9 +5,8 @@ import { getNetworkAddress, getAddressSpaceSize } from '../subnet-utils';
|
|||||||
import IpAddressBinaryString from './IpAddressBinaryString';
|
import IpAddressBinaryString from './IpAddressBinaryString';
|
||||||
import { IpAddress, IpAddressWithSubnetMask, VpcCommand } from '../models';
|
import { IpAddress, IpAddressWithSubnetMask, VpcCommand } from '../models';
|
||||||
import formatter from '../../core/formatter';
|
import formatter from '../../core/formatter';
|
||||||
import Toggle from '../../shell/components/Toggle';
|
import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons';
|
import IconWithToolTip from '../../shell/components/IconWithTooltip';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
||||||
|
|
||||||
|
|
||||||
const MAX_NON_HOSTS_BITS = 30; // leave two bits for hosts min
|
const MAX_NON_HOSTS_BITS = 30; // leave two bits for hosts min
|
||||||
@@ -41,17 +40,13 @@ function SubnetView(props: { vpc: VpcCommand }) {
|
|||||||
<BinaryStringView binaryString={split.subnet} disableHighlight={true} className="address-space subnet-part" />
|
<BinaryStringView binaryString={split.subnet} disableHighlight={true} className="address-space subnet-part" />
|
||||||
<BinaryStringView binaryString={split.hosts} disableHighlight={true} className="address-space host-part" />
|
<BinaryStringView binaryString={split.hosts} disableHighlight={true} className="address-space host-part" />
|
||||||
<span className="address-space decimal-part">{networkAddress.toString()}</span>
|
<span className="address-space decimal-part">{networkAddress.toString()}</span>
|
||||||
<Toggle text="[i]" isOn={vpc.showLegend} onClick={() => setVpc(vpc.toggleLegend())} title="Show/Hide Color Legend">
|
|
||||||
<FontAwesomeIcon className="icon" icon={faQuestionCircle} size="sm" />
|
<IconWithToolTip icon={faInfoCircle}>
|
||||||
</Toggle>
|
<div className='accent1 tooltip-header'>Color Legend</div>
|
||||||
</div>
|
<span className="address-space soft">000</span> - VPC address bits <br/>
|
||||||
<div style={{"display" : vpc.showLegend ? '' : 'none'}}>
|
<span className="address-space subnet-part">000</span> - Bits dedicated for subnets address<br/>
|
||||||
<p>
|
<span className="address-space host-part">000</span> - Bits dedicated to host addresses inside each subnet
|
||||||
Color Legend
|
</IconWithToolTip>
|
||||||
</p>
|
|
||||||
<span className="address-space soft">000</span> - VPC address bits <br/>
|
|
||||||
<span className="address-space subnet-part">000</span> - Bits dedicated for subnets address<br/>
|
|
||||||
<span className="address-space host-part">000</span> - Bits dedicated to host addresses inside each subnet
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -130,13 +125,11 @@ class VpcModel {
|
|||||||
cidr: IpAddressWithSubnetMask;
|
cidr: IpAddressWithSubnetMask;
|
||||||
subnetBits: number;
|
subnetBits: number;
|
||||||
subnetNum: number;
|
subnetNum: number;
|
||||||
showLegend: boolean;
|
|
||||||
|
|
||||||
constructor(cidr: IpAddressWithSubnetMask, subnetBits: number) {
|
constructor(cidr: IpAddressWithSubnetMask, subnetBits: number) {
|
||||||
this.cidr = cidr;
|
this.cidr = cidr;
|
||||||
this.subnetBits = subnetBits;
|
this.subnetBits = subnetBits;
|
||||||
this.subnetNum = 0;
|
this.subnetNum = 0;
|
||||||
this.showLegend = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static create(vpc: VpcCommand) {
|
static create(vpc: VpcCommand) {
|
||||||
@@ -154,10 +147,4 @@ class VpcModel {
|
|||||||
changeVpcCidr(newCidr: IpAddressWithSubnetMask) {
|
changeVpcCidr(newCidr: IpAddressWithSubnetMask) {
|
||||||
return new VpcModel(newCidr, this.subnetBits);
|
return new VpcModel(newCidr, this.subnetBits);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleLegend() {
|
|
||||||
var n = new VpcModel(this.cidr, this.subnetBits);
|
|
||||||
n.showLegend = !this.showLegend;
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ export type PersistedAppData = {
|
|||||||
debugMode: boolean | null;
|
debugMode: boolean | null;
|
||||||
pageVisistsCount: number;
|
pageVisistsCount: number;
|
||||||
donationClicked: boolean;
|
donationClicked: boolean;
|
||||||
annotateTypes: boolean
|
annotateTypes: boolean;
|
||||||
|
dimExtrBits: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CommandResultView = {
|
export type CommandResultView = {
|
||||||
@@ -37,23 +38,25 @@ export default class AppState {
|
|||||||
donationClicked: boolean;
|
donationClicked: boolean;
|
||||||
showSettings: boolean = false;
|
showSettings: boolean = false;
|
||||||
annotateTypes: boolean = false;
|
annotateTypes: boolean = false;
|
||||||
|
dimExtraBits: boolean = false;
|
||||||
|
|
||||||
constructor(persistData : PersistedAppData, env: string) {
|
constructor(persistData: PersistedAppData, env: string) {
|
||||||
|
|
||||||
this.env = env;
|
this.env = env;
|
||||||
|
|
||||||
this.emphasizeBytes = !!persistData.emphasizeBytes;
|
this.emphasizeBytes = !!persistData.emphasizeBytes;
|
||||||
this.persistedVersion = persistData.version || 0.1;
|
this.persistedVersion = persistData.version || 0.1;
|
||||||
this.wasOldVersion = persistData.version != null && this.version > this.persistedVersion;
|
this.wasOldVersion = persistData.version != null && this.version > this.persistedVersion;
|
||||||
this.debugMode = persistData.debugMode === true;
|
this.debugMode = persistData.debugMode === true;
|
||||||
this.pageVisitsCount = persistData.pageVisistsCount || 0;
|
this.pageVisitsCount = persistData.pageVisistsCount || 0;
|
||||||
this.donationClicked = persistData.donationClicked;
|
this.donationClicked = persistData.donationClicked;
|
||||||
this.annotateTypes = !!persistData.annotateTypes;
|
this.annotateTypes = !!persistData.annotateTypes;
|
||||||
|
this.dimExtraBits = !!persistData.dimExtrBits;
|
||||||
}
|
}
|
||||||
|
|
||||||
addCommandResult(input : string, view : ViewFactory) {
|
addCommandResult(input: string, view: ViewFactory) {
|
||||||
const key = generateKey();
|
const key = generateKey();
|
||||||
this.commandResults.unshift({key, input, view});
|
this.commandResults.unshift({ key, input, view });
|
||||||
log.debug(`command result added: ${input}`);
|
log.debug(`command result added: ${input}`);
|
||||||
this.triggerChanged();
|
this.triggerChanged();
|
||||||
}
|
}
|
||||||
@@ -64,19 +67,19 @@ export default class AppState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
removeResult(index: number) {
|
removeResult(index: number) {
|
||||||
if(index < 0 || index >= this.commandResults.length)
|
if (index < 0 || index >= this.commandResults.length)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.commandResults.splice(index, 1);
|
this.commandResults.splice(index, 1);
|
||||||
this.triggerChanged();
|
this.triggerChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleEmphasizeBytes() {
|
toggleEmphasizeBytes(value?: boolean) {
|
||||||
this.emphasizeBytes = !this.emphasizeBytes;
|
this.emphasizeBytes = value != null ? value : !this.emphasizeBytes;
|
||||||
this.triggerChanged();
|
this.triggerChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
onChange(handler : AppStateChangeHandler) {
|
onChange(handler: AppStateChangeHandler) {
|
||||||
this.changeHandlers.push(handler);
|
this.changeHandlers.push(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,8 +88,8 @@ export default class AppState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setUiTheme(theme: string) {
|
setUiTheme(theme: string) {
|
||||||
this.uiTheme = theme;
|
this.uiTheme = theme;
|
||||||
this.triggerChanged();
|
this.triggerChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleDebugMode() {
|
toggleDebugMode() {
|
||||||
@@ -99,8 +102,13 @@ export default class AppState {
|
|||||||
this.triggerChanged();
|
this.triggerChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleAnnotateTypes() {
|
toggleAnnotateTypes(value?: boolean) {
|
||||||
this.annotateTypes = !this.annotateTypes;
|
this.annotateTypes = value != null ? value : !this.annotateTypes;
|
||||||
|
this.triggerChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleDimExtrBits() {
|
||||||
|
this.dimExtraBits = !this.dimExtraBits;
|
||||||
this.triggerChanged();
|
this.triggerChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,15 +117,15 @@ export default class AppState {
|
|||||||
this.triggerChanged();
|
this.triggerChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
onDonationClicked() : boolean{
|
onDonationClicked(): boolean {
|
||||||
if(this.donationClicked === true) return false;
|
if (this.donationClicked === true) return false;
|
||||||
|
|
||||||
this.donationClicked = true;
|
this.donationClicked = true;
|
||||||
this.triggerChanged();
|
this.triggerChanged();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getPersistData() : PersistedAppData {
|
getPersistData(): PersistedAppData {
|
||||||
return {
|
return {
|
||||||
emphasizeBytes: this.emphasizeBytes,
|
emphasizeBytes: this.emphasizeBytes,
|
||||||
uiTheme: this.uiTheme,
|
uiTheme: this.uiTheme,
|
||||||
@@ -125,11 +133,12 @@ export default class AppState {
|
|||||||
debugMode: this.debugMode,
|
debugMode: this.debugMode,
|
||||||
pageVisistsCount: this.pageVisitsCount,
|
pageVisistsCount: this.pageVisitsCount,
|
||||||
donationClicked: this.donationClicked,
|
donationClicked: this.donationClicked,
|
||||||
annotateTypes: this.annotateTypes
|
annotateTypes: this.annotateTypes,
|
||||||
|
dimExtrBits: this.dimExtraBits
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function generateKey() : number {
|
function generateKey(): number {
|
||||||
return Math.ceil(Math.random()*10000000) ^ Date.now(); // Because why the hell not...
|
return Math.ceil(Math.random() * 10000000) ^ Date.now(); // Because why the hell not...
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,8 @@ const DEFAULT_DATA : PersistedAppData = {
|
|||||||
debugMode: false,
|
debugMode: false,
|
||||||
pageVisistsCount: 0,
|
pageVisistsCount: 0,
|
||||||
donationClicked: false,
|
donationClicked: false,
|
||||||
annotateTypes: false
|
annotateTypes: false,
|
||||||
|
dimExtrBits: true
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import log from 'loglevel';
|
|||||||
import DebugIndicators from './DebugIndicators';
|
import DebugIndicators from './DebugIndicators';
|
||||||
import hash from '../../core/hash';
|
import hash from '../../core/hash';
|
||||||
import TopLinks from './TopLinks';
|
import TopLinks from './TopLinks';
|
||||||
import Toggle from './Toggle';
|
|
||||||
import SettingsPane from './SettingsPane';
|
import SettingsPane from './SettingsPane';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { faGear } from '@fortawesome/free-solid-svg-icons';
|
import { faGear } from '@fortawesome/free-solid-svg-icons';
|
||||||
@@ -63,7 +62,7 @@ export default class AppRoot extends React.Component<AppRootProps, AppRootState>
|
|||||||
<div className="expressionInput-container">
|
<div className="expressionInput-container">
|
||||||
<InputBox onCommandEntered={(input) => cmd.execute(input)} />
|
<InputBox onCommandEntered={(input) => cmd.execute(input)} />
|
||||||
|
|
||||||
<button className={settingsCss}><FontAwesomeIcon icon={faGear} onClick={() => this.props.appState.toggleShowSettings()} /></button>
|
<button className={settingsCss} title='Toggle Settings'><FontAwesomeIcon icon={faGear} onClick={() => this.props.appState.toggleShowSettings()} /></button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{this.props.appState.showSettings ? <SettingsPane appState={this.props.appState} /> : null}
|
{this.props.appState.showSettings ? <SettingsPane appState={this.props.appState} /> : null}
|
||||||
|
|||||||
@@ -2,4 +2,3 @@
|
|||||||
.donate-result-view .qrcode-container { background: white; display: inline-block; padding: 10px}
|
.donate-result-view .qrcode-container { background: white; display: inline-block; padding: 10px}
|
||||||
.donate-result-view .section { margin-top: 20px; }
|
.donate-result-view .section { margin-top: 20px; }
|
||||||
|
|
||||||
.light .donate-result-view .paypal-button { border: solid 1px gray; }
|
|
||||||
18
src/shell/components/IconWithTooltip.tsx
Normal file
18
src/shell/components/IconWithTooltip.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { IconProp } from "@fortawesome/fontawesome-svg-core";
|
||||||
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
|
export type IconWithToolTipProps = {
|
||||||
|
icon: IconProp
|
||||||
|
}
|
||||||
|
|
||||||
|
function IconWithToolTip (props: React.PropsWithChildren<IconWithToolTipProps>) {
|
||||||
|
return <div className='tooltip-holder'>
|
||||||
|
<button><FontAwesomeIcon icon={props.icon} /></button>
|
||||||
|
|
||||||
|
<div className='tooltip solid-border solid-background'>
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default IconWithToolTip;
|
||||||
@@ -11,11 +11,20 @@
|
|||||||
margin-right: 3px;
|
margin-right: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-container .inner {
|
||||||
|
padding: 15px 20px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-container h3 {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.settings-container .description {
|
.settings-container .description {
|
||||||
font-size: 0.85em;
|
font-size: 0.85em;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding-left: 30px;
|
padding-left: 40px;
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import './SettingsPane.css';
|
import './SettingsPane.css';
|
||||||
import { faToggleOff, faToggleOn } from '@fortawesome/free-solid-svg-icons';
|
import { faGear, faToggleOff, faToggleOn } from '@fortawesome/free-solid-svg-icons';
|
||||||
import AppState from '../AppState';
|
import AppState from '../AppState';
|
||||||
|
|
||||||
type SettingsPaneProps = {
|
type SettingsPaneProps = {
|
||||||
@@ -12,29 +12,38 @@ function SettingsPane(props : SettingsPaneProps) {
|
|||||||
const {appState} = props;
|
const {appState} = props;
|
||||||
|
|
||||||
return <div id="settings" className='settings-container'>
|
return <div id="settings" className='settings-container'>
|
||||||
<div className="inner">
|
<div className="inner solid-border">
|
||||||
<h3>Settings</h3>
|
<h3>Settings</h3>
|
||||||
<div className='setting'>
|
<div className='setting'>
|
||||||
<button onClick={() => appState.toggleEmphasizeBytes()}>
|
<button onClick={() => appState.toggleEmphasizeBytes()}>
|
||||||
<FontAwesomeIcon icon={appState.emphasizeBytes ? faToggleOn : faToggleOff} /> Emphasize Bytes
|
<FontAwesomeIcon size='xl' icon={appState.emphasizeBytes ? faToggleOn : faToggleOff} /> Emphasize Bytes
|
||||||
</button>
|
</button>
|
||||||
<p className='description'>
|
<p className='description'>
|
||||||
{appState.emphasizeBytes
|
{appState.emphasizeBytes
|
||||||
? "Each binary string is extended to contain at least 8 bits. A white space is added between each group of 8 bits so it easy to tell bytes apart."
|
? "Binary strings are padded with extra bits to have a length that is multiple of 8."
|
||||||
: "Binary strings are not modified."}
|
: "Binary strings are not padded with extra bits."}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className='setting'>
|
||||||
|
<button onClick={() => appState.toggleDimExtrBits()}>
|
||||||
|
<FontAwesomeIcon size='xl' icon={appState.dimExtraBits ? faToggleOn : faToggleOff} /> Dim Extra Bits
|
||||||
|
</button>
|
||||||
|
<p className='description'>
|
||||||
|
{appState.dimExtraBits
|
||||||
|
? "Extra bits used for padding are now dimmed."
|
||||||
|
: "No bits are dimmed."}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className='setting'>
|
<div className='setting'>
|
||||||
<button onClick={() => appState.toggleAnnotateTypes()}>
|
<button onClick={() => appState.toggleAnnotateTypes()}>
|
||||||
<FontAwesomeIcon icon={appState.annotateTypes ? faToggleOn : faToggleOff} /> Annotate Data Types
|
<FontAwesomeIcon size='xl' icon={appState.annotateTypes ? faToggleOn : faToggleOff} /> Annotate Data Types
|
||||||
</button>
|
</button>
|
||||||
<p className='description'>
|
<p className='description'>
|
||||||
{appState.annotateTypes
|
{appState.annotateTypes
|
||||||
? "BitwiseCmd shows the integer size and indicates whether the data type is signed or unsigned."
|
? "Integers are displayed as they are stored in memory. Bit size is shown."
|
||||||
: "Information about the size of integers used in the calculation is hidden."}
|
: "Information about the size of integers is hidden."}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ function TopLinks() {
|
|||||||
|
|
||||||
return <ul className="top-links">
|
return <ul className="top-links">
|
||||||
<li>
|
<li>
|
||||||
<button onClick={onDonate}>
|
<button onClick={onDonate} className='link-button'>
|
||||||
<FontAwesomeIcon className='icon' icon={faDonate} size="lg" /><span className='link-text'>donate</span>
|
<FontAwesomeIcon className='icon' icon={faDonate} size="lg" /><span className='link-text'>donate</span>
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ const shellModule = {
|
|||||||
appState.toggleDebugMode();
|
appState.toggleDebugMode();
|
||||||
appState.addCommandResult(c.input, () => <TextResultView text={`Debug Mode: ${appState.debugMode}`}/>);
|
appState.addCommandResult(c.input, () => <TextResultView text={`Debug Mode: ${appState.debugMode}`}/>);
|
||||||
});
|
});
|
||||||
|
cmd.command('-annotate:on', (c:CommandInput) => appState.toggleAnnotateTypes(true))
|
||||||
|
cmd.command('-annotate:off', (c:CommandInput) => appState.toggleAnnotateTypes(false))
|
||||||
|
cmd.command('-em:off', (c:CommandInput) => appState.toggleEmphasizeBytes(false))
|
||||||
|
cmd.command('-em:on', (c:CommandInput) => appState.toggleEmphasizeBytes(true))
|
||||||
cmd.command("-max", (c:CommandInput) => {
|
cmd.command("-max", (c:CommandInput) => {
|
||||||
const text = `Int32 ${INT32_MAX_VALUE}\nInt64 ${INT64_MAX_VALUE}`
|
const text = `Int32 ${INT32_MAX_VALUE}\nInt64 ${INT64_MAX_VALUE}`
|
||||||
appState.addCommandResult(c.input, () => <TextResultView text={text} />)
|
appState.addCommandResult(c.input, () => <TextResultView text={text} />)
|
||||||
|
|||||||
Reference in New Issue
Block a user