<commit_before>#include "pch.h"
#include "AudioDeviceManager.h"

#include "DspMatrix.h"

namespace SaneAudioRenderer
{
    namespace
    {
        WAVEFORMATEX BuildWaveFormat(WORD formatTag, uint32_t formatBits, uint32_t rate, uint32_t channelCount)
        {
            WAVEFORMATEX format;

            format.wFormatTag      = formatTag;
            format.nChannels       = channelCount;
            format.nSamplesPerSec  = rate;
            format.nAvgBytesPerSec = formatBits / 8 * channelCount * rate;
            format.nBlockAlign     = formatBits / 8 * channelCount;
            format.wBitsPerSample  = formatBits;
            format.cbSize          = (formatTag == WAVE_FORMAT_EXTENSIBLE) ? 22 : 0;

            return format;
        }

        WAVEFORMATEXTENSIBLE BuildWaveFormatExt(GUID formatGuid, uint32_t formatBits, WORD formatExtProps,
                                                uint32_t rate, uint32_t channelCount, DWORD channelMask)
        {
            WAVEFORMATEXTENSIBLE format;

            format.Format        = BuildWaveFormat(WAVE_FORMAT_EXTENSIBLE, formatBits, rate, channelCount);
            format.Samples.wValidBitsPerSample = formatExtProps;
            format.dwChannelMask = channelMask;
            format.SubFormat     = formatGuid;

            return format;
        }

        template <typename T>
        void AppendPcmFormatPack(T& data, uint32_t rate, uint32_t channelCount, DWORD channelMask)
        {
            data.insert(data.cend(), {
                BuildWaveFormatExt(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 32, 32, rate, channelCount, channelMask),
                BuildWaveFormatExt(KSDATAFORMAT_SUBTYPE_PCM, 32, 32, rate, channelCount, channelMask),
                BuildWaveFormatExt(KSDATAFORMAT_SUBTYPE_PCM, 24, 24, rate, channelCount, channelMask),
                BuildWaveFormatExt(KSDATAFORMAT_SUBTYPE_PCM, 32, 24, rate, channelCount, channelMask),
                BuildWaveFormatExt(KSDATAFORMAT_SUBTYPE_PCM, 16, 16, rate, channelCount, channelMask),
            });
        }

        UINT32 GetDevicePropertyUint(IPropertyStore* pStore, REFPROPERTYKEY key)
        {
            assert(pStore);

            PROPVARIANT prop;
            PropVariantInit(&prop);
            ThrowIfFailed(pStore->GetValue(key, &prop));
            assert(prop.vt == VT_UI4);
            UINT32 ret = prop.uintVal;
            PropVariantClear(&prop);

            return ret;
        }

        SharedString GetDevicePropertyString(IPropertyStore* pStore, REFPROPERTYKEY key)
        {
            assert(pStore);

            PROPVARIANT prop;
            PropVariantInit(&prop);
            ThrowIfFailed(pStore->GetValue(key, &prop));
            assert(prop.vt == VT_LPWSTR);
            auto ret = std::make_shared<std::wstring>(prop.pwszVal);
            PropVariantClear(&prop);

            return ret;
        }

        DWORD ShiftBackSide(DWORD mask)
        {
            return mask ^ (SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT |
                           SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT);
        }

        void CreateAudioClient(IMMDeviceEnumerator* pEnumerator, AudioDeviceBackend& backend)
        {
            assert(pEnumerator);

            IMMDevicePtr device;

            if (!backend.id || backend.id->empty())
            {
                ThrowIfFailed(pEnumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &device));

                LPWSTR pDeviceId = nullptr;
                ThrowIfFailed(device->GetId(&pDeviceId));
                std::unique_ptr<WCHAR, CoTaskMemFreeDeleter> holder(pDeviceId);
                backend.id = std::make_shared<std::wstring>(pDeviceId);
            }
            else
            {
                IMMDeviceCollectionPtr collection;
                ThrowIfFailed(pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &collection));

                UINT count = 0;
                ThrowIfFailed(collection->GetCount(&count));

                for (UINT i = 0; i < count; i++)
                {
                    ThrowIfFailed(collection->Item(i, &device));

                    LPWSTR pDeviceId = nullptr;
                    ThrowIfFailed(device->GetId(&pDeviceId));
                    std::unique_ptr<WCHAR, CoTaskMemFreeDeleter> holder(pDeviceId);

                    if (*backend.id == pDeviceId)
                        break;

                    device = nullptr;
                }
            }

            if (!device)
                return;

            IPropertyStorePtr devicePropertyStore;
            ThrowIfFailed(device->OpenPropertyStore(STGM_READ, &devicePropertyStore));

            backend.adapterName  = GetDevicePropertyString(devicePropertyStore, PKEY_DeviceInterface_FriendlyName);
            backend.endpointName = GetDevicePropertyString(devicePropertyStore, PKEY_Device_DeviceDesc);

            static const PROPERTYKEY formFactorKey = { // PKEY_AudioEndpoint_FormFactor
                {0x1da5d803, 0xd492, 0x4edd, {0x8c, 0x23, 0xe0, 0xc0, 0xff, 0xee, 0x7f, 0x0e}}, 0
            };
            backend.endpointFormFactor = GetDevicePropertyUint(devicePropertyStore, formFactorKey);

            ThrowIfFailed(device->Activate(__uuidof(IAudioClient),
                                           CLSCTX_INPROC_SERVER, nullptr, (void**)&backend.audioClient));
        }

        HRESULT CheckBitstreamFormat(IMMDeviceEnumerator* pEnumerator, SharedWaveFormat format, ISettings* pSettings)
        {
            assert(pEnumerator);
            assert(format);
            assert(pSettings);

            try
            {
                AudioDeviceBackend device = {};

                {
                    LPWSTR pDeviceId = nullptr;
                    ThrowIfFailed(pSettings->GetOuputDevice(&pDeviceId, nullptr, nullptr));
                    std::unique_ptr<WCHAR, CoTaskMemFreeDeleter> holder(pDeviceId);

                    device.id = std::make_shared<std::wstring>(pDeviceId);
                }

                CreateAudioClient(pEnumerator, device);

                if (!device.audioClient)
                    return E_FAIL;

                return device.audioClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &(*format), nullptr);
            }
            catch (HRESULT ex)
            {
                return ex;
            }
        }

        HRESULT CreateAudioDeviceBackend(IMMDeviceEnumerator* pEnumerator,
                                         SharedWaveFormat format, bool realtime, ISettings* pSettings,
                                         std::shared_ptr<AudioDeviceBackend>& backend)
        {
            assert(pEnumerator);
            assert(format);
            assert(pSettings);

            try
            {
                backend = std::make_shared<AudioDeviceBackend>();

                {
                    LPWSTR pDeviceId = nullptr;
                    BOOL exclusive;
                    UINT32 buffer;
                    ThrowIfFailed(pSettings->GetOuputDevice(&pDeviceId, &exclusive, &buffer));
                    std::unique_ptr<WCHAR, CoTaskMemFreeDeleter> holder(pDeviceId);

                    backend->id = std::make_shared<std::wstring>(pDeviceId);
                    backend->exclusive = !!exclusive;
                    backend->realtime = realtime;
                    backend->bufferDuration = buffer;
                }

                CreateAudioClient(pEnumerator, *backend);

                if (!backend->audioClient)
                    return E_FAIL;

                WAVEFORMATEX* pFormat;
                ThrowIfFailed(backend->audioClient->GetMixFormat(&pFormat));
                SharedWaveFormat mixFormat(pFormat, CoTaskMemFreeDeleter());

                backend->bitstream = (DspFormatFromWaveFormat(*format) == DspFormat::Unknown);

                const auto inputRate = format->nSamplesPerSec;
                const auto inputChannels = format->nChannels;
                const auto inputMask = DspMatrix::GetChannelMask(*format);
                const auto mixRate = mixFormat->nSamplesPerSec;
                const auto mixChannels = mixFormat->nChannels;
                const auto mixMask = DspMatrix::GetChannelMask(*mixFormat);

                if (backend->bitstream)
                {
                    // Exclusive bitstreaming.
                    if (!backend->exclusive)
                        return E_FAIL;

                    backend->dspFormat = DspFormat::Unknown;
                    backend->waveFormat = format;
                }
                else if (backend->exclusive)
                {
                    // Exclusive.
                    std::vector<WAVEFORMATEXTENSIBLE> priorities;

                    if (backend->endpointFormFactor == DigitalAudioDisplayDevice)
                    {
                        AppendPcmFormatPack(priorities, inputRate, inputChannels, inputMask);
                        AppendPcmFormatPack(priorities, mixRate, inputChannels, inputMask);

                        // Shift between 5.1 with side channels and 5.1 with back channels.
                        if (inputMask == KSAUDIO_SPEAKER_5POINT1 ||
                            inputMask == ShiftBackSide(KSAUDIO_SPEAKER_5POINT1))
                        {
                            auto altMask = ShiftBackSide(inputMask);
                            AppendPcmFormatPack(priorities, inputRate, inputChannels, altMask);
                            AppendPcmFormatPack(priorities, mixRate, inputChannels, altMask);
                        }
                    }

                    AppendPcmFormatPack(priorities, inputRate, mixChannels, mixMask);
                    AppendPcmFormatPack(priorities, mixRate, mixChannels, mixMask);

                    priorities.insert(priorities.cend(), {
                        WAVEFORMATEXTENSIBLE{BuildWaveFormat(WAVE_FORMAT_PCM, 16, inputRate, mixChannels)},
                        WAVEFORMATEXTENSIBLE{BuildWaveFormat(WAVE_FORMAT_PCM, 16, mixRate, mixChannels)}
                    });

                    for (const auto& f : priorities)
                    {
                        assert(DspFormatFromWaveFormat(f.Format) != DspFormat::Unknown);

                        if (SUCCEEDED(backend->audioClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE,
                                                                              &f.Format, nullptr)))
                        {
                            backend->dspFormat = DspFormatFromWaveFormat(f.Format);
                            backend->waveFormat = CopyWaveFormat(f.Format);
                            break;
                        }
                    }
                }
                else
                {
                    // Shared.
                    backend->dspFormat = DspFormat::Float;
                    backend->waveFormat = mixFormat;

                    std::vector<WAVEFORMATEXTENSIBLE> priorities = {
                        BuildWaveFormatExt(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 32, 32,
                                           mixRate, inputChannels, inputMask),
                    };

                    // Shift between 5.1 with side channels and 5.1 with back channels.
                    if (inputMask == KSAUDIO_SPEAKER_5POINT1 ||
                        inputMask == ShiftBackSide(KSAUDIO_SPEAKER_5POINT1))
                    {
                        priorities.push_back(BuildWaveFormatExt(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 32, 32,
                                                                mixRate, inputChannels, ShiftBackSide(inputMask)));
                    }

                    for (const auto& f : priorities)
                    {
                        assert(DspFormatFromWaveFormat(f.Format) != DspFormat::Unknown);

                        WAVEFORMATEX* pClosest;
                        if (SUCCEEDED(backend->audioClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,
                                                                              &f.Format, &pClosest)))
                        {
                            if (pClosest)
                            {
                                CoTaskMemFree(pClosest);
                            }
                            else
                            {
                                backend->dspFormat = DspFormatFromWaveFormat(f.Format);
                                backend->waveFormat = CopyWaveFormat(f.Format);
                                break;
                            }
                        }
                    }
                }

                ThrowIfFailed(backend->audioClient->Initialize(
                                         backend->exclusive ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED,
                                         AUDCLNT_STREAMFLAGS_NOPERSIST,
                                         OneMillisecond * backend->bufferDuration,
                                         0, &(*backend->waveFormat), nullptr));

                ThrowIfFailed(backend->audioClient->GetService(IID_PPV_ARGS(&backend->audioRenderClient)));

                ThrowIfFailed(backend->audioClient->GetService(IID_PPV_ARGS(&backend->audioClock)));

                ThrowIfFailed(backend->audioClient->GetStreamLatency(&backend->latency));

                return S_OK;
            }
            catch (std::bad_alloc&)
            {
                backend = nullptr;
                return E_OUTOFMEMORY;
            }
            catch (HRESULT ex)
            {
                backend = nullptr;
                return ex;
            }
        }
    }

    AudioDeviceNotificationClient::AudioDeviceNotificationClient(std::atomic<uint32_t>& defaultDeviceSerial)
        : CUnknown("SaneAudioRenderer::AudioDeviceNotificationClient", nullptr)
        , m_defaultDeviceSerial(defaultDeviceSerial)
    {
    }

    STDMETHODIMP AudioDeviceNotificationClient::NonDelegatingQueryInterface(REFIID riid, void** ppv)
    {
        if (riid == __uuidof(IMMNotificationClient))
            return GetInterface(static_cast<IMMNotificationClient*>(this), ppv);

        return CUnknown::NonDelegatingQueryInterface(riid, ppv);
    }

    STDMETHODIMP AudioDeviceNotificationClient::OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR)
    {
        if (flow == eRender && role == eMultimedia)
            m_defaultDeviceSerial++;

        return S_OK;
    }

    AudioDeviceManager::AudioDeviceManager(HRESULT& result)
    {
        if (FAILED(result))
            return;

        try
        {
            if (static_cast<HANDLE>(m_wake) == NULL ||
                static_cast<HANDLE>(m_done) == NULL)
            {
                throw E_OUTOFMEMORY;
            }

            m_thread = std::thread(
                [this]
                {
                    CoInitializeHelper coInitializeHelper(COINIT_MULTITHREADED);

                    while (!m_exit)
                    {
                        m_wake.Wait();

                        if (m_function)
                        {
                            m_result = m_function();
                            m_function = nullptr;
                            m_done.Set();
                        }
                    }
                }
            );

            {
                m_function = [&] { return CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr,
                                                           CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_enumerator)); };
                m_wake.Set();
                m_done.Wait();
                ThrowIfFailed(m_result);
                assert(m_enumerator);
            }

            {
                auto pNotificationClient = new AudioDeviceNotificationClient(m_defaultDeviceSerial);

                pNotificationClient->NonDelegatingAddRef();

                ThrowIfFailed(pNotificationClient->NonDelegatingQueryInterface(IID_PPV_ARGS(&m_notificationClient)));

                pNotificationClient->NonDelegatingRelease();

                ThrowIfFailed(m_enumerator->RegisterEndpointNotificationCallback(m_notificationClient));
            }
        }
        catch (HRESULT ex)
        {
            result = ex;
        }
        catch (std::system_error&)
        {
            result = E_FAIL;
        }
    }

    AudioDeviceManager::~AudioDeviceManager()
    {
        if (m_enumerator && m_notificationClient)
            m_enumerator->UnregisterEndpointNotificationCallback(m_notificationClient);

        m_exit = true;
        m_wake.Set();

        if (m_thread.joinable())
            m_thread.join();
    }

    bool AudioDeviceManager::BitstreamFormatSupported(SharedWaveFormat format, ISettings* pSettings)
    {
        assert(format);
        assert(pSettings);

        m_function = [&] { return CheckBitstreamFormat(m_enumerator, format, pSettings); };
        m_wake.Set();
        m_done.Wait();

        return SUCCEEDED(m_result);
    }

    std::unique_ptr<AudioDevice> AudioDeviceManager::CreateDevice(SharedWaveFormat format, bool realtime,
                                                                  ISettings* pSettings)
    {
        assert(format);
        assert(pSettings);

        std::shared_ptr<AudioDeviceBackend> backend;

        m_function = [&] { return CreateAudioDeviceBackend(m_enumerator, format, realtime, pSettings, backend); };
        m_wake.Set();
        m_done.Wait();

        if (FAILED(m_result))
            return nullptr;

        try
        {
            return std::unique_ptr<AudioDevice>(new AudioDevice(backend));
        }
        catch (std::bad_alloc&)
        {
            return nullptr;
        }
        catch (std::system_error&)
        {
            return nullptr;
        }
    }

    std::unique_ptr<WCHAR, CoTaskMemFreeDeleter> AudioDeviceManager::GetDefaultDeviceId()
    {
        assert(m_enumerator);

        try
        {
            IMMDevicePtr device;
            ThrowIfFailed(m_enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &device));

            LPWSTR pDeviceId = nullptr;
            ThrowIfFailed(device->GetId(&pDeviceId));
            return std::unique_ptr<WCHAR, CoTaskMemFreeDeleter>(pDeviceId);
        }
        catch (HRESULT)
        {
            return nullptr;
        }
    }
}
<commit_msg>Release all interfaces before releasing MTA<commit_after>#include "pch.h"
#include "AudioDeviceManager.h"

#include "DspMatrix.h"

namespace SaneAudioRenderer
{
    namespace
    {
        WAVEFORMATEX BuildWaveFormat(WORD formatTag, uint32_t formatBits, uint32_t rate, uint32_t channelCount)
        {
            WAVEFORMATEX format;

            format.wFormatTag      = formatTag;
            format.nChannels       = channelCount;
            format.nSamplesPerSec  = rate;
            format.nAvgBytesPerSec = formatBits / 8 * channelCount * rate;
            format.nBlockAlign     = formatBits / 8 * channelCount;
            format.wBitsPerSample  = formatBits;
            format.cbSize          = (formatTag == WAVE_FORMAT_EXTENSIBLE) ? 22 : 0;

            return format;
        }

        WAVEFORMATEXTENSIBLE BuildWaveFormatExt(GUID formatGuid, uint32_t formatBits, WORD formatExtProps,
                                                uint32_t rate, uint32_t channelCount, DWORD channelMask)
        {
            WAVEFORMATEXTENSIBLE format;

            format.Format        = BuildWaveFormat(WAVE_FORMAT_EXTENSIBLE, formatBits, rate, channelCount);
            format.Samples.wValidBitsPerSample = formatExtProps;
            format.dwChannelMask = channelMask;
            format.SubFormat     = formatGuid;

            return format;
        }

        template <typename T>
        void AppendPcmFormatPack(T& data, uint32_t rate, uint32_t channelCount, DWORD channelMask)
        {
            data.insert(data.cend(), {
                BuildWaveFormatExt(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 32, 32, rate, channelCount, channelMask),
                BuildWaveFormatExt(KSDATAFORMAT_SUBTYPE_PCM, 32, 32, rate, channelCount, channelMask),
                BuildWaveFormatExt(KSDATAFORMAT_SUBTYPE_PCM, 24, 24, rate, channelCount, channelMask),
                BuildWaveFormatExt(KSDATAFORMAT_SUBTYPE_PCM, 32, 24, rate, channelCount, channelMask),
                BuildWaveFormatExt(KSDATAFORMAT_SUBTYPE_PCM, 16, 16, rate, channelCount, channelMask),
            });
        }

        UINT32 GetDevicePropertyUint(IPropertyStore* pStore, REFPROPERTYKEY key)
        {
            assert(pStore);

            PROPVARIANT prop;
            PropVariantInit(&prop);
            ThrowIfFailed(pStore->GetValue(key, &prop));
            assert(prop.vt == VT_UI4);
            UINT32 ret = prop.uintVal;
            PropVariantClear(&prop);

            return ret;
        }

        SharedString GetDevicePropertyString(IPropertyStore* pStore, REFPROPERTYKEY key)
        {
            assert(pStore);

            PROPVARIANT prop;
            PropVariantInit(&prop);
            ThrowIfFailed(pStore->GetValue(key, &prop));
            assert(prop.vt == VT_LPWSTR);
            auto ret = std::make_shared<std::wstring>(prop.pwszVal);
            PropVariantClear(&prop);

            return ret;
        }

        DWORD ShiftBackSide(DWORD mask)
        {
            return mask ^ (SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT |
                           SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT);
        }

        void CreateAudioClient(IMMDeviceEnumerator* pEnumerator, AudioDeviceBackend& backend)
        {
            assert(pEnumerator);

            IMMDevicePtr device;

            if (!backend.id || backend.id->empty())
            {
                ThrowIfFailed(pEnumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &device));

                LPWSTR pDeviceId = nullptr;
                ThrowIfFailed(device->GetId(&pDeviceId));
                std::unique_ptr<WCHAR, CoTaskMemFreeDeleter> holder(pDeviceId);
                backend.id = std::make_shared<std::wstring>(pDeviceId);
            }
            else
            {
                IMMDeviceCollectionPtr collection;
                ThrowIfFailed(pEnumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &collection));

                UINT count = 0;
                ThrowIfFailed(collection->GetCount(&count));

                for (UINT i = 0; i < count; i++)
                {
                    ThrowIfFailed(collection->Item(i, &device));

                    LPWSTR pDeviceId = nullptr;
                    ThrowIfFailed(device->GetId(&pDeviceId));
                    std::unique_ptr<WCHAR, CoTaskMemFreeDeleter> holder(pDeviceId);

                    if (*backend.id == pDeviceId)
                        break;

                    device = nullptr;
                }
            }

            if (!device)
                return;

            IPropertyStorePtr devicePropertyStore;
            ThrowIfFailed(device->OpenPropertyStore(STGM_READ, &devicePropertyStore));

            backend.adapterName  = GetDevicePropertyString(devicePropertyStore, PKEY_DeviceInterface_FriendlyName);
            backend.endpointName = GetDevicePropertyString(devicePropertyStore, PKEY_Device_DeviceDesc);

            static const PROPERTYKEY formFactorKey = { // PKEY_AudioEndpoint_FormFactor
                {0x1da5d803, 0xd492, 0x4edd, {0x8c, 0x23, 0xe0, 0xc0, 0xff, 0xee, 0x7f, 0x0e}}, 0
            };
            backend.endpointFormFactor = GetDevicePropertyUint(devicePropertyStore, formFactorKey);

            ThrowIfFailed(device->Activate(__uuidof(IAudioClient),
                                           CLSCTX_INPROC_SERVER, nullptr, (void**)&backend.audioClient));
        }

        HRESULT CheckBitstreamFormat(IMMDeviceEnumerator* pEnumerator, SharedWaveFormat format, ISettings* pSettings)
        {
            assert(pEnumerator);
            assert(format);
            assert(pSettings);

            try
            {
                AudioDeviceBackend device = {};

                {
                    LPWSTR pDeviceId = nullptr;
                    ThrowIfFailed(pSettings->GetOuputDevice(&pDeviceId, nullptr, nullptr));
                    std::unique_ptr<WCHAR, CoTaskMemFreeDeleter> holder(pDeviceId);

                    device.id = std::make_shared<std::wstring>(pDeviceId);
                }

                CreateAudioClient(pEnumerator, device);

                if (!device.audioClient)
                    return E_FAIL;

                return device.audioClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE, &(*format), nullptr);
            }
            catch (HRESULT ex)
            {
                return ex;
            }
        }

        HRESULT CreateAudioDeviceBackend(IMMDeviceEnumerator* pEnumerator,
                                         SharedWaveFormat format, bool realtime, ISettings* pSettings,
                                         std::shared_ptr<AudioDeviceBackend>& backend)
        {
            assert(pEnumerator);
            assert(format);
            assert(pSettings);

            try
            {
                backend = std::make_shared<AudioDeviceBackend>();

                {
                    LPWSTR pDeviceId = nullptr;
                    BOOL exclusive;
                    UINT32 buffer;
                    ThrowIfFailed(pSettings->GetOuputDevice(&pDeviceId, &exclusive, &buffer));
                    std::unique_ptr<WCHAR, CoTaskMemFreeDeleter> holder(pDeviceId);

                    backend->id = std::make_shared<std::wstring>(pDeviceId);
                    backend->exclusive = !!exclusive;
                    backend->realtime = realtime;
                    backend->bufferDuration = buffer;
                }

                CreateAudioClient(pEnumerator, *backend);

                if (!backend->audioClient)
                    return E_FAIL;

                WAVEFORMATEX* pFormat;
                ThrowIfFailed(backend->audioClient->GetMixFormat(&pFormat));
                SharedWaveFormat mixFormat(pFormat, CoTaskMemFreeDeleter());

                backend->bitstream = (DspFormatFromWaveFormat(*format) == DspFormat::Unknown);

                const auto inputRate = format->nSamplesPerSec;
                const auto inputChannels = format->nChannels;
                const auto inputMask = DspMatrix::GetChannelMask(*format);
                const auto mixRate = mixFormat->nSamplesPerSec;
                const auto mixChannels = mixFormat->nChannels;
                const auto mixMask = DspMatrix::GetChannelMask(*mixFormat);

                if (backend->bitstream)
                {
                    // Exclusive bitstreaming.
                    if (!backend->exclusive)
                        return E_FAIL;

                    backend->dspFormat = DspFormat::Unknown;
                    backend->waveFormat = format;
                }
                else if (backend->exclusive)
                {
                    // Exclusive.
                    std::vector<WAVEFORMATEXTENSIBLE> priorities;

                    if (backend->endpointFormFactor == DigitalAudioDisplayDevice)
                    {
                        AppendPcmFormatPack(priorities, inputRate, inputChannels, inputMask);
                        AppendPcmFormatPack(priorities, mixRate, inputChannels, inputMask);

                        // Shift between 5.1 with side channels and 5.1 with back channels.
                        if (inputMask == KSAUDIO_SPEAKER_5POINT1 ||
                            inputMask == ShiftBackSide(KSAUDIO_SPEAKER_5POINT1))
                        {
                            auto altMask = ShiftBackSide(inputMask);
                            AppendPcmFormatPack(priorities, inputRate, inputChannels, altMask);
                            AppendPcmFormatPack(priorities, mixRate, inputChannels, altMask);
                        }
                    }

                    AppendPcmFormatPack(priorities, inputRate, mixChannels, mixMask);
                    AppendPcmFormatPack(priorities, mixRate, mixChannels, mixMask);

                    priorities.insert(priorities.cend(), {
                        WAVEFORMATEXTENSIBLE{BuildWaveFormat(WAVE_FORMAT_PCM, 16, inputRate, mixChannels)},
                        WAVEFORMATEXTENSIBLE{BuildWaveFormat(WAVE_FORMAT_PCM, 16, mixRate, mixChannels)}
                    });

                    for (const auto& f : priorities)
                    {
                        assert(DspFormatFromWaveFormat(f.Format) != DspFormat::Unknown);

                        if (SUCCEEDED(backend->audioClient->IsFormatSupported(AUDCLNT_SHAREMODE_EXCLUSIVE,
                                                                              &f.Format, nullptr)))
                        {
                            backend->dspFormat = DspFormatFromWaveFormat(f.Format);
                            backend->waveFormat = CopyWaveFormat(f.Format);
                            break;
                        }
                    }
                }
                else
                {
                    // Shared.
                    backend->dspFormat = DspFormat::Float;
                    backend->waveFormat = mixFormat;

                    std::vector<WAVEFORMATEXTENSIBLE> priorities = {
                        BuildWaveFormatExt(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 32, 32,
                                           mixRate, inputChannels, inputMask),
                    };

                    // Shift between 5.1 with side channels and 5.1 with back channels.
                    if (inputMask == KSAUDIO_SPEAKER_5POINT1 ||
                        inputMask == ShiftBackSide(KSAUDIO_SPEAKER_5POINT1))
                    {
                        priorities.push_back(BuildWaveFormatExt(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 32, 32,
                                                                mixRate, inputChannels, ShiftBackSide(inputMask)));
                    }

                    for (const auto& f : priorities)
                    {
                        assert(DspFormatFromWaveFormat(f.Format) != DspFormat::Unknown);

                        WAVEFORMATEX* pClosest;
                        if (SUCCEEDED(backend->audioClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,
                                                                              &f.Format, &pClosest)))
                        {
                            if (pClosest)
                            {
                                CoTaskMemFree(pClosest);
                            }
                            else
                            {
                                backend->dspFormat = DspFormatFromWaveFormat(f.Format);
                                backend->waveFormat = CopyWaveFormat(f.Format);
                                break;
                            }
                        }
                    }
                }

                ThrowIfFailed(backend->audioClient->Initialize(
                                         backend->exclusive ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED,
                                         AUDCLNT_STREAMFLAGS_NOPERSIST,
                                         OneMillisecond * backend->bufferDuration,
                                         0, &(*backend->waveFormat), nullptr));

                ThrowIfFailed(backend->audioClient->GetService(IID_PPV_ARGS(&backend->audioRenderClient)));

                ThrowIfFailed(backend->audioClient->GetService(IID_PPV_ARGS(&backend->audioClock)));

                ThrowIfFailed(backend->audioClient->GetStreamLatency(&backend->latency));

                return S_OK;
            }
            catch (std::bad_alloc&)
            {
                backend = nullptr;
                return E_OUTOFMEMORY;
            }
            catch (HRESULT ex)
            {
                backend = nullptr;
                return ex;
            }
        }
    }

    AudioDeviceNotificationClient::AudioDeviceNotificationClient(std::atomic<uint32_t>& defaultDeviceSerial)
        : CUnknown("SaneAudioRenderer::AudioDeviceNotificationClient", nullptr)
        , m_defaultDeviceSerial(defaultDeviceSerial)
    {
    }

    STDMETHODIMP AudioDeviceNotificationClient::NonDelegatingQueryInterface(REFIID riid, void** ppv)
    {
        if (riid == __uuidof(IMMNotificationClient))
            return GetInterface(static_cast<IMMNotificationClient*>(this), ppv);

        return CUnknown::NonDelegatingQueryInterface(riid, ppv);
    }

    STDMETHODIMP AudioDeviceNotificationClient::OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR)
    {
        if (flow == eRender && role == eMultimedia)
            m_defaultDeviceSerial++;

        return S_OK;
    }

    AudioDeviceManager::AudioDeviceManager(HRESULT& result)
    {
        if (FAILED(result))
            return;

        try
        {
            if (static_cast<HANDLE>(m_wake) == NULL ||
                static_cast<HANDLE>(m_done) == NULL)
            {
                throw E_OUTOFMEMORY;
            }

            m_thread = std::thread(
                [this]
                {
                    CoInitializeHelper coInitializeHelper(COINIT_MULTITHREADED);

                    while (!m_exit)
                    {
                        m_wake.Wait();

                        if (m_function)
                        {
                            m_result = m_function();
                            m_function = nullptr;
                            m_done.Set();
                        }
                    }
                }
            );

            {
                m_function = [&] { return CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr,
                                                           CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_enumerator)); };
                m_wake.Set();
                m_done.Wait();
                ThrowIfFailed(m_result);
                assert(m_enumerator);
            }

            {
                auto pNotificationClient = new AudioDeviceNotificationClient(m_defaultDeviceSerial);

                pNotificationClient->NonDelegatingAddRef();

                ThrowIfFailed(pNotificationClient->NonDelegatingQueryInterface(IID_PPV_ARGS(&m_notificationClient)));

                pNotificationClient->NonDelegatingRelease();

                ThrowIfFailed(m_enumerator->RegisterEndpointNotificationCallback(m_notificationClient));
            }
        }
        catch (HRESULT ex)
        {
            result = ex;
        }
        catch (std::system_error&)
        {
            result = E_FAIL;
        }
    }

    AudioDeviceManager::~AudioDeviceManager()
    {
        if (m_enumerator && m_notificationClient)
            m_enumerator->UnregisterEndpointNotificationCallback(m_notificationClient);

        m_enumerator = nullptr;

        m_exit = true;
        m_wake.Set();

        if (m_thread.joinable())
            m_thread.join();
    }

    bool AudioDeviceManager::BitstreamFormatSupported(SharedWaveFormat format, ISettings* pSettings)
    {
        assert(format);
        assert(pSettings);

        m_function = [&] { return CheckBitstreamFormat(m_enumerator, format, pSettings); };
        m_wake.Set();
        m_done.Wait();

        return SUCCEEDED(m_result);
    }

    std::unique_ptr<AudioDevice> AudioDeviceManager::CreateDevice(SharedWaveFormat format, bool realtime,
                                                                  ISettings* pSettings)
    {
        assert(format);
        assert(pSettings);

        std::shared_ptr<AudioDeviceBackend> backend;

        m_function = [&] { return CreateAudioDeviceBackend(m_enumerator, format, realtime, pSettings, backend); };
        m_wake.Set();
        m_done.Wait();

        if (FAILED(m_result))
            return nullptr;

        try
        {
            return std::unique_ptr<AudioDevice>(new AudioDevice(backend));
        }
        catch (std::bad_alloc&)
        {
            return nullptr;
        }
        catch (std::system_error&)
        {
            return nullptr;
        }
    }

    std::unique_ptr<WCHAR, CoTaskMemFreeDeleter> AudioDeviceManager::GetDefaultDeviceId()
    {
        assert(m_enumerator);

        try
        {
            IMMDevicePtr device;
            ThrowIfFailed(m_enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &device));

            LPWSTR pDeviceId = nullptr;
            ThrowIfFailed(device->GetId(&pDeviceId));
            return std::unique_ptr<WCHAR, CoTaskMemFreeDeleter>(pDeviceId);
        }
        catch (HRESULT)
        {
            return nullptr;
        }
    }
}
<|endoftext|>


<commit_before>#include "inverseIndexStorageBloomierFilter.h"

InverseIndexStorageBloomierFilter::InverseIndexStorageBloomierFilter(size_t pSizeOfInverseIndex, size_t pMaxBinSize) {
	mInverseIndex = new std::vector<BloomierFilter* > (pSizeOfInverseIndex);
	mMaxBinSize = pMaxBinSize;
	mM = 5000;
	mK = 5;
	mQ = 8;
}
InverseIndexStorageBloomierFilter::~InverseIndexStorageBloomierFilter() {

}
size_t InverseIndexStorageBloomierFilter::size() {
	return mInverseIndex->size();
}
vsize_t* InverseIndexStorageBloomierFilter::getElement(size_t pVectorId, size_t pHashValue) {
    return (*mInverseIndex)[pVectorId]->get(pHashValue);
}
void InverseIndexStorageBloomierFilter::insert(size_t pVectorId, size_t pHashValue, size_t pInstance) {
    if ((*mInverseIndex)[pVectorId] == NULL) {
        (*mInverseIndex)[pVectorId] = new BloomierFilter(mM, mK, mQ, 100);
    }
    (*mInverseIndex)[pVectorId]->set(pHashValue, pInstance);
}<commit_msg>Added maxBinSize to bloomieFilter constructor<commit_after>#include "inverseIndexStorageBloomierFilter.h"

InverseIndexStorageBloomierFilter::InverseIndexStorageBloomierFilter(size_t pSizeOfInverseIndex, size_t pMaxBinSize) {
	mInverseIndex = new std::vector<BloomierFilter* > (pSizeOfInverseIndex);
	mMaxBinSize = pMaxBinSize;
	mM = 10000;
	mK = 5;
	mQ = 8;
}
InverseIndexStorageBloomierFilter::~InverseIndexStorageBloomierFilter() {

}
size_t InverseIndexStorageBloomierFilter::size() {
	return mInverseIndex->size();
}
vsize_t* InverseIndexStorageBloomierFilter::getElement(size_t pVectorId, size_t pHashValue) {
    return (*mInverseIndex)[pVectorId]->get(pHashValue);
}
void InverseIndexStorageBloomierFilter::insert(size_t pVectorId, size_t pHashValue, size_t pInstance) {
    if ((*mInverseIndex)[pVectorId] == NULL) {
        (*mInverseIndex)[pVectorId] = new BloomierFilter(mM, mK, mQ, 100, mMaxBinSize);
    }
    (*mInverseIndex)[pVectorId]->set(pHashValue, pInstance);
}<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include "AutoCompleteServlet.h"
#include "CTRCounter.h"
#include "fnord-base/Language.h"
#include "fnord-base/wallclock.h"
#include "fnord-base/io/fileutil.h"

using namespace fnord;

namespace cm {

AutoCompleteServlet::AutoCompleteServlet(
    RefPtr<fts::Analyzer> analyzer) :
    analyzer_(analyzer) {}

void AutoCompleteServlet::addTermInfo(const String& term, const TermInfo& ti) {
  if (ti.score < 1000) return;
  term_info_.emplace(term, ti);
}

void AutoCompleteServlet::handleHTTPRequest(
    http::HTTPRequest* req,
    http::HTTPResponse* res) {
  URI uri(req->uri());
  ResultListType results;
  const auto& params = uri.queryParams();

  /* arguments */
  String lang_str;
  if (!URI::getParam(params, "lang", &lang_str)) {
    res->addBody("error: missing ?lang=... parameter");
    res->setStatus(http::kStatusBadRequest);
    return;
  }

  String qstr;
  if (!URI::getParam(params, "q", &qstr)) {
    res->addBody("error: missing ?q=... parameter");
    res->setStatus(http::kStatusBadRequest);
    return;
  }

  Vector<String> terms;
  Vector<String> valid_terms;
  auto lang = languageFromString(lang_str);
  analyzer_->tokenize(lang, qstr, &terms);
  if (terms.size() == 0) {
    res->addBody("error: invalid ?q=... parameter");
    res->setStatus(http::kStatusBadRequest);
    return;
  }

  for (const auto& t : terms) {
    if (term_info_.count(lang_str + "~" + t) > 0) {
      valid_terms.emplace_back(t);
    }
  }

  if (terms.size() == 1 || valid_terms.size() == 0) {
    suggestSingleTerm(lang, terms, &results);
  } else {
    suggestMultiTerm(lang, terms, valid_terms, &results);
  }

  if (results.size() == 0 && qstr.length() > 2) {
    suggestFuzzy(lang, terms, &results);
  }

#ifndef FNORD_NODEBUG
  fnord::logDebug(
      "cm.autocompleteserver",
      "suggest lang=$0 qstr=$1 terms=$2 valid_terms=$3 results=$4",
      lang_str,
      qstr,
      inspect(terms),
      inspect(valid_terms),
      results.size());
#endif

  /* write response */
  res->setStatus(http::kStatusOK);
  res->addHeader("Content-Type", "application/json; charset=utf-8");
  json::JSONOutputStream json(res->getBodyOutputStream());

  json.beginObject();
  json.addObjectEntry("query");
  json.addString(qstr);
  json.addComma();
  json.addObjectEntry("suggestions");
  json.beginArray();

  for (int i = 0; i < results.size() && i < 12; ++i) {
    if (i > 0) {
      json.addComma();
    }
    json.beginObject();
    json.addObjectEntry("text");
    json.addString(std::get<0>(results[i]));
    json.addComma();
    json.addObjectEntry("score");
    json.addFloat(std::get<1>(results[i]));
    json.addComma();
    json.addObjectEntry("url");
    json.addString(std::get<2>(results[i]));
    json.endObject();
  }

  json.endArray();
  json.endObject();
}

void AutoCompleteServlet::suggestSingleTerm(
    Language lang,
    Vector<String> terms,
    ResultListType* results) {
  auto prefix = languageToString(lang) + "~" + terms.back();
  terms.pop_back();
  String qstr_prefix;

  if (terms.size() > 0 ) {
    qstr_prefix += StringUtil::join(terms, " ") + " ";
  }

  Vector<Pair<String, double>> matches;
  double best_match = 0;

  for (auto iter = term_info_.lower_bound(prefix);
      iter != term_info_.end() && StringUtil::beginsWith(iter->first, prefix);
      ++iter) {
    matches.emplace_back(iter->first, iter->second.score);
    if (iter->second.score > best_match) {
      best_match = iter->second.score;
    }
  }

  std::sort(matches.begin(), matches.end(), [] (
      const Pair<String, double>& a,
      const Pair<String, double>& b) {
    return b.second < a.second;
  });

  if (matches.size() > 0) {
    const auto& best_match_ti = term_info_.find(matches[0].first);
    for (const auto& c : best_match_ti->second.top_categories) {
      if ((c.second / best_match_ti->second.top_categories[0].second) < 0.2) {
        break;
      }

      auto label = StringUtil::format(
          "$0$1 in $2",
          qstr_prefix,
          matches[0].first,
          c.first);

      results->emplace_back(label, c.second, "");
    }
  }

  for (int m = 0; m < matches.size(); ++m) {
    auto score = matches[m].second;

    if ((score / best_match) < 0.1) {
      break;
    }

    results->emplace_back(qstr_prefix + matches[m].first, score, "");
  }

  if (matches.size() > 1) {
    const auto& best_match_ti = term_info_.find(matches[0].first);

    for (const auto& r : best_match_ti->second.related_terms) {
      auto label = StringUtil::format(
          "$0$1 $2",
          qstr_prefix,
          matches[0].first,
          r.first);

      results->emplace_back(label, r.second, "");
    }
  }
}

void AutoCompleteServlet::suggestMultiTerm(
    Language lang,
    Vector<String> terms,
    const Vector<String>& valid_terms,
    ResultListType* results) {
  auto lang_str = fnord::languageToString(lang);
  auto last_term = terms.back();
  terms.pop_back();
  String qstr_prefix;

  if (terms.size() > 0 ) {
    qstr_prefix += StringUtil::join(terms, " ") + " ";
  }

  HashMap<String, double> matches_h;
  double best_match = 0;
  for (const auto& vt : valid_terms) {
    if (last_term == vt) {
      continue;
    }

    const auto& vtinfo = term_info_.find(lang_str + "~" + vt)->second;
    for (const auto& related : vtinfo.related_terms) {
      if (!StringUtil::beginsWith(related.first, last_term)) {
        continue;
      }

      matches_h[related.first] += related.second;

      if (matches_h[related.first] > best_match) {
        best_match = matches_h[related.first];
      }
    }
  }

  Vector<Pair<String, double>> matches;
  for (const auto& m : matches_h) {
    matches.emplace_back(m);
  }

  std::sort(matches.begin(), matches.end(), [] (
      const Pair<String, double>& a,
      const Pair<String, double>& b) {
    return b.second < a.second;
  });

  for (int m = 0; m < matches.size(); ++m) {
    auto score = matches[m].second;

    if ((score / best_match) < 0.1) {
      break;
    }

    results->emplace_back(qstr_prefix + matches[m].first, score, "");
  }

  matches_h.clear();
  matches.clear();
  best_match = 0;

  for (const auto& vt : valid_terms) {
    const auto& vtinfo = term_info_.find(lang_str + "~" + vt)->second;
    for (const auto& related : vtinfo.related_terms) {
      matches_h[related.first] += related.second;

      if (matches_h[related.first] > best_match) {
        best_match = matches_h[related.first];
      }
    }
  }

  for (const auto& m : matches_h) {
    matches.emplace_back(m);
  }

  std::sort(matches.begin(), matches.end(), [] (
      const Pair<String, double>& a,
      const Pair<String, double>& b) {
    return b.second < a.second;
  });

  for (int m = 0; m < matches.size(); ++m) {
    auto score = matches[m].second;

    if ((score / best_match) < 0.1) {
      break;
    }

    results->emplace_back(qstr_prefix + matches[m].first, score, "");
  }
}

void AutoCompleteServlet::suggestFuzzy(
    Language lang,
    Vector<String> terms,
    ResultListType* results) {
  results->emplace_back("here be dragons: fuzzy suggestion", 1.0, "");
}

}
<commit_msg>multi term cat suggestion<commit_after>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include "AutoCompleteServlet.h"
#include "CTRCounter.h"
#include "fnord-base/Language.h"
#include "fnord-base/wallclock.h"
#include "fnord-base/io/fileutil.h"

using namespace fnord;

namespace cm {

AutoCompleteServlet::AutoCompleteServlet(
    RefPtr<fts::Analyzer> analyzer) :
    analyzer_(analyzer) {}

void AutoCompleteServlet::addTermInfo(const String& term, const TermInfo& ti) {
  if (ti.score < 1000) return;
  term_info_.emplace(term, ti);
}

void AutoCompleteServlet::handleHTTPRequest(
    http::HTTPRequest* req,
    http::HTTPResponse* res) {
  URI uri(req->uri());
  ResultListType results;
  const auto& params = uri.queryParams();

  /* arguments */
  String lang_str;
  if (!URI::getParam(params, "lang", &lang_str)) {
    res->addBody("error: missing ?lang=... parameter");
    res->setStatus(http::kStatusBadRequest);
    return;
  }

  String qstr;
  if (!URI::getParam(params, "q", &qstr)) {
    res->addBody("error: missing ?q=... parameter");
    res->setStatus(http::kStatusBadRequest);
    return;
  }

  Vector<String> terms;
  Vector<String> valid_terms;
  auto lang = languageFromString(lang_str);
  analyzer_->tokenize(lang, qstr, &terms);
  if (terms.size() == 0) {
    res->addBody("error: invalid ?q=... parameter");
    res->setStatus(http::kStatusBadRequest);
    return;
  }

  for (const auto& t : terms) {
    if (term_info_.count(lang_str + "~" + t) > 0) {
      valid_terms.emplace_back(t);
    }
  }

  if (terms.size() == 1 || valid_terms.size() == 0) {
    suggestSingleTerm(lang, terms, &results);
  } else {
    suggestMultiTerm(lang, terms, valid_terms, &results);
  }

  if (results.size() == 0 && qstr.length() > 2) {
    suggestFuzzy(lang, terms, &results);
  }

#ifndef FNORD_NODEBUG
  fnord::logDebug(
      "cm.autocompleteserver",
      "suggest lang=$0 qstr=$1 terms=$2 valid_terms=$3 results=$4",
      lang_str,
      qstr,
      inspect(terms),
      inspect(valid_terms),
      results.size());
#endif

  /* write response */
  res->setStatus(http::kStatusOK);
  res->addHeader("Content-Type", "application/json; charset=utf-8");
  json::JSONOutputStream json(res->getBodyOutputStream());

  json.beginObject();
  json.addObjectEntry("query");
  json.addString(qstr);
  json.addComma();
  json.addObjectEntry("suggestions");
  json.beginArray();

  for (int i = 0; i < results.size() && i < 12; ++i) {
    if (i > 0) {
      json.addComma();
    }
    json.beginObject();
    json.addObjectEntry("text");
    json.addString(std::get<0>(results[i]));
    json.addComma();
    json.addObjectEntry("score");
    json.addFloat(std::get<1>(results[i]));
    json.addComma();
    json.addObjectEntry("url");
    json.addString(std::get<2>(results[i]));
    json.endObject();
  }

  json.endArray();
  json.endObject();
}

void AutoCompleteServlet::suggestSingleTerm(
    Language lang,
    Vector<String> terms,
    ResultListType* results) {
  auto prefix = languageToString(lang) + "~" + terms.back();
  terms.pop_back();
  String qstr_prefix;

  if (terms.size() > 0 ) {
    qstr_prefix += StringUtil::join(terms, " ") + " ";
  }

  Vector<Pair<String, double>> matches;
  double best_match = 0;

  for (auto iter = term_info_.lower_bound(prefix);
      iter != term_info_.end() && StringUtil::beginsWith(iter->first, prefix);
      ++iter) {
    matches.emplace_back(iter->first, iter->second.score);
    if (iter->second.score > best_match) {
      best_match = iter->second.score;
    }
  }

  std::sort(matches.begin(), matches.end(), [] (
      const Pair<String, double>& a,
      const Pair<String, double>& b) {
    return b.second < a.second;
  });

  if (matches.size() > 0) {
    const auto& best_match_ti = term_info_.find(matches[0].first);
    for (const auto& c : best_match_ti->second.top_categories) {
      if ((c.second / best_match_ti->second.top_categories[0].second) < 0.2) {
        break;
      }

      auto label = StringUtil::format(
          "$0$1 in $2",
          qstr_prefix,
          matches[0].first,
          c.first);

      results->emplace_back(label, c.second, "");
    }
  }

  for (int m = 0; m < matches.size(); ++m) {
    auto score = matches[m].second;

    if ((score / best_match) < 0.1) {
      break;
    }

    results->emplace_back(qstr_prefix + matches[m].first, score, "");
  }

  if (matches.size() > 1) {
    const auto& best_match_ti = term_info_.find(matches[0].first);

    for (const auto& r : best_match_ti->second.related_terms) {
      auto label = StringUtil::format(
          "$0$1 $2",
          qstr_prefix,
          matches[0].first,
          r.first);

      results->emplace_back(label, r.second, "");
    }
  }
}

void AutoCompleteServlet::suggestMultiTerm(
    Language lang,
    Vector<String> terms,
    const Vector<String>& valid_terms,
    ResultListType* results) {
  auto lang_str = fnord::languageToString(lang);
  auto last_term = terms.back();
  terms.pop_back();
  String qstr_prefix;

  if (terms.size() > 0 ) {
    qstr_prefix += StringUtil::join(terms, " ") + " ";
  }

  HashMap<String, double> topcats_h;
  double best_topcat = 0;
  HashMap<String, double> matches_h;
  double best_match = 0;
  for (const auto& vt : valid_terms) {
    const auto& vtinfo = term_info_.find(lang_str + "~" + vt)->second;

    for (const auto& topcat : vtinfo.top_categories) {
      topcats_h[topcat.first] += topcat.second;
      topcats_h[topcat.first] *= 2;

      if (topcats_h[topcat.first] > best_topcat) {
        best_topcat = topcats_h[topcat.first];
      }
    }

    if (last_term != vt) {
      for (const auto& related : vtinfo.related_terms) {
        if (!StringUtil::beginsWith(related.first, last_term)) {
          continue;
        }

        matches_h[related.first] += related.second;

        if (matches_h[related.first] > best_match) {
          best_match = matches_h[related.first];
        }
      }
    }
  }

  Vector<Pair<String, double>> topcats;
  for (const auto& m : topcats_h) {
    topcats.emplace_back(m);
  }

  std::sort(topcats.begin(), topcats.end(), [] (
      const Pair<String, double>& a,
      const Pair<String, double>& b) {
    return b.second < a.second;
  });

  for (int m = 0; m < topcats.size() && m < 3; ++m) {
    auto score = topcats[m].second;

    if ((score / best_topcat) < 0.2) {
      break;
    }

    auto label = StringUtil::format(
        "$0 in $1",
        StringUtil::join(valid_terms, " "),
        topcats[m].first);

    results->emplace_back(label, score, "");
  }

  Vector<Pair<String, double>> matches;
  for (const auto& m : matches_h) {
    matches.emplace_back(m);
  }

  std::sort(matches.begin(), matches.end(), [] (
      const Pair<String, double>& a,
      const Pair<String, double>& b) {
    return b.second < a.second;
  });

  for (int m = 0; m < matches.size(); ++m) {
    auto score = matches[m].second;

    if ((score / best_match) < 0.1) {
      break;
    }

    results->emplace_back(qstr_prefix + matches[m].first, score, "");
  }

  matches_h.clear();
  matches.clear();
  best_match = 0;

  for (const auto& vt : valid_terms) {
    const auto& vtinfo = term_info_.find(lang_str + "~" + vt)->second;
    for (const auto& related : vtinfo.related_terms) {
      matches_h[related.first] += related.second;

      if (matches_h[related.first] > best_match) {
        best_match = matches_h[related.first];
      }
    }
  }

  for (const auto& m : matches_h) {
    matches.emplace_back(m);
  }

  std::sort(matches.begin(), matches.end(), [] (
      const Pair<String, double>& a,
      const Pair<String, double>& b) {
    return b.second < a.second;
  });

  for (int m = 0; m < matches.size(); ++m) {
    auto score = matches[m].second;

    if ((score / best_match) < 0.1) {
      break;
    }

    results->emplace_back(qstr_prefix + matches[m].first, score, "");
  }
}

void AutoCompleteServlet::suggestFuzzy(
    Language lang,
    Vector<String> terms,
    ResultListType* results) {
  results->emplace_back("here be dragons: fuzzy suggestion", 1.0, "");
}

}
<|endoftext|>


<commit_before>#include "pnlAPI.h"
#include "wx/wxprec.h"
#include "lime/LimeSuite.h"
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif //WX_PRECOMP
#include <wx/spinctrl.h>

#include "IConnection.h"
#include "lms7suiteEvents.h"
#include "pnlLimeNetMicro.h"
#include "lms7suiteAppFrame.h"

using namespace std;
using namespace lime;

enum Buttons {
    btnInit,
    btnEnCh,
    btnSetRate,
    btnSetRateDir,
    btnSetFreq,
    btnSetAnt,
    btnSetGain,
    btnSetdB,
    btnSetTest,
    btnGetRate,
    btnGetFreq,
    btnGetAnt,
    btnGetGain,
    btnGetdB,
    btnGetTest,
    btn_COUNT
};

static const wxString test_signals[] = {"None", "NCO CLK/8", "NCO CLK/4", "NCO CLK/8 FS", "NCO CLK/8 FS", "DC"};

pnlAPI::pnlAPI(LMS7SuiteAppFrame* parent) :
    wxFrame(parent, wxID_ANY, "API Calls", wxDefaultPosition, wxDefaultSize,  wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
    lmsControl(nullptr),
    lmsAppFrame(parent)
{
    SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
    wxFlexGridSizer* mainSizer;
    runButtons.resize(btn_COUNT);
    mainSizer = new wxFlexGridSizer( 0, 3, 1, 1);
    mainSizer->AddGrowableCol( 2 );
    mainSizer->SetFlexibleDirection( wxBOTH );
    mainSizer->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );

    auto dirChoice = [this](){
        auto choice = new wxChoice(this, wxID_ANY);
        choice->Append("RX");
        choice->Append("TX");
        choice->SetSelection(0);
        return choice;
    };

    auto functionEntry = [this, mainSizer](Buttons btn, wxString label, std::vector<wxWindow*>args){
        runButtons[btn] = new wxButton(this, wxID_ANY, wxT("Run"), wxDefaultPosition, wxDefaultSize, 0);
        mainSizer->Add( runButtons[btn], 0, wxALL, 1 );
        mainSizer->Add(new wxStaticText(this, wxID_ANY, label), 0, wxALL|wxALIGN_CENTER_VERTICAL, 5);
        runButtons[btn]->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( pnlAPI::OnRun), NULL, this );
        auto paramSizer = new wxFlexGridSizer( 1, 0, 5, 5);
        for (auto a : args)
            paramSizer->Add(a, 0, wxALL|wxALIGN_CENTER_VERTICAL, 1);
        mainSizer->Add(paramSizer, 0, wxALL, 1 );
        return paramSizer;
    };

    functionEntry(btnInit, _("LMS_Init"), {new wxStaticText(this, wxID_ANY, _(" "))});

    enChDir = dirChoice();
    enChCh = new ChWxChoice(this);
    enChEn = new wxChoice(this, wxID_ANY);
    enChEn->Append("Disabled");
    enChEn->SetSelection(enChEn->Append("Enabled"));
    functionEntry(btnEnCh, _("LMS_EnableChannel"), {enChDir, enChCh, enChEn});

    setRateRate = new wxTextCtrl(this, wxNewId(), _("10"), wxDefaultPosition, wxSize(64, -1));
    setRateOv = new wxChoice(this, wxID_ANY);
    for (int i = 32; i; i /= 2)
        setRateOv->Append(wxString::Format(_("%ix"), i));
    setRateOv->SetSelection(setRateOv->Append("Auto"));
    auto text = new wxStaticText(this, wxID_ANY, _("MSps      oversampling:"));
    functionEntry(btnSetRate, _("LMS_SetSampleRate"), {setRateRate, text, setRateOv});

    setRateDirDir = dirChoice();
    setRateDirRate = new wxTextCtrl(this, wxNewId(), _("10"), wxDefaultPosition, wxSize(64, -1));
    setRateDirOv = new wxChoice(this, wxID_ANY);
    for (int i = 32; i; i /= 2)
        setRateDirOv->Append(wxString::Format(_("%ix"), i));
    setRateDirOv->SetSelection(setRateDirOv->Append("Auto"));
    text = new wxStaticText(this, wxID_ANY, _("MSps      oversampling:"));
    functionEntry(btnSetRateDir, _("LMS_SetSampleRateDir"), {setRateDirRate, text, setRateDirOv, setRateDirDir});

    setFreqDir = dirChoice();
    setFreqCh = new ChWxChoice(this);
    setFreqFreq = new wxTextCtrl(this, wxNewId(), _("500"), wxDefaultPosition, wxSize(80, -1));
    text = new wxStaticText(this, wxID_ANY, _("MHz"));
    functionEntry(btnSetFreq, _("LMS_SetLOFrequency"), {setFreqDir, setFreqCh, setFreqFreq, text});

    setAntDir = dirChoice();
    setAntDir->Connect(wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler(pnlAPI::OnAntDir), NULL, this);
    setAntCh = new ChWxChoice(this);
    setAntAnt = new wxChoice(this, wxID_ANY);
    functionEntry(btnSetAnt, _("LMS_SetAntenna"), {setAntDir, setAntCh, setAntAnt});

    setGainDir = dirChoice();
    setGainCh = new ChWxChoice(this);
    setGainGain = new wxTextCtrl(this, wxNewId(), _("0.7"), wxDefaultPosition, wxSize(48, -1));
    functionEntry(btnSetGain, _("LMS_SetNormalizedGain"), {setGainDir, setGainCh, setGainGain});

    setdBDir = dirChoice();
    setdBCh = new ChWxChoice(this);
    setdBGain = new wxSpinCtrl(this, wxNewId(), _(""), wxDefaultPosition, wxSize(56, -1), wxSP_ARROW_KEYS, 0, 100, 50);
    text = new wxStaticText(this, wxID_ANY, _("dB"));
    functionEntry(btnSetdB,  _("LMS_SetGaindB"), {setdBDir, setdBCh, setdBGain, text});

    setTestDir = dirChoice();
    setTestCh = new ChWxChoice(this);
    setTestSig = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 6 , test_signals);
    setTestSig->SetSelection(0);
    setTestI = new wxSpinCtrl(this, wxNewId(), _(""), wxDefaultPosition, wxSize(72, -1), wxSP_ARROW_KEYS, 0, 65535, 10000);
    setTestQ = new wxSpinCtrl(this, wxNewId(), _(""), wxDefaultPosition, wxSize(72, -1), wxSP_ARROW_KEYS, 0, 65535, 10000);
    text = new wxStaticText(this, wxID_ANY, _("DC_I"));
    auto text2 = new wxStaticText(this, wxID_ANY, _("DC_Q"));
    functionEntry(btnSetTest,  _("LMS_SetTestSignal"), {setTestDir, setTestCh, setTestSig, text, setTestI, text2, setTestQ});

    getRateDir = dirChoice();
    getRateCh = new ChWxChoice(this);
    getRateResult = new wxStaticText(this, wxID_ANY, _(""));
    functionEntry(btnGetRate,  _("LMS_GetSampleRate"), {getRateDir, getRateCh, getRateResult});

    getFreqDir = dirChoice();
    getFreqCh = new ChWxChoice(this);
    getFreqResult = new wxStaticText(this, wxID_ANY, _(""));
    functionEntry(btnGetFreq,  _("LMS_GetLOFrequency"), {getFreqDir, getFreqCh, getFreqResult});

    getAntDir = dirChoice();
    getAntCh = new ChWxChoice(this);
    getAntResult = new wxStaticText(this, wxID_ANY, _(""));
    functionEntry(btnGetAnt,  _("LMS_GetAntenna"), {getAntDir, getAntCh, getAntResult});

    getGainDir = dirChoice();
    getGainCh = new ChWxChoice(this);
    getGainResult = new wxStaticText(this, wxID_ANY, _(""));
    functionEntry(btnGetGain,  _("LMS_GetNormalizedGain"), {getGainDir, getGainCh, getGainResult});

    getdBDir = dirChoice();
    getdBCh = new ChWxChoice(this);
    getdBResult = new wxStaticText(this, wxID_ANY, _(""));
    functionEntry(btnGetdB,  _("LMS_GetGaindB"), {getdBDir, getdBCh, getdBResult});

    getTestDir = dirChoice();
    getTestCh = new ChWxChoice(this);
    getTestResult = new wxStaticText(this, wxID_ANY, _(""));
    functionEntry(btnGetTest,  _("LMS_GetTestSignal"), {getTestDir, getTestCh, getTestResult});

    this->SetSizer( mainSizer );
    this->Layout();
    mainSizer->Fit( this );
}

pnlAPI::~pnlAPI()
{
    // Disconnect Events
    setAntDir->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( pnlAPI::OnAntDir ), NULL, this );
    for (auto btn : runButtons)
        btn->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( pnlAPI::OnRun ), NULL, this );
}

void pnlAPI::OnRun( wxCommandEvent& event )
{
    auto obj = event.GetEventObject();
    if (obj == runButtons[btnInit])
    {
        LMS_Init(lmsControl);
        lmsAppFrame->UpdateVisiblePanel();
    }
    else if (obj == runButtons[btnEnCh])
    {
        LMS_EnableChannel(lmsControl, enChDir->GetSelection(), enChCh->GetSelection(), enChEn->GetSelection());
        lmsAppFrame->UpdateVisiblePanel();
    }
    else if (obj == runButtons[btnSetdB])
    {
        LMS_SetGaindB(lmsControl, setdBDir->GetSelection(), setdBCh->GetSelection(), setdBGain->GetValue());
        lmsAppFrame->UpdateVisiblePanel();
    }
    else if (obj == runButtons[btnSetTest])
    {
        LMS_SetTestSignal(lmsControl, setTestDir->GetSelection(), setTestCh->GetSelection(), lms_testsig_t(setTestSig->GetSelection()), setTestI->GetValue(), setTestQ->GetValue());
        lmsAppFrame->UpdateVisiblePanel();
    }
    else if (obj == runButtons[btnSetFreq])
    {
        double freq = 0;
        setFreqFreq->GetValue().ToDouble(&freq);
        LMS_SetLOFrequency(lmsControl, setFreqDir->GetSelection(), setFreqCh->GetSelection(), freq*1e6);
        lmsAppFrame->UpdateVisiblePanel();
    }
    else if (obj == runButtons[btnSetRate])
    {
        double freq = 0;
        setRateRate->GetValue().ToDouble(&freq);
        LMS_SetSampleRate(lmsControl, freq*1e6, 32>>setRateOv->GetSelection());
        lmsAppFrame->UpdateVisiblePanel();
    }
    else if (obj == runButtons[btnSetAnt])
    {
        double freq = 0;
        setRateRate->GetValue().ToDouble(&freq);
        LMS_SetAntenna(lmsControl, setAntDir->GetSelection(), setAntCh->GetSelection(), setAntAnt->GetSelection());
        lmsAppFrame->UpdateVisiblePanel();
    }
    else if (obj == runButtons[btnSetGain])
    {
        double gain = 0;
        setGainGain->GetValue().ToDouble(&gain);
        LMS_SetNormalizedGain(lmsControl, setGainDir->GetSelection(), setGainCh->GetSelection(), gain);
        lmsAppFrame->UpdateVisiblePanel();
    }
    else if (obj == runButtons[btnGetRate])
    {
        double host, rf;
        LMS_GetSampleRate(lmsControl, getRateDir->GetSelection(), getRateCh->GetSelection(), &host, &rf);
        getRateResult->SetLabel(wxString::Format(_("Sample Rate: %.6f MSps  (RF: %.3f MSps)"), host/1e6, rf/1e6));
    }
    else if (obj == runButtons[btnGetFreq])
    {
        double freq;
        LMS_GetLOFrequency(lmsControl, getFreqDir->GetSelection(), getFreqCh->GetSelection(), &freq);
        getFreqResult->SetLabel(wxString::Format(_("RF Frequency: %.6f MHz"), freq/1e6));
    }
    else if (obj == runButtons[btnGetAnt])
    {
        int index = LMS_GetAntenna(lmsControl, getAntDir->GetSelection(), getAntCh->GetSelection());
        if (index < 0)
            return;
        lms_name_t list[16];
        int cnt = LMS_GetAntennaList(lmsControl, getAntDir->GetSelection(), getAntCh->GetSelection(), list);
        if (index >= cnt)
            return;
        getAntResult->SetLabel(wxString::Format(_("%s"), list[index]));
    }
    else if (obj == runButtons[btnGetGain])
    {
        double gain;
        LMS_GetNormalizedGain(lmsControl, getGainDir->GetSelection(), getGainCh->GetSelection(), &gain);
        getGainResult->SetLabel(wxString::Format(_("%.2f"), gain));
    }
    else if (obj == runButtons[btnGetdB])
    {
        unsigned gain;
        LMS_GetGaindB(lmsControl, getdBDir->GetSelection(), getdBCh->GetSelection(), &gain);
        getdBResult->SetLabel(wxString::Format(_("%d dB"), gain));
    }
    else if (obj == runButtons[btnGetTest])
    {
        lms_testsig_t sig = LMS_TESTSIG_NONE;
        LMS_GetTestSignal(lmsControl, getTestDir->GetSelection(), getTestCh->GetSelection(), &sig);
        getTestResult->SetLabel(wxString::Format(_("%s"), test_signals[sig].c_str()));
    }

}

void pnlAPI::OnAntDir( wxCommandEvent& event )
{
    lms_name_t list[16];
    int cnt = LMS_GetAntennaList(lmsControl, event.GetInt(), 0, list);
    setAntAnt->Clear();
    for (int i = 0; i < cnt; i++)
        setAntAnt->Append(list[i]);
    setAntAnt->SetSelection(0);
}

void pnlAPI::Initialize(lms_device_t* controlPort)
{
    lmsControl = controlPort;

    if (lmsControl)
    {
        for (auto ctrl : chControls)
            ctrl->Clear();
        for (int i = 0; i< LMS_GetNumChannels(lmsControl, false); i++)
        {
            auto txt = wxString::Format(_("ch %i"), i);
            for (auto ctrl : chControls)
                ctrl->Append(txt);
        }
        for (auto ctrl : chControls)
            ctrl->SetSelection(0);
        {
            wxCommandEvent evt;
            evt.SetInt(0);
            OnAntDir(evt);
        }
    }
}

<commit_msg>pnlAPI: fix missing handling for LMS_SetSampleRateDir button<commit_after>#include "pnlAPI.h"
#include "wx/wxprec.h"
#include "lime/LimeSuite.h"
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif //WX_PRECOMP
#include <wx/spinctrl.h>

#include "IConnection.h"
#include "lms7suiteEvents.h"
#include "pnlLimeNetMicro.h"
#include "lms7suiteAppFrame.h"

using namespace std;
using namespace lime;

enum Buttons {
    btnInit,
    btnEnCh,
    btnSetRate,
    btnSetRateDir,
    btnSetFreq,
    btnSetAnt,
    btnSetGain,
    btnSetdB,
    btnSetTest,
    btnGetRate,
    btnGetFreq,
    btnGetAnt,
    btnGetGain,
    btnGetdB,
    btnGetTest,
    btn_COUNT
};

static const wxString test_signals[] = {"None", "NCO CLK/8", "NCO CLK/4", "NCO CLK/8 FS", "NCO CLK/8 FS", "DC"};

pnlAPI::pnlAPI(LMS7SuiteAppFrame* parent) :
    wxFrame(parent, wxID_ANY, "API Calls", wxDefaultPosition, wxDefaultSize,  wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
    lmsControl(nullptr),
    lmsAppFrame(parent)
{
    SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
    wxFlexGridSizer* mainSizer;
    runButtons.resize(btn_COUNT);
    mainSizer = new wxFlexGridSizer( 0, 3, 1, 1);
    mainSizer->AddGrowableCol( 2 );
    mainSizer->SetFlexibleDirection( wxBOTH );
    mainSizer->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );

    auto dirChoice = [this](){
        auto choice = new wxChoice(this, wxID_ANY);
        choice->Append("RX");
        choice->Append("TX");
        choice->SetSelection(0);
        return choice;
    };

    auto functionEntry = [this, mainSizer](Buttons btn, wxString label, std::vector<wxWindow*>args){
        runButtons[btn] = new wxButton(this, wxID_ANY, wxT("Run"), wxDefaultPosition, wxDefaultSize, 0);
        mainSizer->Add( runButtons[btn], 0, wxALL, 1 );
        mainSizer->Add(new wxStaticText(this, wxID_ANY, label), 0, wxALL|wxALIGN_CENTER_VERTICAL, 5);
        runButtons[btn]->Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( pnlAPI::OnRun), NULL, this );
        auto paramSizer = new wxFlexGridSizer( 1, 0, 5, 5);
        for (auto a : args)
            paramSizer->Add(a, 0, wxALL|wxALIGN_CENTER_VERTICAL, 1);
        mainSizer->Add(paramSizer, 0, wxALL, 1 );
        return paramSizer;
    };

    functionEntry(btnInit, _("LMS_Init"), {new wxStaticText(this, wxID_ANY, _(" "))});

    enChDir = dirChoice();
    enChCh = new ChWxChoice(this);
    enChEn = new wxChoice(this, wxID_ANY);
    enChEn->Append("Disabled");
    enChEn->SetSelection(enChEn->Append("Enabled"));
    functionEntry(btnEnCh, _("LMS_EnableChannel"), {enChDir, enChCh, enChEn});

    setRateRate = new wxTextCtrl(this, wxNewId(), _("10"), wxDefaultPosition, wxSize(64, -1));
    setRateOv = new wxChoice(this, wxID_ANY);
    for (int i = 32; i; i /= 2)
        setRateOv->Append(wxString::Format(_("%ix"), i));
    setRateOv->SetSelection(setRateOv->Append("Auto"));
    auto text = new wxStaticText(this, wxID_ANY, _("MSps      oversampling:"));
    functionEntry(btnSetRate, _("LMS_SetSampleRate"), {setRateRate, text, setRateOv});

    setRateDirDir = dirChoice();
    setRateDirRate = new wxTextCtrl(this, wxNewId(), _("10"), wxDefaultPosition, wxSize(64, -1));
    setRateDirOv = new wxChoice(this, wxID_ANY);
    for (int i = 32; i; i /= 2)
        setRateDirOv->Append(wxString::Format(_("%ix"), i));
    setRateDirOv->SetSelection(setRateDirOv->Append("Auto"));
    text = new wxStaticText(this, wxID_ANY, _("MSps      oversampling:"));
    functionEntry(btnSetRateDir, _("LMS_SetSampleRateDir"), {setRateDirRate, text, setRateDirOv, setRateDirDir});

    setFreqDir = dirChoice();
    setFreqCh = new ChWxChoice(this);
    setFreqFreq = new wxTextCtrl(this, wxNewId(), _("500"), wxDefaultPosition, wxSize(80, -1));
    text = new wxStaticText(this, wxID_ANY, _("MHz"));
    functionEntry(btnSetFreq, _("LMS_SetLOFrequency"), {setFreqDir, setFreqCh, setFreqFreq, text});

    setAntDir = dirChoice();
    setAntDir->Connect(wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler(pnlAPI::OnAntDir), NULL, this);
    setAntCh = new ChWxChoice(this);
    setAntAnt = new wxChoice(this, wxID_ANY);
    functionEntry(btnSetAnt, _("LMS_SetAntenna"), {setAntDir, setAntCh, setAntAnt});

    setGainDir = dirChoice();
    setGainCh = new ChWxChoice(this);
    setGainGain = new wxTextCtrl(this, wxNewId(), _("0.7"), wxDefaultPosition, wxSize(48, -1));
    functionEntry(btnSetGain, _("LMS_SetNormalizedGain"), {setGainDir, setGainCh, setGainGain});

    setdBDir = dirChoice();
    setdBCh = new ChWxChoice(this);
    setdBGain = new wxSpinCtrl(this, wxNewId(), _(""), wxDefaultPosition, wxSize(56, -1), wxSP_ARROW_KEYS, 0, 100, 50);
    text = new wxStaticText(this, wxID_ANY, _("dB"));
    functionEntry(btnSetdB,  _("LMS_SetGaindB"), {setdBDir, setdBCh, setdBGain, text});

    setTestDir = dirChoice();
    setTestCh = new ChWxChoice(this);
    setTestSig = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 6 , test_signals);
    setTestSig->SetSelection(0);
    setTestI = new wxSpinCtrl(this, wxNewId(), _(""), wxDefaultPosition, wxSize(72, -1), wxSP_ARROW_KEYS, 0, 65535, 10000);
    setTestQ = new wxSpinCtrl(this, wxNewId(), _(""), wxDefaultPosition, wxSize(72, -1), wxSP_ARROW_KEYS, 0, 65535, 10000);
    text = new wxStaticText(this, wxID_ANY, _("DC_I"));
    auto text2 = new wxStaticText(this, wxID_ANY, _("DC_Q"));
    functionEntry(btnSetTest,  _("LMS_SetTestSignal"), {setTestDir, setTestCh, setTestSig, text, setTestI, text2, setTestQ});

    getRateDir = dirChoice();
    getRateCh = new ChWxChoice(this);
    getRateResult = new wxStaticText(this, wxID_ANY, _(""));
    functionEntry(btnGetRate,  _("LMS_GetSampleRate"), {getRateDir, getRateCh, getRateResult});

    getFreqDir = dirChoice();
    getFreqCh = new ChWxChoice(this);
    getFreqResult = new wxStaticText(this, wxID_ANY, _(""));
    functionEntry(btnGetFreq,  _("LMS_GetLOFrequency"), {getFreqDir, getFreqCh, getFreqResult});

    getAntDir = dirChoice();
    getAntCh = new ChWxChoice(this);
    getAntResult = new wxStaticText(this, wxID_ANY, _(""));
    functionEntry(btnGetAnt,  _("LMS_GetAntenna"), {getAntDir, getAntCh, getAntResult});

    getGainDir = dirChoice();
    getGainCh = new ChWxChoice(this);
    getGainResult = new wxStaticText(this, wxID_ANY, _(""));
    functionEntry(btnGetGain,  _("LMS_GetNormalizedGain"), {getGainDir, getGainCh, getGainResult});

    getdBDir = dirChoice();
    getdBCh = new ChWxChoice(this);
    getdBResult = new wxStaticText(this, wxID_ANY, _(""));
    functionEntry(btnGetdB,  _("LMS_GetGaindB"), {getdBDir, getdBCh, getdBResult});

    getTestDir = dirChoice();
    getTestCh = new ChWxChoice(this);
    getTestResult = new wxStaticText(this, wxID_ANY, _(""));
    functionEntry(btnGetTest,  _("LMS_GetTestSignal"), {getTestDir, getTestCh, getTestResult});

    this->SetSizer( mainSizer );
    this->Layout();
    mainSizer->Fit( this );
}

pnlAPI::~pnlAPI()
{
    // Disconnect Events
    setAntDir->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( pnlAPI::OnAntDir ), NULL, this );
    for (auto btn : runButtons)
        btn->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( pnlAPI::OnRun ), NULL, this );
}

void pnlAPI::OnRun( wxCommandEvent& event )
{
    auto obj = event.GetEventObject();
    if (obj == runButtons[btnInit])
    {
        LMS_Init(lmsControl);
        lmsAppFrame->UpdateVisiblePanel();
    }
    else if (obj == runButtons[btnEnCh])
    {
        LMS_EnableChannel(lmsControl, enChDir->GetSelection(), enChCh->GetSelection(), enChEn->GetSelection());
        lmsAppFrame->UpdateVisiblePanel();
    }
    else if (obj == runButtons[btnSetdB])
    {
        LMS_SetGaindB(lmsControl, setdBDir->GetSelection(), setdBCh->GetSelection(), setdBGain->GetValue());
        lmsAppFrame->UpdateVisiblePanel();
    }
    else if (obj == runButtons[btnSetTest])
    {
        LMS_SetTestSignal(lmsControl, setTestDir->GetSelection(), setTestCh->GetSelection(), lms_testsig_t(setTestSig->GetSelection()), setTestI->GetValue(), setTestQ->GetValue());
        lmsAppFrame->UpdateVisiblePanel();
    }
    else if (obj == runButtons[btnSetFreq])
    {
        double freq = 0;
        setFreqFreq->GetValue().ToDouble(&freq);
        LMS_SetLOFrequency(lmsControl, setFreqDir->GetSelection(), setFreqCh->GetSelection(), freq*1e6);
        lmsAppFrame->UpdateVisiblePanel();
    }
    else if (obj == runButtons[btnSetRate])
    {
        double freq = 0;
        setRateRate->GetValue().ToDouble(&freq);
        LMS_SetSampleRate(lmsControl, freq*1e6, 32>>setRateOv->GetSelection());
        lmsAppFrame->UpdateVisiblePanel();
    }
    else if (obj == runButtons[btnSetRateDir])
    {
        double freq = 0;
        setRateDirRate->GetValue().ToDouble(&freq);
        LMS_SetSampleRateDir(lmsControl, setRateDirDir->GetSelection(), freq*1e6, 32>>setRateDirOv->GetSelection());
        lmsAppFrame->UpdateVisiblePanel();
    }
    else if (obj == runButtons[btnSetAnt])
    {
        double freq = 0;
        setRateRate->GetValue().ToDouble(&freq);
        LMS_SetAntenna(lmsControl, setAntDir->GetSelection(), setAntCh->GetSelection(), setAntAnt->GetSelection());
        lmsAppFrame->UpdateVisiblePanel();
    }
    else if (obj == runButtons[btnSetGain])
    {
        double gain = 0;
        setGainGain->GetValue().ToDouble(&gain);
        LMS_SetNormalizedGain(lmsControl, setGainDir->GetSelection(), setGainCh->GetSelection(), gain);
        lmsAppFrame->UpdateVisiblePanel();
    }
    else if (obj == runButtons[btnGetRate])
    {
        double host, rf;
        LMS_GetSampleRate(lmsControl, getRateDir->GetSelection(), getRateCh->GetSelection(), &host, &rf);
        getRateResult->SetLabel(wxString::Format(_("Sample Rate: %.6f MSps  (RF: %.3f MSps)"), host/1e6, rf/1e6));
    }
    else if (obj == runButtons[btnGetFreq])
    {
        double freq;
        LMS_GetLOFrequency(lmsControl, getFreqDir->GetSelection(), getFreqCh->GetSelection(), &freq);
        getFreqResult->SetLabel(wxString::Format(_("RF Frequency: %.6f MHz"), freq/1e6));
    }
    else if (obj == runButtons[btnGetAnt])
    {
        int index = LMS_GetAntenna(lmsControl, getAntDir->GetSelection(), getAntCh->GetSelection());
        if (index < 0)
            return;
        lms_name_t list[16];
        int cnt = LMS_GetAntennaList(lmsControl, getAntDir->GetSelection(), getAntCh->GetSelection(), list);
        if (index >= cnt)
            return;
        getAntResult->SetLabel(wxString::Format(_("%s"), list[index]));
    }
    else if (obj == runButtons[btnGetGain])
    {
        double gain;
        LMS_GetNormalizedGain(lmsControl, getGainDir->GetSelection(), getGainCh->GetSelection(), &gain);
        getGainResult->SetLabel(wxString::Format(_("%.2f"), gain));
    }
    else if (obj == runButtons[btnGetdB])
    {
        unsigned gain;
        LMS_GetGaindB(lmsControl, getdBDir->GetSelection(), getdBCh->GetSelection(), &gain);
        getdBResult->SetLabel(wxString::Format(_("%d dB"), gain));
    }
    else if (obj == runButtons[btnGetTest])
    {
        lms_testsig_t sig = LMS_TESTSIG_NONE;
        LMS_GetTestSignal(lmsControl, getTestDir->GetSelection(), getTestCh->GetSelection(), &sig);
        getTestResult->SetLabel(wxString::Format(_("%s"), test_signals[sig].c_str()));
    }

}

void pnlAPI::OnAntDir( wxCommandEvent& event )
{
    lms_name_t list[16];
    int cnt = LMS_GetAntennaList(lmsControl, event.GetInt(), 0, list);
    setAntAnt->Clear();
    for (int i = 0; i < cnt; i++)
        setAntAnt->Append(list[i]);
    setAntAnt->SetSelection(0);
}

void pnlAPI::Initialize(lms_device_t* controlPort)
{
    lmsControl = controlPort;

    if (lmsControl)
    {
        for (auto ctrl : chControls)
            ctrl->Clear();
        for (int i = 0; i< LMS_GetNumChannels(lmsControl, false); i++)
        {
            auto txt = wxString::Format(_("ch %i"), i);
            for (auto ctrl : chControls)
                ctrl->Append(txt);
        }
        for (auto ctrl : chControls)
            ctrl->SetSelection(0);
        {
            wxCommandEvent evt;
            evt.SetInt(0);
            OnAntDir(evt);
        }
    }
}

<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015 Damien Tardy-Panis
 *
 * This file is subject to the terms and conditions defined in
 * file 'LICENSE', which is part of this source code package.
 **/

#include "Lunarbaboon.h"

#include <QDebug>
#include <QRegularExpression>
#include <QRegularExpressionMatch>

Lunarbaboon::Lunarbaboon(QObject *parent) :
    Comic(parent)
{
    m_info.id             = QString("lunarbaboon");
    m_info.name           = QString("Lunarbaboon");
    m_info.color          = QColor(205, 207, 206);
    m_info.authors        = QStringList("Chris Grady");
    m_info.homepage       = QUrl("http://www.lunarbaboon.com/");
    m_info.country        = QLocale::Canada;
    m_info.language       = QLocale::English;
    m_info.startDate      = QDate::fromString("2012-07-09", Qt::ISODate);
    m_info.endDate        = QDate::currentDate();
    m_info.stripSourceUrl = QUrl("http://www.lunarbaboon.com/");
}

QUrl Lunarbaboon::extractStripImageUrl(QByteArray data)
{
    QString html(data);
    QRegularExpression reg("<img[^>]*src=\"(/storage/[^\"]*)\"");
    QRegularExpressionMatch match = reg.match(html);

    if (!match.hasMatch()) {
        return QUrl();
    }

    QString src = match.captured(1);

    return QUrl("http://www.lunarbaboon.com/" + src);
}
<commit_msg>fix Lunarbaboon comic<commit_after>/**
 * Copyright (c) 2015 Damien Tardy-Panis
 *
 * This file is subject to the terms and conditions defined in
 * file 'LICENSE', which is part of this source code package.
 **/

#include "Lunarbaboon.h"

#include <QDebug>
#include <QRegularExpression>
#include <QRegularExpressionMatch>

Lunarbaboon::Lunarbaboon(QObject *parent) :
    Comic(parent)
{
    m_info.id             = QString("lunarbaboon");
    m_info.name           = QString("Lunarbaboon");
    m_info.color          = QColor(205, 207, 206);
    m_info.authors        = QStringList("Chris Grady");
    m_info.homepage       = QUrl("http://www.lunarbaboon.com/");
    m_info.country        = QLocale::Canada;
    m_info.language       = QLocale::English;
    m_info.startDate      = QDate::fromString("2012-07-09", Qt::ISODate);
    m_info.endDate        = QDate::currentDate();
    m_info.stripSourceUrl = QUrl("http://www.lunarbaboon.com/");
}

QUrl Lunarbaboon::extractStripImageUrl(QByteArray data)
{
    QString html(data);
    QRegularExpression reg("<img[^>]*src=\"(/storage/[^\"]*)\"");
    QRegularExpressionMatch match = reg.match(html);

    if (!match.hasMatch()) {
        return QUrl();
    }

    QString src = match.captured(1);

    return QUrl("http://www.lunarbaboon.com" + src);
}
<|endoftext|>


<commit_before>#include <osg/ArgumentParser>
#include <osg/ApplicationUsage>

#include <osgDB/ReadFile>
#include <osgDB/FileNameUtils>
#include <osgDB/fstream>

#include <iostream>

// Search in str for all occurences of spat and replace them with rpat.
void searchAndReplace(std::string& str, const std::string& spat, const std::string& rpat)
{
    std::string::size_type pos = 0;
    while ((pos = str.find(spat, pos)) != std::string::npos)
    {
        str.replace(pos, spat.length(), rpat);
    }
}

void writeShader(osg::Shader* shader, const std::string& cppFileName, const std::string& variableName)
{
    osgDB::ofstream fout(cppFileName.c_str());
    if (!fout)
    {
        std::cout<<"Error: could not open file `"<<cppFileName<<"` for writing."<<std::endl;
    }

    std::string shaderSource = shader->getShaderSource();
    searchAndReplace(shaderSource, "\r\n", "\n");
    searchAndReplace(shaderSource, "\r", "\n");

    std::string variableString = std::string("char ")+variableName+std::string("[] = ");

    std::string::size_type startOfLine = 0;
    std::string::size_type endOfLine = shaderSource.find_first_of('\n', startOfLine);

    if (endOfLine==std::string::npos)
    {
        fout<<variableString<<shaderSource<<"\\n\";"<<std::endl;
    }
    else
    {
        std::string padding(variableString.size(),' ');

        fout<<variableString<<"\""<<shaderSource.substr(startOfLine,endOfLine-startOfLine)<<"\\n\""<<std::endl;
        startOfLine = endOfLine+1;
        endOfLine = shaderSource.find_first_of('\n', startOfLine);

        while (endOfLine != std::string::npos)
        {
            fout<<padding<<"\""<<shaderSource.substr(startOfLine,endOfLine-startOfLine)<<"\\n\""<<std::endl;
            startOfLine = endOfLine + 1;
            endOfLine = shaderSource.find_first_of('\n', startOfLine);
        }
        fout<<padding<<"\""<<shaderSource.substr(startOfLine,endOfLine-startOfLine)<<"\\n\";"<<std::endl;
    }
    std::cout<<"Written shader to `"<<cppFileName<<"`"<<std::endl;
}

int main( int argc, char **argv )
{
    // use an ArgumentParser object to manage the program arguments.
    osg::ArgumentParser arguments(&argc,argv);

    // set up the usage document, in case we need to print out how to use this program.
    arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName());
    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is a utility for converting between various input and output databases formats.");
    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");
    arguments.getApplicationUsage()->addCommandLineOption("--shader <filename>","Shader file to create a .cpp file for.");
    arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display command line parameters");

    // if user request help write it out to cout.
    if (arguments.read("-h") || arguments.read("--help"))
    {
        arguments.getApplicationUsage()->write(std::cout);
        return 1;
    }

    std::string filename;
    if (arguments.read("--shader",filename))
    {
        osg::ref_ptr<osg::Shader> shader = osgDB::readShaderFile(filename);
        if (shader.valid())
        {
            std::string name = osgDB::getStrippedName(filename);
            std::string path = osgDB::getFilePath(filename);
            std::string invalidCharacters = "-+/\\*=(){}[]:;<>,.?@'~#`!\"";
            std::string numbericCharacters = "0123456789";
            std::string::size_type pos = name.find_first_of(invalidCharacters);
            while (pos != std::string::npos)
            {
                name[pos] = '_';
                pos = name.find_first_of(invalidCharacters);
            }

            std::string ext = osgDB::getFileExtension(filename);
            std::string cppFileName = osgDB::concatPaths(path, name + "_" + ext + ".cpp");
            std::string variableName = name + "_" + ext;
            writeShader(shader.get(), cppFileName, variableName);

            return 0;
        }
        else
        {
            std::cout<<"Error: could not find file '"<<filename<<"'"<<std::endl;
            return 1;
        }

    }

    std::cout<<"No appropriate command line options used."<<std::endl;

    arguments.getApplicationUsage()->write(std::cout);
    return 1;
}
<commit_msg>From Wang Rui, "A Chinese engineer (named Beilei Geng) reports a possible bug in the osg2cpp application to me today. The conversion result may become incorrect if there are quotation marks ( " ) in the shader file, which will mostly appear in comment lines.<commit_after>#include <osg/ArgumentParser>
#include <osg/ApplicationUsage>

#include <osgDB/ReadFile>
#include <osgDB/FileNameUtils>
#include <osgDB/fstream>

#include <iostream>

// Search in str for all occurences of spat and replace them with rpat.
void searchAndReplace(std::string& str, const std::string& spat, const std::string& rpat)
{
    std::string::size_type pos = 0;
    while ((pos = str.find(spat, pos)) != std::string::npos)
    {
        str.replace(pos, spat.length(), rpat);
    }
}

void writeShader(osg::Shader* shader, const std::string& cppFileName, const std::string& variableName)
{
    osgDB::ofstream fout(cppFileName.c_str());
    if (!fout)
    {
        std::cout<<"Error: could not open file `"<<cppFileName<<"` for writing."<<std::endl;
    }

    std::string shaderSource = shader->getShaderSource();
    searchAndReplace(shaderSource, "\r\n", "\n");
    searchAndReplace(shaderSource, "\r", "\n");
    searchAndReplace(shaderSource, "\"", "\\\"");

    std::string variableString = std::string("char ")+variableName+std::string("[] = ");

    std::string::size_type startOfLine = 0;
    std::string::size_type endOfLine = shaderSource.find_first_of('\n', startOfLine);

    if (endOfLine==std::string::npos)
    {
        fout<<variableString<<shaderSource<<"\\n\";"<<std::endl;
    }
    else
    {
        std::string padding(variableString.size(),' ');

        fout<<variableString<<"\""<<shaderSource.substr(startOfLine,endOfLine-startOfLine)<<"\\n\""<<std::endl;
        startOfLine = endOfLine+1;
        endOfLine = shaderSource.find_first_of('\n', startOfLine);

        while (endOfLine != std::string::npos)
        {
            fout<<padding<<"\""<<shaderSource.substr(startOfLine,endOfLine-startOfLine)<<"\\n\""<<std::endl;
            startOfLine = endOfLine + 1;
            endOfLine = shaderSource.find_first_of('\n', startOfLine);
        }
        fout<<padding<<"\""<<shaderSource.substr(startOfLine,endOfLine-startOfLine)<<"\\n\";"<<std::endl;
    }
    std::cout<<"Written shader to `"<<cppFileName<<"`"<<std::endl;
}

int main( int argc, char **argv )
{
    // use an ArgumentParser object to manage the program arguments.
    osg::ArgumentParser arguments(&argc,argv);

    // set up the usage document, in case we need to print out how to use this program.
    arguments.getApplicationUsage()->setApplicationName(arguments.getApplicationName());
    arguments.getApplicationUsage()->setDescription(arguments.getApplicationName()+" is a utility for converting between various input and output databases formats.");
    arguments.getApplicationUsage()->setCommandLineUsage(arguments.getApplicationName()+" [options] filename ...");
    arguments.getApplicationUsage()->addCommandLineOption("--shader <filename>","Shader file to create a .cpp file for.");
    arguments.getApplicationUsage()->addCommandLineOption("-h or --help","Display command line parameters");

    // if user request help write it out to cout.
    if (arguments.read("-h") || arguments.read("--help"))
    {
        arguments.getApplicationUsage()->write(std::cout);
        return 1;
    }

    std::string filename;
    if (arguments.read("--shader",filename))
    {
        osg::ref_ptr<osg::Shader> shader = osgDB::readShaderFile(filename);
        if (shader.valid())
        {
            std::string name = osgDB::getStrippedName(filename);
            std::string path = osgDB::getFilePath(filename);
            std::string invalidCharacters = "-+/\\*=(){}[]:;<>,.?@'~#`!\"";
            std::string numbericCharacters = "0123456789";
            std::string::size_type pos = name.find_first_of(invalidCharacters);
            while (pos != std::string::npos)
            {
                name[pos] = '_';
                pos = name.find_first_of(invalidCharacters);
            }

            std::string ext = osgDB::getFileExtension(filename);
            std::string cppFileName = osgDB::concatPaths(path, name + "_" + ext + ".cpp");
            std::string variableName = name + "_" + ext;
            writeShader(shader.get(), cppFileName, variableName);

            return 0;
        }
        else
        {
            std::cout<<"Error: could not find file '"<<filename<<"'"<<std::endl;
            return 1;
        }

    }

    std::cout<<"No appropriate command line options used."<<std::endl;

    arguments.getApplicationUsage()->write(std::cout);
    return 1;
}
<|endoftext|>


<commit_before>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

#include "sv_scoped.h"

#include <cassert>
#include <sstream>

// Set scope by name, returning the old scope. If the name doesn't describe a
// valid scope, throw an SVScoped::Error.
static svScope SetAbsScope(const std::string &name) {
  svScope new_scope = svGetScopeFromName(name.c_str());
  if (!new_scope)
    throw SVScoped::Error(name);
  return svSetScope(new_scope);
}

// Resolve name to a scope, using the rules described in the comment above the
// class in sv_scoped.h, and then set it. Returns the old scope.
static svScope SetRelScope(const std::string &name) {
  // Absolute (or empty) names resolve to themselves
  if (name[0] != '.') {
    return SetAbsScope(name);
  }

  svScope prev_scope = svGetScope();

  // Special case: If name is ".", it means to use the current scope. Rather
  // than changing scope, we can just return where we are going to stay.
  if (name == ".")
    return prev_scope;

  // For anything else, count how many dots appear after the first one (so
  // ..foo gives an up_count of 1; ...bar gives an up_count of 2).
  size_t first_not_dot = name.find_first_not_of('.', 1);
  if (first_not_dot == std::string::npos) {
    // name looks like "....": that's fine, it just means to go up some number
    // of steps from the current position and not down again. Amend
    // first_not_dot to point at the '\0'.
    first_not_dot = name.size();
  }
  size_t up_count = first_not_dot - 1;

  // Get the name of the current scope, so that we can perform surgery.
  std::string scope_name = svGetNameFromScope(prev_scope);

  // scope_name will look something like "TOP.foo.bar". Search up_count
  // dots from the end, setting last_dot to point at the last dot that should
  // appear in the resolved name.
  //
  // If up_count is too large, behave like "cd /; cd .." and stop at the
  // left-most dot.
  size_t last_dot = scope_name.size();
  for (size_t i = 0; i < up_count; ++i) {
    // This shouldn't ever trigger (because it would mean scope_name was
    // either empty or started with a "."), but allowing it makes the code a
    // bit more uniform.
    if (last_dot == 0)
      break;

    size_t dot = scope_name.rfind('.', last_dot - 1);
    if (dot == std::string::npos)
      break;

    last_dot = dot;
  }

  // Delete everything from last_dot onwards. If we are actually pointing at a
  // dot, this will do something like "TOP.foo.bar" -> "TOP.foo". If up_count
  // was zero or there were no dots, last_dot will equal the size of the string
  // (which means, conveniently, that erase is a no-op).
  scope_name.erase(last_dot);

  // If first_not_dot points inside name (so name looked like "..foo.bar"
  // rather than "..."), subtract one to point at the last dot of the initial
  // segment (we know there is one because name[0] == '.') and then append
  // everything from there to scope_name. For example, if scope_name
  // was "TOP.foo.bar.baz" and name was "..qux", we will have just amended
  // scope_name to be "TOP.foo.bar". Now we want to add ".qux".
  if (first_not_dot < name.size()) {
    scope_name.append(name, first_not_dot - 1);
  }

  return SetAbsScope(scope_name);
}

SVScoped::SVScoped(const std::string &name) : prev_scope_(SetRelScope(name)) {}

SVScoped::Error::Error(const std::string &scope_name)
    : scope_name_(scope_name) {
  std::ostringstream oss;
  oss << "No such SystemVerilog scope: `" << scope_name << "'.";
  msg_ = oss.str();
}
<commit_msg>[otbn] Use relative scope names for OTBN scopes<commit_after>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

#include "sv_scoped.h"

#include <cassert>
#include <sstream>

// Set scope by name, returning the old scope. If the name doesn't describe a
// valid scope, throw an SVScoped::Error.
static svScope SetAbsScope(const std::string &name) {
  svScope new_scope = svGetScopeFromName(name.c_str());
  if (!new_scope)
    throw SVScoped::Error(name);
  return svSetScope(new_scope);
}

// Resolve name to a scope, using the rules described in the comment above the
// class in sv_scoped.h, and then set it. Returns the old scope.
static svScope SetRelScope(const std::string &name) {
  // Absolute (or empty) names resolve to themselves
  if (name[0] != '.') {
    return SetAbsScope(name);
  }

  svScope prev_scope = svGetScope();

  // Special case: If name is ".", it means to use the current scope. Rather
  // than changing scope, we can just return where we are going to stay.
  if (name == ".")
    return prev_scope;

  // For anything else, count how many dots appear after the first one (so
  // ..foo gives an up_count of 1; ...bar gives an up_count of 2).
  size_t first_not_dot = name.find_first_not_of('.', 1);
  if (first_not_dot == std::string::npos) {
    // name looks like "....": that's fine, it just means to go up some number
    // of steps from the current position and not down again. Amend
    // first_not_dot to point at the '\0'.
    first_not_dot = name.size();
  }
  size_t up_count = first_not_dot - 1;

  // Get the name of the current scope, so that we can perform surgery.
  std::string scope_name = svGetNameFromScope(prev_scope);

  // scope_name will look something like "TOP.foo.bar". Search up_count
  // dots from the end, setting last_dot to point at the last dot that should
  // appear in the resolved name.
  //
  // If up_count is too large, behave like "cd /; cd .." and stop at the
  // left-most dot.
  size_t last_dot = scope_name.size();
  for (size_t i = 0; i < up_count; ++i) {
    // This shouldn't ever trigger (because it would mean scope_name was
    // either empty or started with a "."), but allowing it makes the code a
    // bit more uniform.
    if (last_dot == 0)
      break;

    size_t dot = scope_name.rfind('.', last_dot - 1);
    if (dot == std::string::npos)
      break;

    last_dot = dot;
  }

  // Delete everything from last_dot onwards. If we are actually pointing at a
  // dot, this will do something like "TOP.foo.bar" -> "TOP.foo". If up_count
  // was zero or there were no dots, last_dot will equal the size of the string
  // (which means, conveniently, that erase is a no-op).
  scope_name.erase(last_dot);

  // If first_not_dot points inside name (so name looked like "..foo.bar"
  // rather than "..."), subtract one to point at the last dot of the initial
  // segment (we know there is one because name[0] == '.') and then append
  // everything to scope_name starting from there. For example, if scope_name
  // was "TOP.foo.bar.baz" and name was "..qux", we will have just amended
  // scope_name to be "TOP.foo.bar". Now we want to add ".qux".
  if (first_not_dot < name.size()) {
    scope_name.append(name, first_not_dot - 1, std::string::npos);
  }

  return SetAbsScope(scope_name);
}

SVScoped::SVScoped(const std::string &name) : prev_scope_(SetRelScope(name)) {}

SVScoped::Error::Error(const std::string &scope_name)
    : scope_name_(scope_name) {
  std::ostringstream oss;
  oss << "No such SystemVerilog scope: `" << scope_name << "'.";
  msg_ = oss.str();
}
<|endoftext|>