mirror of
https://github.com/BorysLevytskyi/BitwiseCmd.git
synced 2025-12-10 06:52:05 +01:00
Minor improvements and bug fixes (#48)
This commit is contained in:
@@ -47,6 +47,10 @@ export class Integer {
|
|||||||
return new Integer(value, 8);
|
return new Integer(value, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isTheSame (other : Integer) : boolean {
|
||||||
|
return this.value == other.value && this.signed == other.signed && this.maxBitSize == other.maxBitSize;
|
||||||
|
}
|
||||||
|
|
||||||
toUnsigned() {
|
toUnsigned() {
|
||||||
return this.signed
|
return this.signed
|
||||||
? new Integer(BigInt("0b" + this.toString(2)), this.maxBitSize, false)
|
? new Integer(BigInt("0b" + this.toString(2)), this.maxBitSize, false)
|
||||||
|
|||||||
@@ -62,6 +62,13 @@ describe('calc.addSpace', () => {
|
|||||||
expect(calc.addSpace(n16, 1).maxBitSize).toBe(32);
|
expect(calc.addSpace(n16, 1).maxBitSize).toBe(32);
|
||||||
expect(calc.addSpace(n16, 32).maxBitSize).toBe(64);
|
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', () => {
|
describe('calc.numberOfBitsDisplayed', () => {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Integer, JsNumber, asInteger } from "./Integer";
|
import { Integer, JsNumber, asInteger } from "./Integer";
|
||||||
import { asIntN } from "./utils";
|
import { asIntN, logLines } from "./utils";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
abs (num : Integer) : Integer {
|
abs (num : Integer) : Integer {
|
||||||
@@ -20,9 +20,8 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
addSpace(number: Integer, requiredSpace: number) : Integer {
|
addSpace(number: Integer, requiredSpace: number) : Integer {
|
||||||
const bin = this.toBinaryString(number);
|
|
||||||
const totalSpaceRequired = number.maxBitSize + requiredSpace;
|
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 {
|
operation (op1: Integer, operator: string, op2 : Integer) : Integer {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import './BinaryString.css';
|
|||||||
export type BinaryStringViewProps = {
|
export type BinaryStringViewProps = {
|
||||||
allowFlipBits?: boolean;
|
allowFlipBits?: boolean;
|
||||||
binaryString: string;
|
binaryString: string;
|
||||||
onFlipBit?: (input: FlipBitEventArg) => void;
|
onBitClicked?: (input: FlipBitEventArg) => void;
|
||||||
emphasizeBytes?: boolean;
|
emphasizeBytes?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
disableHighlight?: boolean,
|
disableHighlight?: boolean,
|
||||||
@@ -15,7 +15,8 @@ export type FlipBitEventArg = {
|
|||||||
bitIndex: number;
|
bitIndex: number;
|
||||||
binaryStringLength: number;
|
binaryStringLength: number;
|
||||||
$event: any;
|
$event: any;
|
||||||
newBinaryString: string
|
newBinaryString: any;
|
||||||
|
isTypeExtend: boolean
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class BinaryStringView extends React.Component<BinaryStringViewProps> {
|
export default class BinaryStringView extends React.Component<BinaryStringViewProps> {
|
||||||
@@ -23,8 +24,8 @@ export default class BinaryStringView extends React.Component<BinaryStringViewPr
|
|||||||
return <span className={this.props.className}>{this.getChildren()}</span>
|
return <span className={this.props.className}>{this.getChildren()}</span>
|
||||||
}
|
}
|
||||||
|
|
||||||
onBitClick(index: number, e : any) {
|
onBitClick(index: number, isExtra: boolean, e: any) {
|
||||||
if(!this.props.allowFlipBits || !this.props.onFlipBit) {
|
if (!this.props.allowFlipBits || !this.props.onBitClicked) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +33,13 @@ export default class BinaryStringView extends React.Component<BinaryStringViewPr
|
|||||||
arr[index] = arr[index] == '0' ? '1' : '0';
|
arr[index] = arr[index] == '0' ? '1' : '0';
|
||||||
const newBinaryString = arr.join('');
|
const newBinaryString = arr.join('');
|
||||||
|
|
||||||
this.props.onFlipBit({ bitIndex: index, binaryStringLength: this.props.binaryString.length, $event: e, newBinaryString });
|
this.props.onBitClicked({
|
||||||
|
bitIndex: index,
|
||||||
|
binaryStringLength: this.props.binaryString.length,
|
||||||
|
newBinaryString: newBinaryString,
|
||||||
|
$event: e,
|
||||||
|
isTypeExtend: isExtra
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getChildren() {
|
getChildren() {
|
||||||
@@ -56,7 +63,8 @@ export default class BinaryStringView extends React.Component<BinaryStringViewPr
|
|||||||
var className = c == '1' ? `one${css}` : `zero${css}`;
|
var className = c == '1' ? `one${css}` : `zero${css}`;
|
||||||
var tooltip = '';
|
var tooltip = '';
|
||||||
|
|
||||||
if(i < (this.props.signBitIndex || 0))
|
const isExtra = i < (this.props.signBitIndex || 0);
|
||||||
|
if (isExtra)
|
||||||
className += ' extra-bit';
|
className += ' extra-bit';
|
||||||
|
|
||||||
if (i === this.props.signBitIndex) {
|
if (i === this.props.signBitIndex) {
|
||||||
@@ -67,7 +75,7 @@ export default class BinaryStringView extends React.Component<BinaryStringViewPr
|
|||||||
if (disableHighlight)
|
if (disableHighlight)
|
||||||
className = css;
|
className = css;
|
||||||
|
|
||||||
return <span className={className} title={tooltip} key={i} onClick={e => this.onBitClick(i, e)}>{c}</span>
|
return <span className={className} title={tooltip} key={i} onClick={e => this.onBitClick(i, isExtra, e)}>{c}</span>
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import formatter from '../../core/formatter';
|
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 BitwiseResultViewModel from './BitwiseResultViewModel';
|
||||||
import { Expression, ExpressionElement } from '../expression-interfaces';
|
import { Expression, ExpressionElement } from '../expression-interfaces';
|
||||||
import { Operator, Operand, ListOfNumbers } from '../expression';
|
import { Operator, Operand, ListOfNumbers } from '../expression';
|
||||||
@@ -8,6 +8,7 @@ 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 { faUndo } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import loglevel from 'loglevel';
|
||||||
|
|
||||||
type BitwiseResultViewProps = {
|
type BitwiseResultViewProps = {
|
||||||
expression: Expression;
|
expression: Expression;
|
||||||
@@ -72,10 +73,11 @@ export default class BitwiseResultView extends React.Component<BitwiseResultView
|
|||||||
emphasizeBytes={this.props.emphasizeBytes}
|
emphasizeBytes={this.props.emphasizeBytes}
|
||||||
maxNumberOfBits={this.maxSeenLengthNumberOfBits}
|
maxNumberOfBits={this.maxSeenLengthNumberOfBits}
|
||||||
showInfoColumn={showInfoColumn}
|
showInfoColumn={showInfoColumn}
|
||||||
onBitFlipped={() => this.onBitFlipped()} />);
|
onValueChanged={() => this.onValueChanged()} />);
|
||||||
}
|
}
|
||||||
|
|
||||||
onBitFlipped() {
|
onValueChanged() {
|
||||||
|
loglevel.debug("onValueChanged()");
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,45 +91,58 @@ type ExpressionElementRowProps = {
|
|||||||
allowFlipBits: boolean,
|
allowFlipBits: boolean,
|
||||||
allowSignChange: boolean,
|
allowSignChange: boolean,
|
||||||
expressionItem: ExpressionElement,
|
expressionItem: ExpressionElement,
|
||||||
onBitFlipped: any,
|
onValueChanged: any,
|
||||||
showInfoColumn: boolean
|
showInfoColumn: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExpressionElementTableRow extends React.Component<ExpressionElementRowProps> {
|
class ExpressionElementTableRow extends React.Component<ExpressionElementRowProps> {
|
||||||
|
|
||||||
infoWasShown: boolean = false;
|
infoWasShown: boolean = false;
|
||||||
originalValue: Integer | null = null;
|
originalValue: Integer;
|
||||||
|
scalar: Operand;
|
||||||
|
|
||||||
constructor(props: ExpressionElementRowProps) {
|
constructor(props: ExpressionElementRowProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = { operand: null };
|
this.state = { operand: null };
|
||||||
|
this.scalar = this.props.expressionItem.getUnderlyingOperand();
|
||||||
|
this.originalValue = this.scalar.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { sign, css, maxNumberOfBits, emphasizeBytes, allowFlipBits } = this.props;
|
const { sign, css, maxNumberOfBits, emphasizeBytes, allowFlipBits } = this.props;
|
||||||
const scalar = this.props.expressionItem.evaluate();
|
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;
|
const signBitIndex = scalar.value.signed && bin.length >= scalar.value.maxBitSize ? bin.length - scalar.value.maxBitSize : -1;
|
||||||
|
|
||||||
return <tr className={"row-with-bits " + css}>
|
return <tr className={"row-with-bits " + css}>
|
||||||
<td className="sign">{sign}</td>
|
<td className="sign">{sign}</td>
|
||||||
<td className="label">{this.getLabel()}</td>
|
<td className="label">
|
||||||
|
<span>{this.getLabel()}</span>
|
||||||
|
</td>
|
||||||
<td className="bin">
|
<td className="bin">
|
||||||
<BinaryStringView
|
<BinaryStringView
|
||||||
emphasizeBytes={emphasizeBytes}
|
emphasizeBytes={emphasizeBytes}
|
||||||
binaryString={bin}
|
binaryString={bin}
|
||||||
allowFlipBits={allowFlipBits}
|
allowFlipBits={allowFlipBits}
|
||||||
signBitIndex={signBitIndex}
|
signBitIndex={signBitIndex}
|
||||||
onFlipBit={args => this.flipBit(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(maxNumberOfBits) : null}</td>
|
<td className="info accent1" data-test-name='ignore'>{this.props.showInfoColumn ? this.getInfo() : null}</td>
|
||||||
<td className='undo' data-test-name='ignore'>
|
<td className='undo' data-test-name='ignore'>
|
||||||
{this.originalValue != null ? <button title='Undo all changes' onClick={() => this.undo()}><FontAwesomeIcon icon={faUndo}/></button> : null}
|
{this.getUndoButton()}
|
||||||
</td>
|
</td>
|
||||||
</tr>;
|
</tr>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getUndoButton(): React.ReactNode {
|
||||||
|
|
||||||
|
return !this.originalValue.isTheSame(this.scalar.value)
|
||||||
|
? <button title='Undo all changes' className='undo' data-control="undo" onClick={() => this.undo()}><FontAwesomeIcon icon={faUndo}/></button>
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
getLabel(): string {
|
getLabel(): string {
|
||||||
|
|
||||||
// For expressions like |~2
|
// For expressions like |~2
|
||||||
@@ -159,46 +174,43 @@ class ExpressionElementTableRow extends React.Component<ExpressionElementRowProp
|
|||||||
}
|
}
|
||||||
|
|
||||||
undo() {
|
undo() {
|
||||||
if(this.originalValue == null)
|
this.changeValue(this.originalValue);
|
||||||
return;
|
this.props.onValueChanged();
|
||||||
|
|
||||||
this.props.expressionItem.getUnderlyingOperand().setValue(this.originalValue);
|
|
||||||
this.originalValue = null;
|
|
||||||
this.forceUpdate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
flipBit(args: FlipBitEventArg) {
|
onBitClicked(args: BitClickedEventArg) {
|
||||||
|
|
||||||
const op = this.props.expressionItem.getUnderlyingOperand();
|
|
||||||
const { bitIndex: index, binaryStringLength: totalLength } = args;
|
const { bitIndex: index, binaryStringLength: totalLength } = args;
|
||||||
|
|
||||||
const maxBitSize = op.value.maxBitSize;
|
const maxBitSize = this.scalar.value.maxBitSize;
|
||||||
const space = (totalLength - index - maxBitSize);
|
|
||||||
|
|
||||||
if(this.originalValue == null)
|
if(!args.isTypeExtend)
|
||||||
this.originalValue = op.value;
|
{
|
||||||
|
const pad = this.scalar.value.maxBitSize - totalLength;
|
||||||
if(totalLength > op.value.maxBitSize && space > 0) {
|
const newValue = calc.flipBit(this.scalar.value, pad + index);
|
||||||
op.setValue(calc.addSpace(op.value, space));
|
this.changeValue(newValue);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pad = op.value.maxBitSize - totalLength;
|
const space = (totalLength - index - maxBitSize);
|
||||||
const newValue = calc.flipBit(op.value, pad + index);
|
this.changeValue(calc.addSpace(this.scalar.value, space));
|
||||||
op.setValue(newValue);
|
|
||||||
this.props.onBitFlipped();
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onChangeSign () {
|
onChangeSign () {
|
||||||
|
|
||||||
var op = this.props.expressionItem.getUnderlyingOperand();
|
var op = this.props.expressionItem.getUnderlyingOperand();
|
||||||
if(this.originalValue == null)
|
|
||||||
this.originalValue = op.value;
|
this.changeValue(op.value.signed ? op.value.toUnsigned() : op.value.toSigned());
|
||||||
op.setValue(op.value.signed ? op.value.toUnsigned() : op.value.toSigned());
|
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
getInfo(maxNumberOfBits:number) {
|
changeValue(newValue: Integer) {
|
||||||
|
this.scalar.setValue(newValue);
|
||||||
|
this.props.onValueChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
getInfo() {
|
||||||
|
|
||||||
const op = this.props.expressionItem.getUnderlyingOperand();
|
const op = this.props.expressionItem.getUnderlyingOperand();
|
||||||
const { allowSignChange } = this.props;
|
const { allowSignChange } = this.props;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Operand, ListOfNumbers, BitwiseOperation, Operator } from '../expressio
|
|||||||
import { ExpressionElement, Expression } from '../expression-interfaces';
|
import { ExpressionElement, Expression } from '../expression-interfaces';
|
||||||
import calc from '../../core/calc';
|
import calc from '../../core/calc';
|
||||||
import formatter from '../../core/formatter';
|
import formatter from '../../core/formatter';
|
||||||
|
import exp from 'constants';
|
||||||
|
|
||||||
type Config = {
|
type Config = {
|
||||||
emphasizeBytes: boolean;
|
emphasizeBytes: boolean;
|
||||||
@@ -156,6 +157,8 @@ export default class BitwiseResultViewModel {
|
|||||||
|
|
||||||
static createModel(expr : Expression, emphasizeBytes: boolean) : BitwiseResultViewModel {
|
static createModel(expr : Expression, emphasizeBytes: boolean) : BitwiseResultViewModel {
|
||||||
|
|
||||||
|
console.log(expr);
|
||||||
|
|
||||||
if(expr instanceof ListOfNumbers) {
|
if(expr instanceof ListOfNumbers) {
|
||||||
return BitwiseResultViewModel.buildListOfNumbers(expr, {
|
return BitwiseResultViewModel.buildListOfNumbers(expr, {
|
||||||
emphasizeBytes: emphasizeBytes,
|
emphasizeBytes: emphasizeBytes,
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ code { font-size: 1.2em; font-weight: bold; }
|
|||||||
background: rgba(0, 0, 0, 0);
|
background: rgba(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.label { width: 5em; overflow: visible;}
|
||||||
|
.label span {position: global;}
|
||||||
.hidden { display: none;}
|
.hidden { display: none;}
|
||||||
|
|
||||||
.result { margin: 10px 10px 30px; }
|
.result { margin: 10px 10px 30px; }
|
||||||
@@ -71,7 +73,7 @@ a.hashLink { font-size: 1.1em;}
|
|||||||
|
|
||||||
.cur { color: lightgray; }
|
.cur { color: lightgray; }
|
||||||
|
|
||||||
.extra-bit { opacity: 0.4;}
|
.extra-bit { opacity: 0.2;}
|
||||||
|
|
||||||
button { border: none; text-decoration: underline;}
|
button { border: none; text-decoration: underline;}
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export class IpAddressView extends React.Component<IpAddressViewProps>
|
|||||||
emphasizeBytes={false}
|
emphasizeBytes={false}
|
||||||
allowFlipBits={true}
|
allowFlipBits={true}
|
||||||
className={`octet-${octetNumber}`}
|
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) {
|
onFlippedBit(binaryString: string, number: OctetNumber, ip : IpAddress) {
|
||||||
|
|||||||
Reference in New Issue
Block a user