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);
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<BinaryStringViewProps> {
|
||||
@@ -23,8 +24,8 @@ export default class BinaryStringView extends React.Component<BinaryStringViewPr
|
||||
return <span className={this.props.className}>{this.getChildren()}</span>
|
||||
}
|
||||
|
||||
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<BinaryStringViewPr
|
||||
arr[index] = arr[index] == '0' ? '1' : '0';
|
||||
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() {
|
||||
var bits = this.createBits(this.props.binaryString.split(''));
|
||||
|
||||
if(this.props.emphasizeBytes) {
|
||||
|
||||
if (this.props.emphasizeBytes) {
|
||||
return this.splitIntoBytes(bits);
|
||||
}
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
createBits(bitChars:string[]) : JSX.Element[] {
|
||||
createBits(bitChars: string[]): JSX.Element[] {
|
||||
const allowFlipBits = this.props.allowFlipBits || false;
|
||||
const css = allowFlipBits ? ' flipable' : ''
|
||||
|
||||
@@ -56,18 +63,19 @@ export default class BinaryStringView extends React.Component<BinaryStringViewPr
|
||||
var className = c == '1' ? `one${css}` : `zero${css}`;
|
||||
var tooltip = '';
|
||||
|
||||
if(i < (this.props.signBitIndex || 0))
|
||||
const isExtra = i < (this.props.signBitIndex || 0);
|
||||
if (isExtra)
|
||||
className += ' extra-bit';
|
||||
|
||||
if(i === this.props.signBitIndex) {
|
||||
if (i === this.props.signBitIndex) {
|
||||
className += ' accent1';
|
||||
tooltip = 'Signature bit. 0 means a positive number and 1 means a negative.'
|
||||
}
|
||||
|
||||
if(disableHighlight)
|
||||
|
||||
if (disableHighlight)
|
||||
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>
|
||||
});
|
||||
}
|
||||
|
||||
@@ -75,10 +83,10 @@ export default class BinaryStringView extends React.Component<BinaryStringViewPr
|
||||
const bytes = [];
|
||||
|
||||
var key = 0;
|
||||
while(bits.length > 0) {
|
||||
while (bits.length > 0) {
|
||||
bytes.push(<span key={key++} className="byte">{bits.splice(0, 8)}</span>);
|
||||
}
|
||||
|
||||
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
@@ -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<BitwiseResultView
|
||||
emphasizeBytes={this.props.emphasizeBytes}
|
||||
maxNumberOfBits={this.maxSeenLengthNumberOfBits}
|
||||
showInfoColumn={showInfoColumn}
|
||||
onBitFlipped={() => 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<ExpressionElementRowProps> {
|
||||
|
||||
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 <tr className={"row-with-bits " + css}>
|
||||
<td className="sign">{sign}</td>
|
||||
<td className="label">{this.getLabel()}</td>
|
||||
<td className="label">
|
||||
<span>{this.getLabel()}</span>
|
||||
</td>
|
||||
<td className="bin">
|
||||
<BinaryStringView
|
||||
emphasizeBytes={emphasizeBytes}
|
||||
binaryString={bin}
|
||||
allowFlipBits={allowFlipBits}
|
||||
signBitIndex={signBitIndex}
|
||||
onFlipBit={args => this.flipBit(args)} />
|
||||
onBitClicked={args => this.onBitClicked(args)} />
|
||||
</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'>
|
||||
{this.originalValue != null ? <button title='Undo all changes' onClick={() => this.undo()}><FontAwesomeIcon icon={faUndo}/></button> : null}
|
||||
{this.getUndoButton()}
|
||||
</td>
|
||||
</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 {
|
||||
|
||||
// For expressions like |~2
|
||||
@@ -159,46 +174,43 @@ class ExpressionElementTableRow extends React.Component<ExpressionElementRowProp
|
||||
}
|
||||
|
||||
undo() {
|
||||
if(this.originalValue == null)
|
||||
return;
|
||||
|
||||
this.props.expressionItem.getUnderlyingOperand().setValue(this.originalValue);
|
||||
this.originalValue = null;
|
||||
this.forceUpdate();
|
||||
this.changeValue(this.originalValue);
|
||||
this.props.onValueChanged();
|
||||
}
|
||||
|
||||
flipBit(args: FlipBitEventArg) {
|
||||
onBitClicked(args: BitClickedEventArg) {
|
||||
|
||||
const op = this.props.expressionItem.getUnderlyingOperand();
|
||||
const { bitIndex: index, binaryStringLength: totalLength } = args;
|
||||
|
||||
const maxBitSize = op.value.maxBitSize;
|
||||
const space = (totalLength - index - maxBitSize);
|
||||
|
||||
if(this.originalValue == null)
|
||||
this.originalValue = op.value;
|
||||
const maxBitSize = this.scalar.value.maxBitSize;
|
||||
|
||||
if(totalLength > 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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ export class IpAddressView extends React.Component<IpAddressViewProps>
|
||||
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) {
|
||||
|
||||
Reference in New Issue
Block a user