Skip to content

Commit

Permalink
Introduce 'Endianness' in UI and honor it during group rendering
Browse files Browse the repository at this point in the history
- Move Endianness interface into common code
- Add setting for 'Endianness' in package.json
- Add option for 'Endianness' in options panel
- Assume DAP as big endian and revert byte-order for little endian
- Set 'Little Endian' as default as it may be more common

Closes #88
  • Loading branch information
martin-fleck-at committed Mar 5, 2024
1 parent d1400a1 commit 41ecf3a
Show file tree
Hide file tree
Showing 9 changed files with 65 additions and 19 deletions.
13 changes: 13 additions & 0 deletions package.json
Expand Up @@ -196,6 +196,19 @@
"default": 4,
"description": "Default groups per row"
},
"memory-inspector.endianness": {
"type": "string",
"enum": [
"Big Endian",
"Little Endian"
],
"enumDescriptions": [
"Most significant byte stored at smallest memory address",
"Least significant byte stored at smallest memory address"
],
"default": "Little Endian",
"description": "Order of bytes within a group"
},
"memory-inspector.columns.variables": {
"type": "boolean",
"default": false,
Expand Down
5 changes: 5 additions & 0 deletions src/common/memory-range.ts
Expand Up @@ -116,3 +116,8 @@ export function areVariablesEqual(one: BigIntVariableRange, other: BigIntVariabl
export function toOffset(startAddress: bigint, targetAddress: bigint, wordSize: number): number {
return Number(targetAddress - startAddress) * (wordSize / 8);
}

export enum Endianness {
Little = 'Little Endian',
Big = 'Big Endian'
}
4 changes: 4 additions & 0 deletions src/plugin/manifest.ts
Expand Up @@ -14,6 +14,8 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { Endianness } from '../common/memory-range';

export const PACKAGE_NAME = 'memory-inspector';
export const DISPLAY_NAME = 'Memory Inspector';
export const EDITOR_NAME = `${PACKAGE_NAME}.inspect`;
Expand All @@ -31,6 +33,8 @@ export const CONFIG_WORDS_PER_GROUP = 'groupings.wordsPerGroup';
export const DEFAULT_WORDS_PER_GROUP = 1;
export const CONFIG_GROUPS_PER_ROW = 'groupings.groupsPerRow';
export const DEFAULT_GROUPS_PER_ROW = 4;
export const CONFIG_ENDIANNESS = 'endianness';
export const DEFAULT_ENDIANNESS = Endianness.Little;
export const CONFIG_SCROLLING_BEHAVIOR = 'scrollingBehavior';
export const DEFAULT_SCROLLING_BEHAVIOR = 'Paginate';
export const CONFIG_ADDRESS_RADIX = 'addressRadix';
Expand Down
5 changes: 3 additions & 2 deletions src/plugin/memory-webview-main.ts
Expand Up @@ -34,7 +34,7 @@ import {
} from '../common/messaging';
import { MemoryProvider } from './memory-provider';
import { outputChannelLogger } from './logger';
import { VariableRange } from '../common/memory-range';
import { Endianness, VariableRange } from '../common/memory-range';
import { MemoryViewSettings, ScrollingBehavior } from '../webview/utils/view-types';

interface Variable {
Expand Down Expand Up @@ -221,13 +221,14 @@ export class MemoryWebview implements vscode.CustomReadonlyEditorProvider {
const bytesPerWord = memoryInspectorConfiguration.get<number>(manifest.CONFIG_BYTES_PER_WORD, manifest.DEFAULT_BYTES_PER_WORD);
const wordsPerGroup = memoryInspectorConfiguration.get<number>(manifest.CONFIG_WORDS_PER_GROUP, manifest.DEFAULT_WORDS_PER_GROUP);
const groupsPerRow = memoryInspectorConfiguration.get<number>(manifest.CONFIG_GROUPS_PER_ROW, manifest.DEFAULT_GROUPS_PER_ROW);
const endianness = memoryInspectorConfiguration.get<Endianness>(manifest.CONFIG_ENDIANNESS, manifest.DEFAULT_ENDIANNESS);
const scrollingBehavior = memoryInspectorConfiguration.get<ScrollingBehavior>(manifest.CONFIG_SCROLLING_BEHAVIOR, manifest.DEFAULT_SCROLLING_BEHAVIOR);
const visibleColumns = CONFIGURABLE_COLUMNS
.filter(column => vscode.workspace.getConfiguration(manifest.PACKAGE_NAME).get<boolean>(column, false))
.map(columnId => columnId.replace('columns.', ''));
const addressRadix = memoryInspectorConfiguration.get<number>(manifest.CONFIG_ADDRESS_RADIX, manifest.DEFAULT_ADDRESS_RADIX);
const showRadixPrefix = memoryInspectorConfiguration.get<boolean>(manifest.CONFIG_SHOW_RADIX_PREFIX, manifest.DEFAULT_SHOW_RADIX_PREFIX);
return { title, bytesPerWord, wordsPerGroup, groupsPerRow, scrollingBehavior, visibleColumns, addressRadix, showRadixPrefix };
return { title, bytesPerWord, wordsPerGroup, groupsPerRow, scrollingBehavior, visibleColumns, addressRadix, showRadixPrefix, endianness };
}

protected async readMemory(request: DebugProtocol.ReadMemoryArguments): Promise<MemoryReadResult> {
Expand Down
20 changes: 14 additions & 6 deletions src/webview/columns/data-column.tsx
Expand Up @@ -15,7 +15,7 @@
********************************************************************************/

import * as React from 'react';
import { BigIntMemoryRange, toOffset } from '../../common/memory-range';
import { BigIntMemoryRange, Endianness, toOffset } from '../../common/memory-range';
import { FullNodeAttributes, Memory } from '../utils/view-types';
import { ColumnContribution, TableRenderOptions } from './column-contribution-service';
import { decorationService } from '../decorations/decoration-service';
Expand All @@ -31,28 +31,36 @@ export class DataColumn implements ColumnContribution {

protected renderGroups(range: BigIntMemoryRange, memory: Memory, options: TableRenderOptions): React.ReactNode {
const groups = [];
let words = [];
for (let i = range.startAddress; i < range.endAddress; i++) {
words.push(this.renderWord(memory, options, i));
let words: React.ReactNode[] = [];
for (let address = range.startAddress; address < range.endAddress; address++) {
words.push(this.renderWord(memory, options, address));
if (words.length % options.wordsPerGroup === 0) {
groups.push(<span className='byte-group' key={i.toString(16)}>{words}</span>);
this.applyEndianness(words, options);
groups.push(<span className='byte-group' key={address.toString(16)}>{words}</span>);
words = [];
}
}
this.applyEndianness(words, options);
if (words.length) { groups.push(<span className='byte-group' key={(range.endAddress - BigInt(words.length)).toString(16)}>{words}</span>); }
return groups;
}

protected renderWord(memory: Memory, options: TableRenderOptions, currentAddress: bigint): React.ReactNode {
const initialOffset = toOffset(memory.address, currentAddress, options.bytesPerWord * 8);
const finalOffset = initialOffset + options.bytesPerWord;
const bytes = [];
const bytes: React.ReactNode[] = [];
for (let i = initialOffset; i < finalOffset; i++) {
bytes.push(this.renderEightBits(memory, currentAddress, i));
}
this.applyEndianness(bytes, options);
return <span className='single-word' key={currentAddress.toString(16)}>{bytes}</span>;
}

protected applyEndianness<T>(group: T[], options: TableRenderOptions): T[] {
// Assume data from the DAP comes in Big Endian so we need to revert the order if we use Little Endian
return options.endianness === Endianness.Big ? group : group.reverse();
}

protected renderEightBits(memory: Memory, currentAddress: bigint, offset: number): React.ReactNode {
const { content, className, style, title } = this.getBitAttributes(memory, currentAddress, offset);
return <span
Expand Down
8 changes: 3 additions & 5 deletions src/webview/components/memory-widget.tsx
Expand Up @@ -17,7 +17,7 @@
import { DebugProtocol } from '@vscode/debugprotocol';
import React from 'react';
import { ColumnStatus } from '../columns/column-contribution-service';
import { Decoration, Endianness, Memory, MemoryDisplayConfiguration } from '../utils/view-types';
import { Decoration, Memory, MemoryDisplayConfiguration } from '../utils/view-types';
import { MemoryTable } from './memory-table';
import { OptionsWidget } from './options-widget';

Expand All @@ -40,11 +40,9 @@ interface MemoryWidgetProps extends MemoryDisplayConfiguration {
}

interface MemoryWidgetState {
endianness: Endianness;
}

const defaultOptions: MemoryWidgetState = {
endianness: Endianness.Little,
};

export class MemoryWidget extends React.Component<MemoryWidgetProps, MemoryWidgetState> {
Expand All @@ -62,7 +60,7 @@ export class MemoryWidget extends React.Component<MemoryWidgetProps, MemoryWidge
memoryReference={this.props.memoryReference}
offset={this.props.offset}
count={this.props.count}
endianness={this.state.endianness}
endianness={this.props.endianness}
bytesPerWord={this.props.bytesPerWord}
wordsPerGroup={this.props.wordsPerGroup}
groupsPerRow={this.props.groupsPerRow}
Expand All @@ -78,7 +76,7 @@ export class MemoryWidget extends React.Component<MemoryWidgetProps, MemoryWidge
decorations={this.props.decorations}
columnOptions={this.props.columns.filter(candidate => candidate.active)}
memory={this.props.memory}
endianness={this.state.endianness}
endianness={this.props.endianness}
bytesPerWord={this.props.bytesPerWord}
wordsPerGroup={this.props.wordsPerGroup}
groupsPerRow={this.props.groupsPerRow}
Expand Down
18 changes: 18 additions & 0 deletions src/webview/components/options-widget.tsx
Expand Up @@ -28,6 +28,7 @@ import {
} from '../utils/view-types';
import { MultiSelectWithLabel } from './multi-select';
import { Checkbox } from 'primereact/checkbox';
import { Endianness } from '../../common/memory-range';

export interface OptionsWidgetProps
extends Omit<TableRenderOptions, 'scrollingBehavior'>,
Expand All @@ -54,6 +55,7 @@ const enum InputId {
BytesPerWord = 'word-size',
WordsPerGroup = 'words-per-group',
GroupsPerRow = 'groups-per-row',
EndiannessId = 'endianness',
AddressRadix = 'address-radix',
ShowRadixPrefix = 'show-radix-prefix',
}
Expand Down Expand Up @@ -305,6 +307,19 @@ export class OptionsWidget extends React.Component<OptionsWidgetProps, OptionsWi
options={allowedGroupsPerRow}
className='advanced-options-dropdown' />

<label
htmlFor={InputId.EndiannessId}
className='advanced-options-label mt-1'
>
Group Endianness
</label>
<Dropdown
id={InputId.EndiannessId}
value={this.props.endianness}
onChange={this.handleAdvancedOptionsDropdownChange}
options={Object.values(Endianness)}
className='advanced-options-dropdown' />

<h2>Address Format</h2>
<label
htmlFor={InputId.AddressRadix}
Expand Down Expand Up @@ -401,6 +416,9 @@ export class OptionsWidget extends React.Component<OptionsWidgetProps, OptionsWi
case InputId.GroupsPerRow:
this.props.updateRenderOptions({ groupsPerRow: Number(value) });
break;
case InputId.EndiannessId:
this.props.updateRenderOptions({ endianness: value });
break;
case InputId.AddressRadix:
this.props.updateRenderOptions({ addressRadix: Number(value) });
break;
Expand Down
3 changes: 3 additions & 0 deletions src/webview/memory-webview-view.tsx
Expand Up @@ -38,6 +38,7 @@ import { AddressColumn } from './columns/address-column';
import { DataColumn } from './columns/data-column';
import { PrimeReactProvider } from 'primereact/api';
import 'primeflex/primeflex.css';
import { Endianness } from '../common/memory-range';

export interface MemoryAppState extends MemoryState, MemoryDisplayConfiguration {
title: string;
Expand All @@ -49,6 +50,7 @@ const MEMORY_DISPLAY_CONFIGURATION_DEFAULTS: MemoryDisplayConfiguration = {
bytesPerWord: 1,
wordsPerGroup: 1,
groupsPerRow: 4,
endianness: Endianness.Little,
scrollingBehavior: 'Paginate',
addressRadix: 16,
showRadixPrefix: true,
Expand Down Expand Up @@ -109,6 +111,7 @@ class App extends React.Component<{}, MemoryAppState> {
isMemoryFetching={this.state.isMemoryFetching}
bytesPerWord={this.state.bytesPerWord}
groupsPerRow={this.state.groupsPerRow}
endianness={this.state.endianness}
wordsPerGroup={this.state.wordsPerGroup}
scrollingBehavior={this.state.scrollingBehavior}
addressRadix={this.state.addressRadix}
Expand Down
8 changes: 2 additions & 6 deletions src/webview/utils/view-types.ts
Expand Up @@ -17,12 +17,7 @@
import type { DebugProtocol } from '@vscode/debugprotocol';
import deepequal from 'fast-deep-equal';
import type * as React from 'react';
import { areRangesEqual, BigIntMemoryRange, Radix } from '../../common/memory-range';

export enum Endianness {
Little = 'Little Endian',
Big = 'Big Endian'
}
import { areRangesEqual, BigIntMemoryRange, Endianness, Radix } from '../../common/memory-range';

export interface Memory {
address: bigint;
Expand Down Expand Up @@ -82,6 +77,7 @@ export interface MemoryDisplayConfiguration {
bytesPerWord: number;
wordsPerGroup: number;
groupsPerRow: number;
endianness: Endianness;
scrollingBehavior: ScrollingBehavior;
addressRadix: Radix;
showRadixPrefix: boolean;
Expand Down

0 comments on commit 41ecf3a

Please sign in to comment.