diff --git a/src/core/Integer.ts b/src/core/Integer.ts index 7d78d79..b9dfe62 100644 --- a/src/core/Integer.ts +++ b/src/core/Integer.ts @@ -47,6 +47,10 @@ export class Integer { return new Integer(value, 8); } + isTheSame (other : Integer) : boolean { + return this.value == other.value && this.signed == other.signed && this.maxBitSize == other.maxBitSize; + } + toUnsigned() { return this.signed ? new Integer(BigInt("0b" + this.toString(2)), this.maxBitSize, false) diff --git a/src/core/calc.test.ts b/src/core/calc.test.ts index 9cc4692..32284fa 100644 --- a/src/core/calc.test.ts +++ b/src/core/calc.test.ts @@ -62,6 +62,13 @@ describe('calc.addSpace', () => { expect(calc.addSpace(n16, 1).maxBitSize).toBe(32); expect(calc.addSpace(n16, 32).maxBitSize).toBe(64); }); + + it('preserves the sign when extending number', () => { + const byte = Integer.byte(-1); + const actual = calc.addSpace(byte, 1); + expect(actual.maxBitSize).toBe(16); + expect(actual.num()).toBe(-1); + }) }); describe('calc.numberOfBitsDisplayed', () => { diff --git a/src/core/calc.ts b/src/core/calc.ts index 9467d77..7a11ad0 100644 --- a/src/core/calc.ts +++ b/src/core/calc.ts @@ -1,5 +1,5 @@ import { Integer, JsNumber, asInteger } from "./Integer"; -import { asIntN } from "./utils"; +import { asIntN, logLines } from "./utils"; export default { abs (num : Integer) : Integer { @@ -20,9 +20,8 @@ export default { }, addSpace(number: Integer, requiredSpace: number) : Integer { - const bin = this.toBinaryString(number); const totalSpaceRequired = number.maxBitSize + requiredSpace; - return new Integer(BigInt("0b" + bin), nextPowOfTwo(totalSpaceRequired)); + return new Integer(number.value, nextPowOfTwo(totalSpaceRequired)); }, operation (op1: Integer, operator: string, op2 : Integer) : Integer { diff --git a/src/core/components/BinaryString.tsx b/src/core/components/BinaryString.tsx index e477438..6882795 100644 --- a/src/core/components/BinaryString.tsx +++ b/src/core/components/BinaryString.tsx @@ -4,10 +4,10 @@ import './BinaryString.css'; export type BinaryStringViewProps = { allowFlipBits?: boolean; binaryString: string; - onFlipBit?: (input: FlipBitEventArg) => void; + onBitClicked?: (input: FlipBitEventArg) => void; emphasizeBytes?: boolean; - className?:string; - disableHighlight?:boolean, + className?: string; + disableHighlight?: boolean, signBitIndex?: number, }; @@ -15,7 +15,8 @@ export type FlipBitEventArg = { bitIndex: number; binaryStringLength: number; $event: any; - newBinaryString: string + newBinaryString: any; + isTypeExtend: boolean }; export default class BinaryStringView extends React.Component { @@ -23,8 +24,8 @@ export default class BinaryStringView extends React.Component{this.getChildren()} } - onBitClick(index: number, e : any) { - if(!this.props.allowFlipBits || !this.props.onFlipBit) { + onBitClick(index: number, isExtra: boolean, e: any) { + if (!this.props.allowFlipBits || !this.props.onBitClicked) { return; } @@ -32,20 +33,26 @@ export default class BinaryStringView extends React.Component this.onBitClick(i, e)}>{c} + return this.onBitClick(i, isExtra, e)}>{c} }); } @@ -75,10 +83,10 @@ export default class BinaryStringView extends React.Component 0) { + while (bits.length > 0) { bytes.push({bits.splice(0, 8)}); } - + return bytes; } } \ No newline at end of file diff --git a/src/expression/components/BitwiseResultView.tsx b/src/expression/components/BitwiseResultView.tsx index 0cc88cd..63a720e 100644 --- a/src/expression/components/BitwiseResultView.tsx +++ b/src/expression/components/BitwiseResultView.tsx @@ -1,6 +1,6 @@ import React from 'react'; import formatter from '../../core/formatter'; -import BinaryStringView, { FlipBitEventArg } from '../../core/components/BinaryString'; +import BinaryStringView, { FlipBitEventArg as BitClickedEventArg } from '../../core/components/BinaryString'; import BitwiseResultViewModel from './BitwiseResultViewModel'; import { Expression, ExpressionElement } from '../expression-interfaces'; import { Operator, Operand, ListOfNumbers } from '../expression'; @@ -8,6 +8,7 @@ import calc from '../../core/calc'; import { Integer } from '../../core/Integer'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faUndo } from '@fortawesome/free-solid-svg-icons'; +import loglevel from 'loglevel'; type BitwiseResultViewProps = { expression: Expression; @@ -72,10 +73,11 @@ export default class BitwiseResultView extends React.Component this.onBitFlipped()} />); + onValueChanged={() => this.onValueChanged()} />); } - onBitFlipped() { + onValueChanged() { + loglevel.debug("onValueChanged()"); this.forceUpdate(); } } @@ -89,45 +91,58 @@ type ExpressionElementRowProps = { allowFlipBits: boolean, allowSignChange: boolean, expressionItem: ExpressionElement, - onBitFlipped: any, + onValueChanged: any, showInfoColumn: boolean } class ExpressionElementTableRow extends React.Component { infoWasShown: boolean = false; - originalValue: Integer | null = null; + originalValue: Integer; + scalar: Operand; constructor(props: ExpressionElementRowProps) { super(props); this.state = { operand: null }; + this.scalar = this.props.expressionItem.getUnderlyingOperand(); + this.originalValue = this.scalar.value; } render() { const { sign, css, maxNumberOfBits, emphasizeBytes, allowFlipBits } = this.props; const scalar = this.props.expressionItem.evaluate(); - const bin = formatter.numberToString(scalar.value, 'bin').padStart(maxNumberOfBits, '0'); + const padChar = scalar.value.value >= 0 ? '0' : '1'; + 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; return {sign} - {this.getLabel()} + + {this.getLabel()} + this.flipBit(args)} /> + onBitClicked={args => this.onBitClicked(args)} /> {this.getAlternative()} - {this.props.showInfoColumn ? this.getInfo(maxNumberOfBits) : null} + {this.props.showInfoColumn ? this.getInfo() : null} - {this.originalValue != null ? : null} + {this.getUndoButton()} ; } + getUndoButton(): React.ReactNode { + + return !this.originalValue.isTheSame(this.scalar.value) + ? + : null; + } + getLabel(): string { // For expressions like |~2 @@ -159,46 +174,43 @@ class ExpressionElementTableRow extends React.Component op.value.maxBitSize && space > 0) { - op.setValue(calc.addSpace(op.value, space)); + if(!args.isTypeExtend) + { + const pad = this.scalar.value.maxBitSize - totalLength; + const newValue = calc.flipBit(this.scalar.value, pad + index); + this.changeValue(newValue); + return; } - - const pad = op.value.maxBitSize - totalLength; - const newValue = calc.flipBit(op.value, pad + index); - op.setValue(newValue); - this.props.onBitFlipped(); - - + + const space = (totalLength - index - maxBitSize); + this.changeValue(calc.addSpace(this.scalar.value, space)); } onChangeSign () { + var op = this.props.expressionItem.getUnderlyingOperand(); - if(this.originalValue == null) - this.originalValue = op.value; - op.setValue(op.value.signed ? op.value.toUnsigned() : op.value.toSigned()); + + this.changeValue(op.value.signed ? op.value.toUnsigned() : op.value.toSigned()); + this.forceUpdate(); } - getInfo(maxNumberOfBits:number) { + changeValue(newValue: Integer) { + this.scalar.setValue(newValue); + this.props.onValueChanged(); + } + + getInfo() { const op = this.props.expressionItem.getUnderlyingOperand(); const { allowSignChange } = this.props; diff --git a/src/expression/components/BitwiseResultViewModel.ts b/src/expression/components/BitwiseResultViewModel.ts index 5569e26..76e55ff 100644 --- a/src/expression/components/BitwiseResultViewModel.ts +++ b/src/expression/components/BitwiseResultViewModel.ts @@ -2,6 +2,7 @@ import { Operand, ListOfNumbers, BitwiseOperation, Operator } from '../expressio import { ExpressionElement, Expression } from '../expression-interfaces'; import calc from '../../core/calc'; import formatter from '../../core/formatter'; +import exp from 'constants'; type Config = { emphasizeBytes: boolean; @@ -156,6 +157,8 @@ export default class BitwiseResultViewModel { static createModel(expr : Expression, emphasizeBytes: boolean) : BitwiseResultViewModel { + console.log(expr); + if(expr instanceof ListOfNumbers) { return BitwiseResultViewModel.buildListOfNumbers(expr, { emphasizeBytes: emphasizeBytes, diff --git a/src/index.css b/src/index.css index 0130e3e..46ddf1d 100644 --- a/src/index.css +++ b/src/index.css @@ -30,6 +30,8 @@ code { font-size: 1.2em; font-weight: bold; } background: rgba(0, 0, 0, 0); } +.label { width: 5em; overflow: visible;} +.label span {position: global;} .hidden { display: none;} .result { margin: 10px 10px 30px; } @@ -71,7 +73,7 @@ a.hashLink { font-size: 1.1em;} .cur { color: lightgray; } -.extra-bit { opacity: 0.4;} +.extra-bit { opacity: 0.2;} button { border: none; text-decoration: underline;} diff --git a/src/networking/components/IpAddressView.tsx b/src/networking/components/IpAddressView.tsx index 71cd176..62c856f 100644 --- a/src/networking/components/IpAddressView.tsx +++ b/src/networking/components/IpAddressView.tsx @@ -34,7 +34,7 @@ export class IpAddressView extends React.Component emphasizeBytes={false} allowFlipBits={true} className={`octet-${octetNumber}`} - onFlipBit={e => this.onFlippedBit(e.newBinaryString, octetNumber, ip)} />; + onBitClicked={e => this.onFlippedBit(e.newBinaryString, octetNumber, ip)} />; } onFlippedBit(binaryString: string, number: OctetNumber, ip : IpAddress) {