Skip to content

Commit

Permalink
Make memory address padding configurable
Browse files Browse the repository at this point in the history
Introduces VS Code setting for memory address padding with the values
  * Unpadded
  * Minimal padding
  * 32 bit padding
  * 64 bit padding

Fixes #76
  • Loading branch information
planger committed Mar 1, 2024
1 parent d1400a1 commit 84e1195
Show file tree
Hide file tree
Showing 10 changed files with 100 additions and 14 deletions.
17 changes: 17 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,23 @@
],
"description": "Behavior when adding more memory beyond the current view."
},
"memory-inspector.addressPadding": {
"type": "string",
"enum": [
"Minimal",
"Unpadded",
"32bit",
"64bit"
],
"default": "Minimal",
"enumDescriptions": [
"Minimal padding based on the longest address in range.",
"Disables padding, presenting addresses as-is.",
"Pads addresses to fit within a 32-bit address space.",
"Pads addresses to fit within a 64-bit address space."
],
"description": "Controls the padding with 0s for memory addresses, enhancing readability by aligning them within a specified bit-width context."
},
"memory-inspector.addressRadix": {
"type": "number",
"enum": [
Expand Down
7 changes: 5 additions & 2 deletions src/common/memory-range.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,14 @@ export function getRadixMarker(radix: Radix): string {
return radixPrefixMap[radix];
}

export function getAddressString(address: bigint, radix: Radix, architecture: Architecture = 32, addPadding = false): string {
const paddedLength = addPadding ? Math.ceil(architecture / Math.log2(radix)) : 0;
export function getAddressString(address: bigint, radix: Radix, paddedLength: number = 0): string {
return address.toString(radix).padStart(paddedLength, '0');
}

export function getAddressLength(padding: number, radix: Radix): number {
return Math.ceil(padding / Math.log2(radix));
}

export function toHexStringWithRadixMarker(target: bigint): string {
return `${getRadixMarker(Radix.Hexadecimal)}${getAddressString(target, Radix.Hexadecimal)}`;
}
Expand Down
2 changes: 2 additions & 0 deletions src/plugin/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export const CONFIG_GROUPS_PER_ROW = 'groupings.groupsPerRow';
export const DEFAULT_GROUPS_PER_ROW = 4;
export const CONFIG_SCROLLING_BEHAVIOR = 'scrollingBehavior';
export const DEFAULT_SCROLLING_BEHAVIOR = 'Paginate';
export const CONFIG_ADDRESS_PADDING = 'addressPadding';
export const DEFAULT_ADDRESS_PADDING = 'Minimal';
export const CONFIG_ADDRESS_RADIX = 'addressRadix';
export const DEFAULT_ADDRESS_RADIX = 16;
export const CONFIG_SHOW_RADIX_PREFIX = 'showRadixPrefix';
Expand Down
5 changes: 3 additions & 2 deletions src/plugin/memory-webview-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import {
import { MemoryProvider } from './memory-provider';
import { outputChannelLogger } from './logger';
import { VariableRange } from '../common/memory-range';
import { MemoryViewSettings, ScrollingBehavior } from '../webview/utils/view-types';
import { AddressPaddingOptions, MemoryViewSettings, ScrollingBehavior } from '../webview/utils/view-types';

interface Variable {
name: string;
Expand Down Expand Up @@ -225,9 +225,10 @@ export class MemoryWebview implements vscode.CustomReadonlyEditorProvider {
const visibleColumns = CONFIGURABLE_COLUMNS
.filter(column => vscode.workspace.getConfiguration(manifest.PACKAGE_NAME).get<boolean>(column, false))
.map(columnId => columnId.replace('columns.', ''));
const addressPadding = AddressPaddingOptions[memoryInspectorConfiguration.get(manifest.CONFIG_ADDRESS_PADDING, manifest.DEFAULT_ADDRESS_PADDING)];
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, addressPadding, addressRadix, showRadixPrefix };
}

protected async readMemory(request: DebugProtocol.ReadMemoryArguments): Promise<MemoryReadResult> {
Expand Down
9 changes: 5 additions & 4 deletions src/webview/columns/address-column.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@

import React, { ReactNode } from 'react';
import { BigIntMemoryRange, getAddressString, getRadixMarker } from '../../common/memory-range';
import { ColumnContribution } from './column-contribution-service';
import { Memory, MemoryDisplayConfiguration } from '../utils/view-types';
import { ColumnContribution, TableRenderOptions } from './column-contribution-service';
import { Memory } from '../utils/view-types';

export class AddressColumn implements ColumnContribution {
static ID = 'address';
Expand All @@ -26,10 +26,11 @@ export class AddressColumn implements ColumnContribution {
readonly label = 'Address';
readonly priority = 0;

render(range: BigIntMemoryRange, _: Memory, options: MemoryDisplayConfiguration): ReactNode {
render(range: BigIntMemoryRange, _: Memory, options: TableRenderOptions): ReactNode {
console.log('options', options);
return <span className='memory-start-address'>
{options.showRadixPrefix && <span className='radix-prefix'>{getRadixMarker(options.addressRadix)}</span>}
<span className='address'>{getAddressString(range.startAddress, options.addressRadix)}</span>
<span className='address'>{getAddressString(range.startAddress, options.addressRadix, options.effectiveAddressLength)}</span>
</span>;
}
}
1 change: 1 addition & 0 deletions src/webview/components/memory-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ interface MemoryTableProps extends TableRenderOptions, MemoryDisplayConfiguratio
decorations: Decoration[];
offset: number;
count: number;
effectiveAddressLength: number;
fetchMemory(partialOptions?: Partial<DebugProtocol.ReadMemoryArguments>): Promise<void>;
isMemoryFetching: boolean;
}
Expand Down
4 changes: 4 additions & 0 deletions src/webview/components/memory-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ interface MemoryWidgetProps extends MemoryDisplayConfiguration {
memoryReference: string;
offset: number;
count: number;
effectiveAddressLength: number;
isMemoryFetching: boolean;
refreshMemory: () => void;
updateMemoryArguments: (memoryArguments: Partial<DebugProtocol.ReadMemoryArguments>) => void;
Expand Down Expand Up @@ -70,6 +71,7 @@ export class MemoryWidget extends React.Component<MemoryWidgetProps, MemoryWidge
updateRenderOptions={this.props.updateMemoryDisplayConfiguration}
resetRenderOptions={this.props.resetMemoryDisplayConfiguration}
refreshMemory={this.props.refreshMemory}
addressPadding={this.props.addressPadding}
addressRadix={this.props.addressRadix}
showRadixPrefix={this.props.showRadixPrefix}
toggleColumn={this.props.toggleColumn}
Expand All @@ -84,9 +86,11 @@ export class MemoryWidget extends React.Component<MemoryWidgetProps, MemoryWidge
groupsPerRow={this.props.groupsPerRow}
offset={this.props.offset}
count={this.props.count}
effectiveAddressLength={this.props.effectiveAddressLength}
fetchMemory={this.props.fetchMemory}
isMemoryFetching={this.props.isMemoryFetching}
scrollingBehavior={this.props.scrollingBehavior}
addressPadding={this.props.addressPadding}
addressRadix={this.props.addressRadix}
showRadixPrefix={this.props.showRadixPrefix}
/>
Expand Down
24 changes: 20 additions & 4 deletions src/webview/components/options-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,12 @@ import { OverlayPanel } from 'primereact/overlaypanel';
import { classNames } from 'primereact/utils';
import React, { FocusEventHandler, KeyboardEvent, KeyboardEventHandler, MouseEventHandler } from 'react';
import { TableRenderOptions } from '../columns/column-contribution-service';
import {
SerializedTableRenderOptions,
} from '../utils/view-types';
import { AddressPaddingOptions, SerializedTableRenderOptions } from '../utils/view-types';
import { MultiSelectWithLabel } from './multi-select';
import { Checkbox } from 'primereact/checkbox';

export interface OptionsWidgetProps
extends Omit<TableRenderOptions, 'scrollingBehavior'>,
extends Omit<TableRenderOptions, 'scrollingBehavior' | 'effectiveAddressLength'>,
Required<DebugProtocol.ReadMemoryArguments> {
title: string;
updateRenderOptions: (options: Partial<SerializedTableRenderOptions>) => void;
Expand All @@ -54,6 +52,7 @@ const enum InputId {
BytesPerWord = 'word-size',
WordsPerGroup = 'words-per-group',
GroupsPerRow = 'groups-per-row',
AddressPadding = 'address-padding',
AddressRadix = 'address-radix',
ShowRadixPrefix = 'show-radix-prefix',
}
Expand Down Expand Up @@ -306,6 +305,20 @@ export class OptionsWidget extends React.Component<OptionsWidgetProps, OptionsWi
className='advanced-options-dropdown' />

<h2>Address Format</h2>

<label
htmlFor={InputId.AddressPadding}
className='advanced-options-label'
>
Address Padding
</label>
<Dropdown
id={InputId.AddressPadding}
value={this.props.addressPadding}
onChange={this.handleAdvancedOptionsDropdownChange}
options={Object.entries(AddressPaddingOptions).map(([label, value]) => ({ label, value }))}
className="advanced-options-dropdown" />

<label
htmlFor={InputId.AddressRadix}
className='advanced-options-label'
Expand Down Expand Up @@ -401,6 +414,9 @@ export class OptionsWidget extends React.Component<OptionsWidgetProps, OptionsWi
case InputId.GroupsPerRow:
this.props.updateRenderOptions({ groupsPerRow: Number(value) });
break;
case InputId.AddressPadding:
this.props.updateRenderOptions({ addressPadding: value });
break;
case InputId.AddressRadix:
this.props.updateRenderOptions({ addressRadix: Number(value) });
break;
Expand Down
34 changes: 33 additions & 1 deletion src/webview/memory-webview-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ import { AddressColumn } from './columns/address-column';
import { DataColumn } from './columns/data-column';
import { PrimeReactProvider } from 'primereact/api';
import 'primeflex/primeflex.css';
import { getAddressLength, getAddressString } from '../common/memory-range';

export interface MemoryAppState extends MemoryState, MemoryDisplayConfiguration {
title: string;
effectiveAddressLength: number;
decorations: Decoration[];
columns: ColumnStatus[];
}
Expand All @@ -50,6 +52,7 @@ const MEMORY_DISPLAY_CONFIGURATION_DEFAULTS: MemoryDisplayConfiguration = {
wordsPerGroup: 1,
groupsPerRow: 4,
scrollingBehavior: 'Paginate',
addressPadding: 'Min',
addressRadix: 16,
showRadixPrefix: true,
};
Expand All @@ -69,6 +72,7 @@ class App extends React.Component<{}, MemoryAppState> {
memoryReference: '',
offset: 0,
count: 256,
effectiveAddressLength: 0,
decorations: [],
columns: columnContributionService.getColumns(),
isMemoryFetching: false,
Expand All @@ -89,6 +93,15 @@ class App extends React.Component<{}, MemoryAppState> {
messenger.sendNotification(readyType, HOST_EXTENSION, undefined);
}

public componentDidUpdate(_: {}, prevState: MemoryAppState): void {
const addressPaddingNeedsUpdate =
(this.state.addressPadding === 'Min' && this.state.memory !== prevState.memory)
|| this.state.addressPadding !== prevState.addressPadding;
if (addressPaddingNeedsUpdate) {
this.setState({ effectiveAddressLength: this.getEffectiveAddressLength(this.state.memory), });
}
}

public render(): React.ReactNode {
return <PrimeReactProvider>
<MemoryWidget
Expand All @@ -99,6 +112,7 @@ class App extends React.Component<{}, MemoryAppState> {
offset={this.state.offset ?? 0}
count={this.state.count}
title={this.state.title}
effectiveAddressLength={this.state.effectiveAddressLength}
updateMemoryArguments={this.updateMemoryState}
updateMemoryDisplayConfiguration={this.updateMemoryDisplayConfiguration}
resetMemoryDisplayConfiguration={this.resetMemoryDisplayConfiguration}
Expand All @@ -111,6 +125,7 @@ class App extends React.Component<{}, MemoryAppState> {
groupsPerRow={this.state.groupsPerRow}
wordsPerGroup={this.state.wordsPerGroup}
scrollingBehavior={this.state.scrollingBehavior}
addressPadding={this.state.addressPadding}
addressRadix={this.state.addressRadix}
showRadixPrefix={this.state.showRadixPrefix}
/>
Expand Down Expand Up @@ -149,9 +164,11 @@ class App extends React.Component<{}, MemoryAppState> {
executor => executor.fetchData(completeOptions)
));

const memory = this.convertMemory(response);

this.setState({
decorations: decorationService.decorations,
memory: this.convertMemory(response),
memory,
memoryReference: completeOptions.memoryReference,
offset: completeOptions.offset,
count: completeOptions.count,
Expand All @@ -172,6 +189,21 @@ class App extends React.Component<{}, MemoryAppState> {
return { bytes, address };
}

protected getEffectiveAddressLength(memory?: Memory): number {
const { addressRadix, addressPadding } = this.state;
return addressPadding === 'Min' ? this.getLastAddressLength(memory) : getAddressLength(addressPadding, addressRadix);
}

protected getLastAddressLength(memory?: Memory): number {
if (memory === undefined) {
return 0;
}
const rowLength = this.state.bytesPerWord * this.state.wordsPerGroup * this.state.groupsPerRow;
const rows = Math.ceil(memory.bytes.length / rowLength);
const finalAddress = memory.address + BigInt(((rows - 1) * rowLength));
return getAddressString(finalAddress, this.state.addressRadix).length;
}

protected toggleColumn = (id: string, active: boolean): void => { this.doToggleColumn(id, active); };
protected async doToggleColumn(id: string, isVisible: boolean): Promise<void> {
const columns = isVisible ? await columnContributionService.show(id, this.state) : columnContributionService.hide(id);
Expand Down
11 changes: 10 additions & 1 deletion src/webview/utils/view-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export interface Memory {
export interface SerializedTableRenderOptions extends MemoryDisplayConfiguration {
columnOptions: Array<{ label: string, doRender: boolean }>;
endianness: Endianness;
bytesPerWord: number;
effectiveAddressLength: number;
}

export interface Event<T> {
Expand Down Expand Up @@ -83,11 +83,20 @@ export interface MemoryDisplayConfiguration {
wordsPerGroup: number;
groupsPerRow: number;
scrollingBehavior: ScrollingBehavior;
addressPadding: AddressPadding;
addressRadix: Radix;
showRadixPrefix: boolean;
}
export type ScrollingBehavior = 'Paginate' | 'Infinite';

export type AddressPadding = 'Min' | number;
export const AddressPaddingOptions = {
'Minimal': 'Min',
'Unpadded': 0,
'32bit': 32,
'64bit': 64,
} as const;

export interface ColumnVisibilityStatus {
visibleColumns: string[];
}
Expand Down

0 comments on commit 84e1195

Please sign in to comment.