|
|
/*++
Copyright (c) 1992-2002 Microsoft Corporation
Module Name:
Memwin.cpp
Abstract:
This module contains the main line code for display of multiple memory windows and the subclassed win proc to handle editing, display, etc.
--*/
#include "precomp.hxx"
#pragma hdrstop
_INTERFACE_TYPE_NAMES rgInterfaceTypeNames[MaximumInterfaceType] = { { Internal, "Internal" }, { Isa, "Isa" }, { Eisa, "Eisa" }, { MicroChannel, "MicroChannel" }, { TurboChannel, "TurboChannel" }, { PCIBus, "PCIBus" }, { VMEBus, "VMEBus" }, { NuBus, "NuBus" }, { PCMCIABus, "PCMCIABus" }, { CBus, "CBus" }, { MPIBus, "MPIBus" }, { MPSABus, "MPSABus" }, { ProcessorInternal, "ProcessorInternal" }, { InternalPowerBus, "InternalPowerBus" }, { PNPISABus, "PNPISABus" }, { PNPBus, "PNPBus" } };
_BUS_TYPE_NAMES rgBusTypeNames[MaximumBusDataType] = { { Cmos, "Cmos" }, { EisaConfiguration, "EisaConfiguration" }, { Pos, "Pos" }, { CbusConfiguration, "CbusConfiguration" }, { PCIConfiguration, "PCIConfiguration" }, { VMEConfiguration, "VMEConfiguration" }, { NuBusConfiguration, "NuBusConfiguration" }, { PCMCIAConfiguration, "PCMCIAConfiguration" }, { MPIConfiguration, "MPIConfiguration" }, { MPSAConfiguration, "MPSAConfiguration" }, { PNPISAConfiguration, "PNPISAConfiguration" }, { SgiInternalConfiguration, "SgiInternalConfiguration" } };
PSTR g_MemTypeNames[] = { "Virtual:", "Physical:", "Control:", "I/O:", "MSR:", "Bus data:" };
//
//
//
MEMWIN_DATA::MEMWIN_DATA() : EDITWIN_DATA(512) { m_enumType = MEM_WINDOW;
ZeroMemory(&m_GenMemData, sizeof(m_GenMemData));
strcpy(m_OffsetExpr, FormatAddr64(g_EventIp)); m_GenMemData.memtype = VIRTUAL_MEM_TYPE; m_GenMemData.nDisplayFormat = 3; m_Columns = 4; m_WindowDataSize = 0; }
void MEMWIN_DATA::Validate() { EDITWIN_DATA::Validate();
Assert(MEM_WINDOW == m_enumType); }
HRESULT MEMWIN_DATA::ReadState(void) { HRESULT Status; ULONG DataNeeded; PVOID Data; ULONG64 Offset; DEBUG_VALUE Value; ULONG i;
// Evaluate offset expression.
if ((Status = g_pDbgControl->Evaluate(m_OffsetExpr, DEBUG_VALUE_INT64, &Value, NULL)) != S_OK) { return Status; }
Offset = Value.I64;
// Compute how much data to retrieve. We don't want to
// create a big matrix of memtype/display format so just
// ask for a chunk of data big enough for any display format.
DataNeeded = m_LineHeight * m_Columns * 2 * sizeof(ULONG64);
Empty(); Data = AddData(DataNeeded); if (Data == NULL) { return E_OUTOFMEMORY; }
ULONG Read;
switch(m_GenMemData.memtype) { default: Assert(!"Unhandled condition"); Status = E_FAIL; break;
case PHYSICAL_MEM_TYPE: Status = g_pDbgData->ReadPhysical(Offset, Data, DataNeeded, &Read ); break;
case VIRTUAL_MEM_TYPE: Status = g_pDbgData->ReadVirtual(Offset, Data, DataNeeded, &Read ); break;
case CONTROL_MEM_TYPE: Status = g_pDbgData->ReadControl(m_GenMemData.any.control.Processor, Offset, Data, DataNeeded, &Read ); break;
case IO_MEM_TYPE: Status = g_pDbgData->ReadIo(m_GenMemData.any.io.interface_type, m_GenMemData.any.io.BusNumber, m_GenMemData.any.io.AddressSpace, Offset, Data, DataNeeded, &Read ); break;
case MSR_MEM_TYPE: Read = 0; for (i = 0; i < DataNeeded / sizeof(ULONG64); i++) { if ((Status = g_pDbgData->ReadMsr((ULONG)Offset + i, (PULONG64)Data + i )) != S_OK) { // Assume an error means we've run out of MSRs to
// read. If some were read, don't consider it an error.
if (Read > 0) { Status = S_OK; } break; }
Read += sizeof(ULONG64); } break;
case BUS_MEM_TYPE: Status = g_pDbgData->ReadBusData(m_GenMemData.any.bus.bus_type, m_GenMemData.any.bus.BusNumber, m_GenMemData.any.bus.SlotNumber, (ULONG)Offset, Data, DataNeeded, &Read ); break; }
if (Status == S_OK) { // Trim data back if read didn't get everything.
RemoveTail(DataNeeded - Read); m_OffsetRead = Offset; }
return Status; }
BOOL MEMWIN_DATA::HasEditableProperties() { return TRUE; }
BOOL MEMWIN_DATA::EditProperties() /*++
Returns TRUE - If properties were edited FALSE - If nothing was changed --*/ { if (g_TargetClass != DEBUG_CLASS_UNINITIALIZED) { INT_PTR Res = DisplayOptionsPropSheet(GetParent(m_hwndChild), g_hInst, m_GenMemData.memtype ); if (IDOK == Res) { UpdateOptions(); return TRUE; // Properties have been changed
} }
MessageBeep(0); return FALSE; // No Debuggee or User Cancel out.
}
BOOL MEMWIN_DATA::OnCreate(void) { RECT Rect; int i; ULONG Height;
Height = GetSystemMetrics(SM_CYVSCROLL) + 4 * GetSystemMetrics(SM_CYEDGE);
m_Toolbar = CreateWindowEx(0, REBARCLASSNAME, NULL, WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NOPARENTALIGN | RBS_VARHEIGHT | RBS_BANDBORDERS, 0, 0, m_Size.cx, Height, m_Win, (HMENU)ID_TOOLBAR, g_hInst, NULL); if (m_Toolbar == NULL) { return FALSE; }
REBARINFO BarInfo; BarInfo.cbSize = sizeof(BarInfo); BarInfo.fMask = 0; BarInfo.himl = NULL; SendMessage(m_Toolbar, RB_SETBARINFO, 0, (LPARAM)&BarInfo);
REBARBANDINFO BandInfo; BandInfo.cbSize = sizeof(BandInfo); BandInfo.fMask = RBBIM_TEXT | RBBIM_CHILD | RBBIM_CHILDSIZE;
m_ToolbarEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_VISIBLE | WS_CHILD | ES_AUTOHSCROLL, 0, 0, 18 * m_Font->Metrics.tmAveCharWidth, Height, m_Toolbar, (HMENU)IDC_EDIT_OFFSET, g_hInst, NULL); if (m_ToolbarEdit == NULL) { return FALSE; }
SendMessage(m_ToolbarEdit, WM_SETFONT, (WPARAM)m_Font->Font, 0); SendMessage(m_ToolbarEdit, EM_LIMITTEXT, sizeof(m_OffsetExpr) - 1, 0);
GetClientRect(m_ToolbarEdit, &Rect);
BandInfo.lpText = "Offset:"; BandInfo.hwndChild = m_ToolbarEdit; BandInfo.cxMinChild = Rect.right - Rect.left; BandInfo.cyMinChild = Rect.bottom - Rect.top; SendMessage(m_Toolbar, RB_INSERTBAND, -1, (LPARAM)&BandInfo);
m_FormatCombo = CreateWindowEx(0, "COMBOBOX", NULL, WS_VISIBLE | WS_CHILD | WS_VSCROLL | CBS_SORT | CBS_DROPDOWNLIST, 0, 0, 15 * m_Font->Metrics.tmAveCharWidth, (g_nMaxNumFormatsMemWin * m_Font->Metrics.tmHeight / 2), m_Toolbar, (HMENU)IDC_COMBO_DISPLAY_FORMAT, g_hInst, NULL); if (m_FormatCombo == NULL) { return FALSE; }
SendMessage(m_FormatCombo, WM_SETFONT, (WPARAM)m_Font->Font, 0);
for (i = 0; i < g_nMaxNumFormatsMemWin; i++) { LRESULT Idx;
// The format strings will be sorted so mark them with
// their true index for retrieval when selected.
Idx = SendMessage(m_FormatCombo, CB_ADDSTRING, 0, (LPARAM)g_FormatsMemWin[i].lpszDescription); SendMessage(m_FormatCombo, CB_SETITEMDATA, (WPARAM)Idx, i); }
GetClientRect(m_FormatCombo, &Rect);
BandInfo.lpText = "Display format:"; BandInfo.hwndChild = m_FormatCombo; BandInfo.cxMinChild = Rect.right - Rect.left; BandInfo.cyMinChild = Rect.bottom - Rect.top; SendMessage(m_Toolbar, RB_INSERTBAND, -1, (LPARAM)&BandInfo);
PSTR PrevText = "Previous"; m_PreviousButton = AddButtonBand(m_Toolbar, PrevText, PrevText, IDC_MEM_PREVIOUS); m_NextButton = AddButtonBand(m_Toolbar, "Next", PrevText, IDC_MEM_NEXT); if (m_PreviousButton == NULL || m_NextButton == NULL) { return FALSE; }
// Maximize the space for the offset expression.
SendMessage(m_Toolbar, RB_MAXIMIZEBAND, 0, FALSE);
GetClientRect(m_Toolbar, &Rect); m_ToolbarHeight = Rect.bottom - Rect.top + GetSystemMetrics(SM_CYEDGE); m_ShowToolbar = TRUE;
if (!EDITWIN_DATA::OnCreate()) { return FALSE; }
SendMessage(m_hwndChild, EM_SETBKGNDCOLOR, FALSE, g_Colors[COL_PLAIN].Color);
SendMessage(m_hwndChild, EM_SETEVENTMASK, 0, ENM_KEYEVENTS); UpdateOptions(); return TRUE; }
LRESULT MEMWIN_DATA::OnCommand( WPARAM wParam, LPARAM lParam ) { switch(LOWORD(wParam)) { case IDC_EDIT_OFFSET: if (HIWORD(wParam) == EN_CHANGE) { // This message is sent on every keystroke
// which causes a bit too much updating.
// Set up a timer to trigger the actual
// update in half a second.
SetTimer(m_Win, IDC_EDIT_OFFSET, EDIT_DELAY, NULL); m_UpdateExpr = TRUE; } break;
case IDC_COMBO_DISPLAY_FORMAT: if (HIWORD(wParam) == CBN_SELCHANGE) { LRESULT Sel = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0); if (Sel != CB_ERR) { m_GenMemData.nDisplayFormat = (int) SendMessage((HWND)lParam, CB_GETITEMDATA, (WPARAM)Sel, 0); UpdateOptions(); UiRequestRead(); } } break;
case IDC_MEM_PREVIOUS: ScrollLower(); break; case IDC_MEM_NEXT: ScrollHigher(); break; }
return 0; }
void MEMWIN_DATA::OnSize(void) { EDITWIN_DATA::OnSize();
// Force buffer to refill for new line count.
UiRequestRead(); }
void MEMWIN_DATA::OnTimer(WPARAM TimerId) { if (TimerId == IDC_EDIT_OFFSET && m_UpdateExpr) { m_UpdateExpr = FALSE; if (SendMessage(m_ToolbarEdit, EM_GETMODIFY, 0,0)) { GetWindowText(m_ToolbarEdit, m_OffsetExpr, sizeof(m_OffsetExpr)); SendMessage(m_ToolbarEdit, EM_SETMODIFY, 0,0); UiRequestRead(); } // KillTimer(m_Win, IDC_EDIT_OFFSET);
} }
LRESULT MEMWIN_DATA::OnNotify(WPARAM Wpm, LPARAM Lpm) { LPNMHDR Hdr = (LPNMHDR)Lpm;
switch(Hdr->code) { case RBN_HEIGHTCHANGE: PostMessage(m_Win, WU_RECONFIGURE, 0, 0); break; case EN_MSGFILTER: MSGFILTER* Filter = (MSGFILTER*)Lpm;
if (Filter->msg == WM_KEYDOWN) { switch(Filter->wParam) { case VK_UP: { CHARRANGE range;
SendMessage(m_hwndChild, EM_EXGETSEL, 0, (LPARAM) &range); if (!SendMessage(m_hwndChild, EM_LINEFROMCHAR, range.cpMin, 0)) { // up arrow on top line, scroll
ScrollLower(); return TRUE; } break; } case VK_DOWN: { CHARRANGE range; int MaxLine;
SendMessage(m_hwndChild, EM_EXGETSEL, 0, (LPARAM) &range); MaxLine = (int)SendMessage(m_hwndChild, EM_GETLINECOUNT, 0, 0);
if (MaxLine == (1 + SendMessage(m_hwndChild, EM_LINEFROMCHAR, range.cpMin, 0))) { // down arrow on bottom line, scroll
ScrollHigher(); return TRUE; } break; }
case VK_PRIOR: ScrollLower(); return TRUE; case VK_NEXT: ScrollHigher(); return TRUE;
case VK_LEFT: case VK_RIGHT: break; case VK_DELETE: MessageBeep(0); return TRUE; default: // Allow default processing of everything else
return TRUE; } } else if (Filter->msg == WM_KEYUP) { return TRUE; }
if (ES_READONLY & GetWindowLongPtr(m_hwndChild, GWL_STYLE)) { break; } if (Filter->msg == WM_CHAR) { switch(Filter->wParam) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'a': case 'A': case 'b': case 'B': case 'c': case 'C': case 'd': case 'D': case 'e': case 'E': case 'f': case 'F': { CHARRANGE value; ULONG charIndex; ULONG64 Address; CHAR writeval[2] = {0};
writeval[0] = (CHAR) tolower((CHAR) Filter->wParam); Address = GetAddressOfCurValue(&charIndex, &value); if (Address) { TEXTRANGE textRange;
SendMessage(m_hwndChild, EM_SETSEL, charIndex, charIndex+1); SendMessage(m_hwndChild, EM_REPLACESEL, FALSE, (LPARAM) writeval);
textRange.chrg = value; textRange.lpstrText = &m_ValueExpr[0]; if (SendMessage(m_hwndChild, EM_GETTEXTRANGE, 0, (LPARAM) &textRange)) { m_ValueExpr[charIndex - value.cpMin] = writeval[0]; WriteValue(Address); SendMessage(m_hwndChild, EM_SETSEL, charIndex+1, charIndex+1); return TRUE; } } }
default: MessageBeep(0); return TRUE; } } break; }
return EDITWIN_DATA::OnNotify(Wpm, Lpm); }
void MEMWIN_DATA::OnUpdate( UpdateType Type ) { if (Type != UPDATE_BUFFER) { return; }
HRESULT Status;
Status = UiLockForRead(); if (Status == S_OK) { ULONG charIndex; SendMessage(m_hwndChild, EM_GETSEL, (WPARAM) &charIndex, NULL);
SendMessage(m_hwndChild, WM_SETREDRAW, FALSE, 0);
CHARRANGE Sel;
// Select everything so it's all replaced.
Sel.cpMin = 0; Sel.cpMax = -1; SendMessage(m_hwndChild, EM_EXSETSEL, 0, (LPARAM)&Sel);
TCHAR Buf[64]; TCHAR CharBuf[64]; TCHAR* ColChar; ULONG Row, Col; ULONG64 Offset; ULONG64 DataEnd; PUCHAR Data; ULONG Bytes; _FORMATS_MEM_WIN* Fmt = g_FormatsMemWin + m_GenMemData.nDisplayFormat;
Offset = m_OffsetRead; Data = (PUCHAR)m_Data; DataEnd = Offset + m_DataUsed; Bytes = (Fmt->cBits + 7) / 8;
for (Row = 0; Row < m_LineHeight; Row++) { SendMessage(m_hwndChild, EM_REPLACESEL, FALSE, (LPARAM)FormatAddr64(Offset));
ColChar = CharBuf; *ColChar++ = ' '; *ColChar++ = ' ';
for (Col = 0; Col < m_Columns; Col++) { if (Offset < DataEnd) { _tcscpy(Buf, _T(" "));
// If the formatting succeeds,
// Buf contains the formatted data.
if (!CPFormatMemory(Buf + 1, (DWORD)min(_tsizeof(Buf) - 1, Fmt->cchMax + 1), Data, Fmt->cBits, Fmt->fmtType, Fmt->radix)) { // Else we don't know what to format
for (UINT uTmp = 0; uTmp < Bytes; uTmp++) { m_AllowWrite = FALSE; _tcscat(Buf + 1, _T("??")); } }
if (Fmt->fTwoFields) { if (!CPFormatMemory(ColChar, 1, Data, 8, fmtAscii, 0)) { *ColChar = '?'; }
ColChar++; } } else { m_AllowWrite = FALSE; _tcscpy(Buf, _T(" ????????")); *ColChar++ = '?'; } SendMessage(m_hwndChild, EM_REPLACESEL, FALSE, (LPARAM)Buf);
Data += Bytes; Offset += Bytes; }
if (Fmt->fTwoFields) { *ColChar = 0; SendMessage(m_hwndChild, EM_REPLACESEL, FALSE, (LPARAM)CharBuf); }
// Don't complete the last line to avoid leaving
// a blank line at the bottom.
if (Row < m_LineHeight - 1) { SendMessage(m_hwndChild, EM_REPLACESEL, FALSE, (LPARAM)"\n"); } }
m_WindowDataSize = (ULONG)(Offset - m_OffsetRead);
UnlockStateBuffer(this);
SendMessage(m_hwndChild, WM_SETREDRAW, TRUE, 0); InvalidateRect(m_hwndChild, NULL, TRUE);
SendMessage(m_hwndChild, EM_SETSEL, charIndex, charIndex); } else { SendLockStatusMessage(m_hwndChild, WM_SETTEXT, Status); } }
void MEMWIN_DATA::ScrollLower(void) { ULONG64 Offs = m_OffsetRead;
if (Offs >= m_WindowDataSize) { Offs -= m_WindowDataSize; } else { Offs = 0; } sprintf(m_OffsetExpr, "0x%I64x", Offs); UiRequestRead(); }
void MEMWIN_DATA::ScrollHigher(void) { ULONG64 Offs = m_OffsetRead;
if (Offs + m_WindowDataSize > Offs) { Offs += m_WindowDataSize; } else { Offs = (ULONG64)-1 - m_WindowDataSize; } sprintf(m_OffsetExpr, "0x%I64x", Offs); UiRequestRead(); }
void MEMWIN_DATA::UpdateOptions(void) { REBARBANDINFO BandInfo;
BandInfo.cbSize = sizeof(BandInfo); BandInfo.fMask = RBBIM_TEXT; BandInfo.lpText = g_MemTypeNames[m_GenMemData.memtype]; SendMessage(m_Toolbar, RB_SETBANDINFO, 0, (LPARAM)&BandInfo); SetWindowText(m_ToolbarEdit, m_OffsetExpr);
m_AllowWrite = (m_GenMemData.memtype == PHYSICAL_MEM_TYPE || m_GenMemData.memtype == VIRTUAL_MEM_TYPE) && ((g_FormatsMemWin[m_GenMemData.nDisplayFormat].fmtType & fmtBasis) == fmtUInt || (g_FormatsMemWin[m_GenMemData.nDisplayFormat].fmtType & fmtBasis) == fmtInt || (g_FormatsMemWin[m_GenMemData.nDisplayFormat].fmtType & fmtBasis) == fmtAddress ) && g_FormatsMemWin[m_GenMemData.nDisplayFormat].radix == 16;
SendMessage(m_hwndChild, EM_SETREADONLY, !m_AllowWrite, 0);
for (LONG Idx = 0; Idx < g_nMaxNumFormatsMemWin; Idx++) { if ((LONG)SendMessage(m_FormatCombo, CB_GETITEMDATA, Idx, 0) == m_GenMemData.nDisplayFormat) { SendMessage(m_FormatCombo, CB_SETCURSEL, Idx, 0); break; } }
switch(m_GenMemData.memtype) { case MSR_MEM_TYPE: m_Columns = 1; break; default: if ((g_FormatsMemWin[m_GenMemData.nDisplayFormat].fmtType & fmtBasis) == fmtBit) { m_Columns = 8; } else if ((g_FormatsMemWin[m_GenMemData.nDisplayFormat].fmtType & fmtBasis) == fmtAscii || (g_FormatsMemWin[m_GenMemData.nDisplayFormat].fmtType & fmtBasis) == fmtUnicode || g_FormatsMemWin[m_GenMemData.nDisplayFormat].cBits == 8) { m_Columns = 16; } else if (g_FormatsMemWin[m_GenMemData.nDisplayFormat].cBits == 16) { m_Columns = 8; } else if (g_FormatsMemWin[m_GenMemData.nDisplayFormat].cBits > 64) { m_Columns = 2; } else { m_Columns = 4; } break; } }
void MEMWIN_DATA::WriteValue( ULONG64 Offset ) { if (!m_AllowWrite) { return; } ULONG64 Data; ULONG Size; DEBUG_VALUE Value;
// Evaluate value expression.
if (g_pDbgControl->Evaluate(m_ValueExpr, DEBUG_VALUE_INT64, &Value, NULL) != S_OK) { return; } Data = Value.I64; Size = g_FormatsMemWin[m_GenMemData.nDisplayFormat].cBits / 8; UIC_WRITE_DATA_DATA* WriteData;
WriteData = StartStructCommand(UIC_WRITE_DATA); if (WriteData == NULL) { return; }
// Fill in WriteData members.
memcpy(WriteData->Data, &Data, Size); WriteData->Length = Size; WriteData->Offset = Offset; WriteData->Type = m_GenMemData.memtype; WriteData->Any = m_GenMemData.any; FinishCommand(); }
ULONG64 MEMWIN_DATA::GetAddressOfCurValue( PULONG pCharIndex, CHARRANGE *pCRange ) { CHARRANGE range; ULONG CurLine, FirstLineChar, CurCol;
SendMessage(m_hwndChild, EM_EXGETSEL, NULL, (LPARAM) &range); CurLine = (ULONG)SendMessage(m_hwndChild, EM_LINEFROMCHAR, range.cpMin, 0); FirstLineChar = (ULONG)SendMessage(m_hwndChild, EM_LINEINDEX, CurLine, 0); CurCol = range.cpMin - FirstLineChar;
ULONG Length; PCHAR pLineTxt = (PCHAR) malloc(Length = ((ULONG)SendMessage(m_hwndChild, EM_LINELENGTH, FirstLineChar, 0) + 2));
if (!pLineTxt) { return 0; }
Assert(Length >= CurCol);
ZeroMemory(pLineTxt, Length); // Assert (Length = (ULONG) SendMessage(m_hwndChild, EM_GETLINE, (WPARAM) CurLine, (LPARAM) pLineTxt));
TEXTRANGE textrange; textrange.chrg.cpMin = FirstLineChar; textrange.chrg.cpMax = FirstLineChar + Length-2; textrange.lpstrText = (LPSTR) pLineTxt; SendMessage(m_hwndChild, EM_GETTEXTRANGE, 0, (LPARAM) &textrange);
ULONG ValueCol=0, Index=0, ValueIndex=0; while (pLineTxt[CurCol] == ' ') { CurCol++; } while (Index < CurCol) { if (pLineTxt[Index] == ' ') { if (ValueIndex != Index) { ValueCol++; } ValueIndex = Index+1; } ++Index; }
if (!ValueIndex || !pLineTxt[CurCol]) // cursor on address column
{ free (pLineTxt); return 0; }
ULONG Bytes; Bytes = (g_FormatsMemWin[m_GenMemData.nDisplayFormat].cBits + 7) / 8;
ULONG64 Offset; Offset = m_OffsetRead + (CurLine * Bytes * m_Columns) + (ValueCol - 1)*Bytes;
for (Index = ValueIndex; pLineTxt[Index] && pLineTxt[Index] != ' '; Index++) ; memcpy(m_ValueExpr, pLineTxt+ValueIndex, Index - ValueIndex); m_ValueExpr[Index-ValueIndex]=0;
free (pLineTxt); if (pCharIndex) { *pCharIndex = FirstLineChar + CurCol; } if (pCRange) { pCRange->cpMin = FirstLineChar + CurCol - (CurCol - ValueIndex); pCRange->cpMax = pCRange->cpMin + Index - ValueIndex; } return Offset; }
|