You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
591 lines
22 KiB
C++
591 lines
22 KiB
C++
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
|
|
#include "Woodpecker_precompiled.h"
|
|
|
|
#include "StreamerDataAggregator.hxx"
|
|
#include <Woodpecker/Driller/IO/moc_StreamerDataAggregator.cpp>
|
|
|
|
#include "StreamerDrillerDialog.hxx"
|
|
#include "StreamerEvents.h"
|
|
#include <AzCore/Serialization/SerializeContext.h>
|
|
#include <AzCore/UserSettings/UserSettings.h>
|
|
#include "Woodpecker/Driller/Workspaces/Workspace.h"
|
|
|
|
#include <QMenu>
|
|
#include <QAction>
|
|
|
|
namespace Driller
|
|
{
|
|
class StreamerDataAggregatorSavedState
|
|
: public AZ::UserSettings
|
|
{
|
|
public:
|
|
AZ_RTTI(StreamerDataAggregatorSavedState, "{0174A3EE-C555-482F-9E7B-7D67D9B4B0A7}", AZ::UserSettings);
|
|
AZ_CLASS_ALLOCATOR(StreamerDataAggregatorSavedState, AZ::SystemAllocator, 0);
|
|
StreamerDataAggregatorSavedState() {}
|
|
|
|
static void Reflect(AZ::ReflectContext* context)
|
|
{
|
|
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
|
|
if (serialize)
|
|
{
|
|
serialize->Class<StreamerDataAggregatorSavedState>()
|
|
->Version(1)
|
|
;
|
|
}
|
|
}
|
|
};
|
|
|
|
// WORKSPACES are files loaded and stored independent of the global application
|
|
// designed to be used for DRL data specific view settings and to pass around
|
|
class StreamerDataAggregatorWorkspace
|
|
: public AZ::UserSettings
|
|
{
|
|
public:
|
|
AZ_RTTI(StreamerDataAggregatorWorkspace, "{D35E8CCA-6FA7-47F6-8A24-8E12EF237E40}", AZ::UserSettings);
|
|
AZ_CLASS_ALLOCATOR(StreamerDataAggregatorWorkspace, AZ::SystemAllocator, 0);
|
|
|
|
int m_activeViewCount;
|
|
AZStd::vector<int> m_activeViewTypes;
|
|
|
|
StreamerDataAggregatorWorkspace()
|
|
: m_activeViewCount(0)
|
|
{}
|
|
|
|
static void Reflect(AZ::ReflectContext* context)
|
|
{
|
|
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
|
|
if (serialize)
|
|
{
|
|
serialize->Class<StreamerDataAggregatorWorkspace>()
|
|
->Field("m_activeViewCount", &StreamerDataAggregatorWorkspace::m_activeViewCount)
|
|
->Field("m_activeViewTypes", &StreamerDataAggregatorWorkspace::m_activeViewTypes)
|
|
->Version(1);
|
|
}
|
|
}
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
StreamerDataAggregator::StreamerDataAggregator(int identity)
|
|
: Aggregator(identity)
|
|
, m_activeViewCount(0)
|
|
, m_highwaterFrame(-1)
|
|
{
|
|
m_parser.SetAggregator(this);
|
|
ResetTrackingInfo();
|
|
}
|
|
|
|
StreamerDataAggregator::~StreamerDataAggregator()
|
|
{
|
|
KillAllViews();
|
|
}
|
|
|
|
// gross generalization of activity based on total number of all events this frame
|
|
float StreamerDataAggregator::ValueAtFrame(FrameNumberType frame)
|
|
{
|
|
const float maxEventsPerFrame = 10.0f; // just a scale number
|
|
float numEventsPerFrame = static_cast<float>(NumOfEventsAtFrame(frame));
|
|
return AZStd::GetMin(numEventsPerFrame / maxEventsPerFrame, 1.0f) * 2.0f - 1.0f;
|
|
}
|
|
|
|
QColor StreamerDataAggregator::GetColor() const
|
|
{
|
|
return QColor(0, 255, 255);
|
|
}
|
|
|
|
QString StreamerDataAggregator::GetName() const
|
|
{
|
|
return "Streamer";
|
|
}
|
|
|
|
QString StreamerDataAggregator::GetChannelName() const
|
|
{
|
|
return ChannelName();
|
|
}
|
|
|
|
QString StreamerDataAggregator::GetDescription() const
|
|
{
|
|
return "Streamer events driller";
|
|
}
|
|
|
|
QString StreamerDataAggregator::GetToolTip() const
|
|
{
|
|
return "Streamer Events";
|
|
}
|
|
|
|
AZ::Uuid StreamerDataAggregator::GetID() const
|
|
{
|
|
return AZ::Uuid("{9A2854C8-8106-4075-9287-3E047821D934}");
|
|
}
|
|
|
|
QWidget* StreamerDataAggregator::DrillDownRequest(FrameNumberType frame)
|
|
{
|
|
StreamerDrillerDialog* dialog = NULL;
|
|
|
|
AZ::u32 availableIdx = 0;
|
|
bool foundASpot = true;
|
|
do
|
|
{
|
|
foundASpot = true;
|
|
for (DataViewMap::iterator iter = m_dataViews.begin(); iter != m_dataViews.end(); ++iter)
|
|
{
|
|
if (iter->second == availableIdx)
|
|
{
|
|
foundASpot = false;
|
|
++availableIdx;
|
|
break;
|
|
}
|
|
}
|
|
} while (!foundASpot);
|
|
|
|
dialog = aznew StreamerDrillerDialog(this, frame, (1024 * GetIdentity()) + availableIdx);
|
|
if (dialog)
|
|
{
|
|
m_dataViews[dialog] = availableIdx;
|
|
connect(dialog, SIGNAL(destroyed(QObject*)), this, SLOT(OnDataViewDestroyed(QObject*)));
|
|
++m_activeViewCount;
|
|
}
|
|
|
|
return dialog;
|
|
}
|
|
|
|
QWidget* StreamerDataAggregator::DrillDownRequest(FrameNumberType frame, int type)
|
|
{
|
|
StreamerDrillerDialog* view = static_cast<StreamerDrillerDialog*>(DrillDownRequest(frame));
|
|
if (view)
|
|
{
|
|
view->SetChartType(type);
|
|
}
|
|
|
|
return view;
|
|
}
|
|
|
|
void StreamerDataAggregator::OptionsRequest()
|
|
{
|
|
QMenu* popupMenu = new QMenu();
|
|
QMenu* cachedHitsTypeMenu = new QMenu(tr("Cached Hits"));
|
|
cachedHitsTypeMenu->addAction(tr("Do Not Report Cache Hits"));
|
|
cachedHitsTypeMenu->addAction(tr("Report Cache Hits"));
|
|
popupMenu->addMenu(cachedHitsTypeMenu);
|
|
QAction* act = cachedHitsTypeMenu->exec(QCursor::pos());
|
|
if (act)
|
|
{
|
|
if (act->text() == tr("Report Cache Hits"))
|
|
{
|
|
m_parser.AllowCacheHitsInReportedStream(true);
|
|
}
|
|
if (act->text() == tr("Do Not Report Cache Hits"))
|
|
{
|
|
m_parser.AllowCacheHitsInReportedStream(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void StreamerDataAggregator::OnDataViewDestroyed(QObject* dataView)
|
|
{
|
|
m_dataViews.erase(dataView);
|
|
--m_activeViewCount;
|
|
}
|
|
|
|
void StreamerDataAggregator::KillAllViews()
|
|
{
|
|
do
|
|
{
|
|
DataViewMap::iterator iter = m_dataViews.begin();
|
|
if (iter != m_dataViews.end())
|
|
{
|
|
delete iter->first;
|
|
continue;
|
|
}
|
|
break;
|
|
} while (1);
|
|
}
|
|
|
|
void StreamerDataAggregator::ApplySettingsFromWorkspace(WorkspaceSettingsProvider* provider)
|
|
{
|
|
StreamerDataAggregatorWorkspace* workspace = provider->FindSetting<StreamerDataAggregatorWorkspace>(AZ_CRC("STREAMER DATA AGGREGATOR WORKSPACE", 0x105be192));
|
|
if (workspace)
|
|
{
|
|
m_activeViewCount = workspace->m_activeViewCount;
|
|
}
|
|
}
|
|
void StreamerDataAggregator::ActivateWorkspaceSettings(WorkspaceSettingsProvider* provider)
|
|
{
|
|
StreamerDataAggregatorWorkspace* workspace = provider->FindSetting<StreamerDataAggregatorWorkspace>(AZ_CRC("STREAMER DATA AGGREGATOR WORKSPACE", 0x105be192));
|
|
if (workspace)
|
|
{
|
|
// kill all existing data view windows in preparation of opening the workspace specified ones
|
|
KillAllViews();
|
|
|
|
// the internal count should be 0 from the above house cleaning
|
|
// and incremented back up from the workspace instantiations
|
|
m_activeViewCount = 0;
|
|
for (int i = 0; i < workspace->m_activeViewCount; ++i)
|
|
{
|
|
//// start this check to default
|
|
int discoveredType = 0;
|
|
if (workspace->m_activeViewTypes.size() > i)
|
|
{
|
|
discoveredType = workspace->m_activeViewTypes[i];
|
|
}
|
|
|
|
Driller::StreamerDrillerDialog* dataView = qobject_cast<Driller::StreamerDrillerDialog*>(DrillDownRequest(1, discoveredType));
|
|
if (dataView)
|
|
{
|
|
// apply will overlay the workspace settings on top of the local user settings
|
|
dataView->ApplySettingsFromWorkspace(provider);
|
|
// activate will do the heavy lifting
|
|
dataView->ActivateWorkspaceSettings(provider);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void StreamerDataAggregator::SaveSettingsToWorkspace(WorkspaceSettingsProvider* provider)
|
|
{
|
|
StreamerDataAggregatorWorkspace* workspace = provider->CreateSetting<StreamerDataAggregatorWorkspace>(AZ_CRC("STREAMER DATA AGGREGATOR WORKSPACE", 0x105be192));
|
|
if (workspace)
|
|
{
|
|
workspace->m_activeViewTypes.clear();
|
|
workspace->m_activeViewCount = m_activeViewCount;
|
|
|
|
for (DataViewMap::iterator iter = m_dataViews.begin(); iter != m_dataViews.end(); ++iter)
|
|
{
|
|
Driller::StreamerDrillerDialog* dataView = qobject_cast<Driller::StreamerDrillerDialog* >(iter->first);
|
|
if (dataView)
|
|
{
|
|
workspace->m_activeViewTypes.push_back(dataView->GetViewType());
|
|
dataView->SaveSettingsToWorkspace(provider);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//=========================================================================
|
|
// Reset
|
|
// [7/10/2013]
|
|
//=========================================================================
|
|
void StreamerDataAggregator::Reset()
|
|
{
|
|
m_devices.clear();
|
|
m_streams.clear();
|
|
m_requests.clear();
|
|
m_seeksInfo.clear();
|
|
ResetTrackingInfo();
|
|
}
|
|
|
|
void StreamerDataAggregator::ResetTrackingInfo()
|
|
{
|
|
m_seekTracking.clear();
|
|
m_highwaterFrame = -1;
|
|
|
|
// default device with ID := 0
|
|
SeekTrackingInfo seekTrackingInfo;
|
|
seekTrackingInfo.m_currentStreamId = 0;
|
|
seekTrackingInfo.m_offset = 0;
|
|
m_seekTracking.insert(AZStd::make_pair(0, seekTrackingInfo));
|
|
}
|
|
|
|
void StreamerDataAggregator::Reflect(AZ::ReflectContext* context)
|
|
{
|
|
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
|
|
if (serialize)
|
|
{
|
|
StreamerDataAggregatorSavedState::Reflect(context);
|
|
StreamerDataAggregatorWorkspace::Reflect(context);
|
|
StreamerDrillerDialog::Reflect(context);
|
|
|
|
serialize->Class<StreamerDataAggregator>()
|
|
->Version(1)
|
|
;
|
|
}
|
|
}
|
|
|
|
void StreamerDataAggregator::AdvanceToFrame(FrameNumberType frame)
|
|
{
|
|
while (m_highwaterFrame < frame)
|
|
{
|
|
++m_highwaterFrame;
|
|
FrameChanged(m_highwaterFrame);
|
|
|
|
m_frameInfo.push_back();
|
|
m_frameInfo.back().m_computedSeeksCount = 0;
|
|
m_frameInfo.back().m_computedThroughput = 0;
|
|
|
|
AZ::s64 numEvents = NumOfEventsAtFrame(m_highwaterFrame);
|
|
if (numEvents)
|
|
{
|
|
AZ::s64 firstIndex = GetFirstIndexAtFrame(m_highwaterFrame);
|
|
|
|
for (AZ::s64 idx = 0; idx < numEvents; ++idx)
|
|
{
|
|
DrillerEvent* dep = m_events[ idx + firstIndex ];
|
|
AZ::u64 geid = dep->GetGlobalEventId();
|
|
|
|
switch (dep->GetEventType())
|
|
{
|
|
case Driller::Streamer::SET_DEVICE_MOUNTED:
|
|
{
|
|
SeekTrackingInfo seekTrackingInfo;
|
|
seekTrackingInfo.m_currentStreamId = 0;
|
|
seekTrackingInfo.m_offset = 0;
|
|
m_seekTracking.insert(AZStd::make_pair(static_cast<StreamerMountDeviceEvent*>(dep)->m_deviceData.m_id, seekTrackingInfo));
|
|
break;
|
|
}
|
|
case Driller::Streamer::SET_DEVICE_UNMOUNTED:
|
|
{
|
|
m_seekTracking.erase(static_cast<StreamerUnmountDeviceEvent*>(dep)->m_unmountedDeviceData->m_id);
|
|
break;
|
|
}
|
|
case Driller::Streamer::SET_ADD_REQUEST:
|
|
{
|
|
break;
|
|
}
|
|
case Driller::Streamer::SET_CANCEL_REQUEST:
|
|
case Driller::Streamer::SET_RESCHEDULE_REQUEST:
|
|
case Driller::Streamer::SET_COMPLETE_REQUEST:
|
|
{
|
|
break;
|
|
}
|
|
case Driller::Streamer::SET_REGISTER_STREAM:
|
|
case Driller::Streamer::SET_UNREGISTER_STREAM:
|
|
{
|
|
break;
|
|
}
|
|
// m_stream := possibly NULL in these two cases
|
|
case Driller::Streamer::SET_OPERATION_START:
|
|
{
|
|
auto depEvt = static_cast<StreamerOperationStartEvent*>(dep);
|
|
if (depEvt->m_stream)
|
|
{
|
|
auto trackedDevice = m_seekTracking.find(depEvt->m_stream->m_deviceId);
|
|
if (trackedDevice == m_seekTracking.end())
|
|
{
|
|
SeekTrackingInfo seekTrackingInfo;
|
|
seekTrackingInfo.m_currentStreamId = 0;
|
|
seekTrackingInfo.m_offset = 0;
|
|
|
|
m_seekTracking.insert(AZStd::make_pair(depEvt->m_stream->m_deviceId, seekTrackingInfo));
|
|
trackedDevice = m_seekTracking.find(depEvt->m_stream->m_deviceId);
|
|
}
|
|
// reasons a device might SEEK
|
|
if (trackedDevice->second.m_currentStreamId != depEvt->m_streamId)
|
|
{
|
|
if (
|
|
(depEvt->m_stream->m_isCompressed && depEvt->m_operation.m_type == Streamer::SOP_COMPRESSOR_READ)
|
|
||
|
|
(!depEvt->m_stream->m_isCompressed)
|
|
)
|
|
{
|
|
m_frameInfo.back().m_seekInfo.push_back();
|
|
m_frameInfo.back().m_seekInfo.back().m_eventId = geid;
|
|
m_frameInfo.back().m_seekInfo.back().m_eventReason = SEEK_EVENT_SWITCH;
|
|
trackedDevice->second.m_currentStreamId = depEvt->m_streamId;
|
|
trackedDevice->second.m_offset = depEvt->m_operation.m_offset;
|
|
++m_frameInfo.back().m_computedSeeksCount;
|
|
m_seeksInfo.insert(AZStd::make_pair(geid, SEEK_EVENT_SWITCH));
|
|
}
|
|
}
|
|
else if (trackedDevice->second.m_offset != depEvt->m_operation.m_offset)
|
|
{
|
|
if (
|
|
(depEvt->m_stream->m_isCompressed && depEvt->m_operation.m_type == Streamer::SOP_COMPRESSOR_READ)
|
|
||
|
|
(!depEvt->m_stream->m_isCompressed)
|
|
)
|
|
{
|
|
m_frameInfo.back().m_seekInfo.push_back();
|
|
m_frameInfo.back().m_seekInfo.back().m_eventId = geid;
|
|
m_frameInfo.back().m_seekInfo.back().m_eventReason = SEEK_EVENT_SKIP;
|
|
trackedDevice->second.m_offset = depEvt->m_operation.m_offset;
|
|
++m_frameInfo.back().m_computedSeeksCount;
|
|
m_seeksInfo.insert(AZStd::make_pair(geid, SEEK_EVENT_SKIP));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case Driller::Streamer::SET_OPERATION_COMPLETE:
|
|
{
|
|
auto depEvt = azdynamic_cast<StreamerOperationCompleteEvent*>(dep);
|
|
|
|
if (depEvt->m_stream)
|
|
{
|
|
m_frameInfo.back().m_transferInfo.push_back();
|
|
m_frameInfo.back().m_transferInfo.back().m_eventId = geid;
|
|
m_frameInfo.back().m_transferInfo.back().m_eventReason = (TransferEventType)(depEvt->m_type);
|
|
|
|
auto trackedDevice = m_seekTracking.find(depEvt->m_stream->m_deviceId);
|
|
if (trackedDevice == m_seekTracking.end())
|
|
{
|
|
SeekTrackingInfo seekTrackingInfo;
|
|
seekTrackingInfo.m_currentStreamId = 0;
|
|
seekTrackingInfo.m_offset = 0;
|
|
|
|
m_seekTracking.insert(AZStd::make_pair(depEvt->m_stream->m_deviceId, seekTrackingInfo));
|
|
trackedDevice = m_seekTracking.find(depEvt->m_stream->m_deviceId);
|
|
}
|
|
|
|
if (depEvt->m_stream->m_isCompressed)
|
|
{
|
|
if (static_cast<TransferEventType>(depEvt->m_type) == TRANSFER_EVENT_COMPRESSOR_READ || static_cast<TransferEventType>(depEvt->m_type) == TRANSFER_EVENT_COMPRESSOR_WRITE)
|
|
{
|
|
m_frameInfo.back().m_transferInfo.back().m_byteCount = depEvt->m_bytesTransferred;
|
|
m_frameInfo.back().m_computedThroughput += depEvt->m_bytesTransferred;
|
|
trackedDevice->second.m_offset += depEvt->m_bytesTransferred;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_frameInfo.back().m_transferInfo.back().m_byteCount = depEvt->m_bytesTransferred;
|
|
m_frameInfo.back().m_computedThroughput += depEvt->m_bytesTransferred;
|
|
trackedDevice->second.m_offset += depEvt->m_bytesTransferred;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const char* StreamerDataAggregator::GetFilenameFromStreamId(unsigned int globalEventId, AZ::u64 streamId)
|
|
{
|
|
// starting at eventId, skip backwards through the events until a register stream is found
|
|
while (1)
|
|
{
|
|
if (globalEventId > m_events.size())
|
|
{
|
|
break;
|
|
}
|
|
auto event = m_events[globalEventId];
|
|
auto registerEvent = azdynamic_cast<StreamerRegisterStreamEvent*>(event);
|
|
if (registerEvent && registerEvent->m_streamData.m_id == streamId)
|
|
{
|
|
return registerEvent->m_streamData.m_name;
|
|
}
|
|
|
|
if (globalEventId == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
--globalEventId;
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
const char* StreamerDataAggregator::GetDebugNameFromStreamId(unsigned int globalEventId, AZ::u64 streamId)
|
|
{
|
|
// starting at eventId, skip backwards through the events until a request added (which has debug info) is found
|
|
while (1)
|
|
{
|
|
if (globalEventId > m_events.size())
|
|
{
|
|
break;
|
|
}
|
|
auto event = m_events[globalEventId];
|
|
auto requestEvent = azdynamic_cast<StreamerAddRequestEvent*>(event);
|
|
if (requestEvent && requestEvent->m_requestData.m_streamId == streamId)
|
|
{
|
|
if (requestEvent->m_requestData.m_debugName)
|
|
{
|
|
return requestEvent->m_requestData.m_debugName;
|
|
}
|
|
else
|
|
{
|
|
return "";
|
|
}
|
|
}
|
|
|
|
if (globalEventId == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
--globalEventId;
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
|
|
const AZ::u64 StreamerDataAggregator::GetOffsetFromStreamId(unsigned int globalEventId, AZ::u64 streamId)
|
|
{
|
|
// starting at eventId, skip backwards through the events until the start operation related to this ID is found
|
|
while (1)
|
|
{
|
|
if (globalEventId > m_events.size())
|
|
{
|
|
break;
|
|
}
|
|
auto event = m_events[globalEventId];
|
|
auto startEvent = azdynamic_cast<StreamerOperationStartEvent*>(event);
|
|
if (startEvent && startEvent->m_streamId == streamId)
|
|
{
|
|
return startEvent->m_operation.m_offset;
|
|
}
|
|
|
|
if (globalEventId == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
--globalEventId;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
float StreamerDataAggregator::ThroughputAtFrame(FrameNumberType frame)
|
|
{
|
|
if (frame >= 0)
|
|
{
|
|
AdvanceToFrame(frame);
|
|
return (float)m_frameInfo[frame].m_computedThroughput;
|
|
}
|
|
|
|
return 0.0f;
|
|
}
|
|
float StreamerDataAggregator::SeeksAtFrame(FrameNumberType frame)
|
|
{
|
|
if (frame >= 0)
|
|
{
|
|
AdvanceToFrame(frame);
|
|
return (float)m_frameInfo[frame].m_computedSeeksCount;
|
|
}
|
|
|
|
return 0.0f;
|
|
}
|
|
Driller::StreamerDataAggregator::TransferBreakoutType& StreamerDataAggregator::ThroughputAtFrameBreakout(FrameNumberType frame)
|
|
{
|
|
AdvanceToFrame(frame);
|
|
return m_frameInfo[frame].m_transferInfo;
|
|
}
|
|
Driller::StreamerDataAggregator::SeeksBreakoutType& StreamerDataAggregator::SeeksAtFrameBreakout(FrameNumberType frame)
|
|
{
|
|
AdvanceToFrame(frame);
|
|
return m_frameInfo[frame].m_seekInfo;
|
|
}
|
|
StreamerDataAggregator::SeekEventType StreamerDataAggregator::GetSeekType(AZ::s64 id)
|
|
{
|
|
auto iter = m_seeksInfo.find(id);
|
|
if (iter != m_seeksInfo.end())
|
|
{
|
|
return iter->second;
|
|
}
|
|
return SEEK_EVENT_NONE;
|
|
}
|
|
} // namespace Driller
|