switch (Tags::updateReplaygain(filePath, it.value())) {
        case Tags::Update_Failed:
            failed.append(filePath);
            break;
        case Tags::Update_BadFile:
            failed.append(tr("%1 (Corrupt tags?)", "filename (Corrupt tags?)").arg(filePath));
            break;
        default:
            break;
        }

/*
 * Cantata
 *
 *
 * ----
 *
 */

#include "rgdialog.h"
#ifdef ENABLE_DEVICES_SUPPORT
#include "devices/device.h"
#include "models/devicesmodel.h"
#endif
#include "gui/settings.h"
#include "tags/tags.h"
#include "tagreader.h"
#include "support/utils.h"
#include "support/messagebox.h"
#include "support/monoicon.h"
#include "jobcontroller.h"
#include "widgets/basicitemdelegate.h"
#include "support/action.h"
#include "mpd-interface/cuefile.h"
#include <QComboBox>
#include <QTreeWidget>
#include <QLabel>
#include <QProgressBar>
#include <QBoxLayout>
#include <QHeaderView>
#include <QCloseEvent>
#include <QCoreApplication>
#include <QEventLoop>
#include <algorithm>

enum Columns
{
    COL_ARTIST,
    COL_ALBUM,
    COL_TITLE,
    COL_ALBUMGAIN,
    COL_TRACKGAIN,
    COL_ALBUMPEAK,
    COL_TRACKPEAK
};

static int iCount=0;

int RgDialog::instanceCount()
{
    return iCount;
}

static inline void setResizeMode(QHeaderView *hdr, int idx, QHeaderView::ResizeMode mode)
{
    hdr->setSectionResizeMode(idx, mode);
}

RgDialog::RgDialog(QWidget *parent)
    : SongDialog(parent, "RgDialog", QSize(800, 400))
    , state(State_Idle)
    , totalToScan(0)
    , tagReader(0)
    , autoScanTags(false)
{
    iCount++;
    setButtons(User1|Ok|Cancel);
    setCaption(tr("ReplayGain"));
    setAttribute(Qt::WA_DeleteOnClose);
    QWidget *mainWidget = new QWidget(this);
    QBoxLayout *layout=new QBoxLayout(QBoxLayout::TopToBottom, mainWidget);
    combo = new QComboBox(this);
    view = new QTreeWidget(this);
    statusLabel = new QLabel(this);
    statusLabel->setVisible(false);
    progress = new QProgressBar(this);
    progress->setVisible(false);
    combo->addItem(tr("Show All Tracks"), true);
    combo->addItem(tr("Show Untagged Tracks"), false);
    view->setRootIsDecorated(false);
    view->setAllColumnsShowFocus(true);
    view->setItemDelegate(new BasicItemDelegate(view));
    view->setAlternatingRowColors(false);
    view->setContextMenuPolicy(Qt::ActionsContextMenu);
    view->setSelectionMode(QAbstractItemView::ExtendedSelection);
    removeAct=new Action(tr("Remove From List"), view);
    removeAct->setEnabled(false);
    view->addAction(removeAct);
    QTreeWidgetItem *hdr = view->headerItem();
    hdr->setText(COL_ARTIST, tr("Artist"));
    hdr->setText(COL_ALBUM, tr("Album"));
    hdr->setText(COL_TITLE, tr("Title"));
    hdr->setText(COL_ALBUMGAIN, tr("Album Gain"));
    hdr->setText(COL_TRACKGAIN, tr("Track Gain"));
    hdr->setText(COL_ALBUMPEAK, tr("Album Peak"));
    hdr->setText(COL_TRACKPEAK, tr("Track Peak"));

    QHeaderView *hv=view->header();
    setResizeMode(hv, COL_ARTIST, QHeaderView::ResizeToContents);
    setResizeMode(hv, COL_ALBUM, QHeaderView::ResizeToContents);
    setResizeMode(hv, COL_TITLE, QHeaderView::Stretch);
    setResizeMode(hv, COL_ALBUMGAIN, QHeaderView::Fixed);
    setResizeMode(hv, COL_TRACKGAIN, QHeaderView::Fixed);
    setResizeMode(hv, COL_ALBUMPEAK, QHeaderView::Fixed);
    setResizeMode(hv, COL_TRACKPEAK, QHeaderView::Fixed);
    hv->setStretchLastSection(false);

    layout->setMargin(0);
    layout->addWidget(combo);
    layout->addWidget(view);
    layout->addWidget(statusLabel);
    layout->addWidget(progress);
    setMainWidget(mainWidget);
    setButtonGuiItem(Ok, StdGuiItem::save());
    setButtonGuiItem(Cancel, StdGuiItem::close());
    setButtonGuiItem(User1, GuiItem(tr("Scan"), FontAwesome::search));
    enableButton(Ok, false);
    enableButton(User1, false);
    qRegisterMetaType<Tags::ReplayGain>("Tags::ReplayGain");
    connect(combo, SIGNAL(currentIndexChanged(int)), SLOT(toggleDisplay()));
    connect(view, SIGNAL(itemSelectionChanged()), SLOT(controlRemoveAct()));
    connect(removeAct, SIGNAL(triggered()), SLOT(removeItems()));

    italic=font();
    italic.setItalic(true);
    JobController::self()->setMaxActive(1);
}

RgDialog::~RgDialog()
{
    clearScanners();
    iCount--;
}

void RgDialog::show(const QList<Song> &songs, const QString &udi, bool autoScan)
{
    if (songs.isEmpty()) {
        deleteLater();
        return;
    }

    for (const Song &s: songs) {
        if (!CueFile::isCue(s.file)) {
           origSongs.append(s);
        }
    }

    if (origSongs.isEmpty()) {
        deleteLater();
        return;
    }

    autoScanTags=autoScan;
    std::sort(origSongs.begin(), origSongs.end());

    #ifdef ENABLE_DEVICES_SUPPORT
    if (udi.isEmpty()) {
        base=MPDConnection::self()->getDetails().dir;
    } else {
        Device *dev=getDevice(udi, parentWidget());

        if (!dev) {
            deleteLater();
            return;
        }

        base=dev->path();
    }
    #else
    base=MPDConnection::self()->getDetails().dir;
    #endif

    if (!songsOk(origSongs, base, udi.isEmpty())) {
        return;
    }

    state=State_Idle;
    enableButton(User1, origSongs.count());
    view->clear();
    for (const Song &s: origSongs) {
        if (s.isLocalFile()) {
            base = QString();
        }
        QString aa = s.albumArtist();
        const QString &al = s.album;
        const QString &t = s.title;
        if (aa.isEmpty() && al.isEmpty() && t.isEmpty()) {
            new QTreeWidgetItem(view, QStringList() << "-" << "-" << Utils::getFile(s.file));
        } else {
            new QTreeWidgetItem(view, QStringList() << s.albumArtist() << s.album << s.title);
        }
    }
    Dialog::show();
    startReadingTags();
}

void RgDialog::slotButtonClicked(int button)
{
    if (State_Saving==state) {
        return;
    }

    switch (button) {
    case Ok:
        //if (MessageBox::Yes==MessageBox::questionYesNo(this, tr("Update ReplayGain tags in tracks?"), tr("Update Tags"),
            if (saveTags()) {
                stopScanning();
                accept();
            }
        break;
    case User1:
        startScanning();
        break;
    case Cancel:
        switch (state) {
        case State_ScanningFiles:
            if (MessageBox::Yes==MessageBox::questionYesNo(this, tr("Abort scanning of tracks?"), tr("Abort"),
                                                           GuiItem(tr("Abort")), StdGuiItem::no())) {
                stopScanning();
                // Need to call this - if not, when dialog is closed by window X control, it is not deleted!!!!
                Dialog::slotButtonClicked(button);
                state=State_Idle;
            }
            break;
        case State_ScanningTags:
            if (MessageBox::Yes==MessageBox::questionYesNo(this, tr("Abort reading of existing tags?"), tr("Abort"),
                                                           GuiItem(tr("Abort")), StdGuiItem::no())) {
                stopReadingTags();
                // Need to call this - if not, when dialog is closed by window X control, it is not deleted!!!!
                Dialog::slotButtonClicked(button);
                state=State_Idle;
            }
            break;
        default:
        case State_Idle:
            stopScanning();
            reject();
            // Need to call this - if not, when dialog is closed by window X control, it is not deleted!!!!
            Dialog::slotButtonClicked(button);
            break;
        }
        break;
    default:
        break;
    }
}

void RgDialog::startScanning()
{
    bool all=origTags.isEmpty() ||
             (origTags.count()==origSongs.count()
                ? MessageBox::Yes==MessageBox::questionYesNo(this, tr("Scan all tracks?

All tracks have existing ReplayGain tags."), QString(),
                                                             GuiItem(tr("Scan")), StdGuiItem::cancel())
                : MessageBox::No==MessageBox::questionYesNo(this, tr("Do you wish to scan all tracks, or only tracks without existing tags?"), QString(),
                                                            GuiItem(tr("Untagged Tracks")), GuiItem(tr("All Tracks"))));
    if (!all && origTags.count()==origSongs.count()) {
        return;
    }
    setButtonGuiItem(Cancel, StdGuiItem::cancel());
    state=State_ScanningFiles;
    enableButton(Ok, false);
    enableButton(User1, false);
    progress->setValue(0);
    progress->setVisible(true);
    statusLabel->setText(tr("Scanning tracks..."));
    statusLabel->setVisible(true);
    clearScanners();
    totalToScan=0;
    QMap<QString, QList<int> > groupedTracks;
    for (int i=0; i<origSongs.count(); ++i) {
        if (!removedItems.contains(i) && (all || !origTags.contains(i))) {
            const Song &sng=origSongs.at(i);
            QString aa = sng.albumArtist();
            const QString &al = sng.album;
            groupedTracks[aa.isEmpty() && al.isEmpty() ? (Utils::getFile(sng.file)+" -- "+QString::number(i)) : (sng.albumArtist()+" -- "+sng.album)].append(i);
        }
    }
    QMap<QString, QList<int> >::ConstIterator it(groupedTracks.constBegin());
    QMap<QString, QList<int> >::ConstIterator end(groupedTracks.constEnd());

    for (; it!=end; ++it) {
        createScanner(*it);
        totalToScan++;
    }
    progress->setRange(0, 100*totalToScan);
}

void RgDialog::stopScanning()
{
    state=State_Idle;
    enableButton(User1, true);
    progress->setVisible(false);
    statusLabel->setVisible(false);

    JobController::self()->cancel();
    clearScanners();
    setButtonGuiItem(Cancel, StdGuiItem::close());
}

void RgDialog::createScanner(const QList<int> &indexes)
{
    QMap<int, QString> fileMap;
    for (int i: indexes) {
        fileMap[i]=origSongs.at(i).filePath(base);
    }

    AlbumScanner *s=new AlbumScanner(fileMap);
    connect(s, SIGNAL(progress(int)), this, SLOT(scannerProgress(int)));
    connect(s, SIGNAL(done()), this, SLOT(scannerDone()));
    scanners[s]=0;
    JobController::self()->add(s);
}

void RgDialog::clearScanners()
{
    QList<AlbumScanner *> scannerList=scanners.keys();
    for (AlbumScanner *sc: scannerList) {
        sc->stop();
    }
    scanners.clear();
}

void RgDialog::startReadingTags()
{
    if (tagReader) {
        return;
    }
    setButtonGuiItem(Cancel, StdGuiItem::cancel());
    state=State_ScanningTags;
    enableButton(Ok, false);
    enableButton(User1, false);
    progress->setRange(0, origSongs.count());
    progress->setVisible(true);
    statusLabel->setText(tr("Reading existing tags..."));
    statusLabel->setVisible(true);
    tagReader=new TagReader();
    tagReader->setDetails(origSongs, base);
    connect(tagReader, SIGNAL(progress(int, Tags::ReplayGain)), this, SLOT(songTags(int, Tags::ReplayGain)));
    connect(tagReader, SIGNAL(done()), this, SLOT(tagReaderDone()));
    JobController::self()->add(tagReader);
}

void RgDialog::stopReadingTags()
{
    if (!tagReader) {
        return;
    }

    state=State_Idle;
    enableButton(User1, true);
    progress->setVisible(false);
    statusLabel->setVisible(false);
    disconnect(tagReader, SIGNAL(progress(int, Tags::ReplayGain)), this, SLOT(songTags(int, Tags::ReplayGain)));
    disconnect(tagReader, SIGNAL(done()), this, SLOT(tagReaderDone()));
    tagReader->requestAbort();
    tagReader=0;
    autoScanTags=false;
}

bool RgDialog::saveTags()
{
    state=State_Saving;
    enableButton(Ok, false);
    enableButton(Close, false);
    enableButton(Cancel, false);
    enableButton(User1, false);

    QStringList failed;

    progress->setVisible(true);
    progress->setRange(0, tagsToSave.count());

    int count=0;
    bool someTimedout=false;
    QMap<int, Tags::ReplayGain>::ConstIterator it=tagsToSave.constBegin();
    QMap<int, Tags::ReplayGain>::ConstIterator end=tagsToSave.constEnd();

    for (; it!=end; ++it) {
        QString filePath=origSongs.at(it.key()).filePath(base);

        progress->setValue(progress->value()+1);
        if (0==count++%10) {
            QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
        }
    }

    if (failed.count()) {
        MessageBox::errorListEx(this, tr("Failed to update the tags of the following tracks:"), failed);
    }

    return !someTimedout;
}

void RgDialog::updateView()
{
    int finished=0;
    quint64 totalProgress=0;
    QMap<AlbumScanner *, int>::ConstIterator it=scanners.constBegin();
    QMap<AlbumScanner *, int>::ConstIterator end=scanners.constEnd();

    for (; it!=end; ++it) {
        if (100==it.value()) {
            finished++;
        }
        totalProgress+=(*it);
    }

    if (finished==totalToScan) {
        progress->setVisible(false);
        statusLabel->setVisible(false);
        state=State_Idle;
        setButtonGuiItem(Cancel, StdGuiItem::close());
        enableButton(Ok, !tagsToSave.isEmpty());
    } else {
        progress->setValue(totalProgress);
    }
}

#ifdef ENABLE_DEVICES_SUPPORT
Device * RgDialog::getDevice(const QString &udi, QWidget *p)
{
    Device *dev=DevicesModel::self()->device(udi);
    if (!dev) {
        MessageBox::error(p ? p : this, tr("Device has been removed!"));
        reject();
        return 0;
    }
    if (!dev->isConnected()) {
        MessageBox::error(p ? p : this, tr("Device is not connected."));
        reject();
        return 0;
    }
    if (!dev->isIdle()) {
        MessageBox::error(p ? p : this, tr("Device is busy?"));
        reject();
        return 0;
    }
    return dev;
}
#endif

void RgDialog::scannerProgress(int p)
{
    AlbumScanner *s=qobject_cast<AlbumScanner *>(sender());
    if (!s) {
        return;
    }

    scanners[s]=p;
    updateView();
}

void RgDialog::scannerDone()
{
    AlbumScanner *s=qobject_cast<AlbumScanner *>(sender());
    if (!s) {
        return;
    }

    const QMap<int, AlbumScanner::Values> &trackValues=s->trackValues();
    QMap<int, AlbumScanner::Values>::ConstIterator it(trackValues.constBegin());
    QMap<int, AlbumScanner::Values>::ConstIterator end(trackValues.constEnd());

    if (s->success()) {
        for(; it!=end; ++it) {
            Tags::ReplayGain updatedTags(it.value().gain, s->albumValues().gain, it.value().peak, s->albumValues().peak);
            QTreeWidgetItem *item=view->topLevelItem(it.key());
            if (it.value().ok) {
                item->setText(COL_TRACKGAIN, tr("%1 dB").arg(Utils::formatNumber(updatedTags.trackGain, 2)));
                if (!Utils::equal(updatedTags.trackPeak, 0.0)) {
                    item->setText(COL_TRACKPEAK, Utils::formatNumber(updatedTags.trackPeak, 6));
                }
            } else {
                item->setText(COL_TRACKGAIN, tr("Failed"));
                item->setText(COL_TRACKPEAK, tr("Failed"));
            }
            if (s->albumValues().ok) {
                item->setText(COL_ALBUMGAIN, tr("%1 dB").arg(Utils::formatNumber(updatedTags.albumGain, 2)));
                if (!Utils::equal(updatedTags.albumPeak, 0.0)) {
                    item->setText(COL_ALBUMPEAK, Utils::formatNumber(updatedTags.albumPeak, 6));
                }
            } else {
                item->setText(COL_ALBUMGAIN, tr("Failed"));
                item->setText(COL_ALBUMPEAK, tr("Failed"));
            }

            if (it.value().ok && origTags.contains(it.key())) {
                Tags::ReplayGain t=origTags[it.key()];
                bool diff=false;
                bool diffAlbum=false;
                if (!Utils::equal(t.trackGain, updatedTags.trackGain, 0.01)) {
                    item->setFont(COL_TRACKGAIN, italic);
                    item->setToolTip(COL_TRACKGAIN, tr("Original: %1 dB").arg(Utils::formatNumber(t.trackGain, 2)));
                    diff=true;
                }
                if (!Utils::equal(updatedTags.trackPeak, 0.0) && !Utils::equal(t.trackPeak, updatedTags.trackPeak, 0.000001)) {
                    item->setFont(COL_TRACKPEAK, italic);
                    item->setToolTip(COL_TRACKPEAK, tr("Original: %1").arg(Utils::formatNumber(t.trackPeak, 6)));
                    diff=true;
                }
                if (!Utils::equal(t.albumGain, updatedTags.albumGain, 0.01)) {
                    item->setFont(COL_ALBUMGAIN, italic);
                    item->setToolTip(COL_ALBUMGAIN, tr("Original: %1 dB").arg(Utils::formatNumber(t.albumGain, 2)));
                    diffAlbum=true;
                }
                if (!Utils::equal(updatedTags.albumPeak, 0.0) && !Utils::equal(t.albumPeak, updatedTags.albumPeak, 0.000001)) {
                    item->setFont(COL_ALBUMPEAK, italic);
                    item->setToolTip(COL_ALBUMPEAK, tr("Original: %1").arg(Utils::formatNumber(t.albumPeak, 6)));
                    diffAlbum=true;
                }
                if (diff || diffAlbum) {
                    if (diff) {
                        item->setFont(COL_ARTIST, italic);
                        item->setFont(COL_TITLE, italic);
                    }
                    if (diffAlbum) {
                        item->setFont(COL_ALBUM, italic);
                    }
                    tagsToSave.insert(it.key(), updatedTags);
                } else {
                    tagsToSave.remove(it.key());
                }
            } else if (it.value().ok) {
                item->setFont(COL_ARTIST, italic);
                item->setFont(COL_ALBUM, italic);
                item->setFont(COL_TITLE, italic);
                item->setFont(COL_TRACKGAIN, italic);
                item->setFont(COL_TRACKPEAK, italic);
                item->setFont(COL_ALBUMGAIN, italic);
                item->setFont(COL_ALBUMPEAK, italic);
                tagsToSave.insert(it.key(), updatedTags);
            } else {
                tagsToSave.remove(it.key());
            }
        }
    } else {
        for(; it!=end; ++it) {
            QTreeWidgetItem *item=view->topLevelItem(it.key());
            item->setText(COL_TRACKGAIN, tr("Failed"));
            item->setText(COL_TRACKPEAK, tr("Failed"));
            item->setText(COL_ALBUMGAIN, tr("Failed"));
            item->setText(COL_ALBUMPEAK, tr("Failed"));
            tagsToSave.remove(it.key());
        }
    }

    scanners[s]=100;
    updateView();
    JobController::self()->finishedWith(s);
}

void RgDialog::songTags(int index, Tags::ReplayGain tags)
{
    if (index>=0 && index<origSongs.count()) {
        progress->setValue(progress->value()+1);
        if (!tags.isEmpty()) {
            origTags[index]=tags;

            QTreeWidgetItem *item=view->topLevelItem(index);
            if (!item) {
                return;
            }
            item->setText(COL_TRACKGAIN, tr("%1 dB").arg(Utils::formatNumber(tags.trackGain, 2)));
            if (!Utils::equal(tags.trackPeak, 0.0)) {
                item->setText(COL_TRACKPEAK, Utils::formatNumber(tags.trackPeak, 6));
            }
            item->setText(COL_ALBUMGAIN, tr("%1 dB").arg(Utils::formatNumber(tags.albumGain, 2)));
            if (!Utils::equal(tags.albumPeak, 0.0)) {
                item->setText(COL_ALBUMPEAK, Utils::formatNumber(tags.albumPeak, 6));
            }
        }
    }
}

void RgDialog::tagReaderDone()
{
    TagReader *t=qobject_cast<TagReader *>(sender());
    if (!t) {
        return;
    }

    JobController::self()->finishedWith(t);
    tagReader=0;

    state=State_Idle;
    enableButton(User1, true);
    progress->setVisible(false);
    statusLabel->setVisible(false);

    if (autoScanTags) {
        autoScanTags=false;
        startScanning();
    }
}

void RgDialog::toggleDisplay()
{
    bool showAll=combo->itemData(combo->currentIndex()).toBool();

    for (int i=0; i<view->topLevelItemCount(); ++i) {
        QTreeWidgetItem *item=view->topLevelItem(i);
        if (!removedItems.contains(view->indexOfTopLevelItem(item))) {
            item->setHidden(showAll ? false : origTags.contains(i));
        }
    }
}

void RgDialog::controlRemoveAct()
{
    removeAct->setEnabled(view->topLevelItemCount()>1 && !view->selectedItems().isEmpty());
}

void RgDialog::removeItems()
{
    if (view->topLevelItemCount()<1) {
        return;
    }

    if (MessageBox::Yes==MessageBox::questionYesNo(this, tr("Remove the selected tracks from the list?"),
                                                   tr("Remove Tracks"), StdGuiItem::remove(), StdGuiItem::cancel())) {
        QList<QTreeWidgetItem *> selection=view->selectedItems();
        for (QTreeWidgetItem *item: selection) {
            int index=view->indexOfTopLevelItem(item);
            item->setHidden(true);
            removedItems.insert(index);
            if (tagsToSave.contains(index)) {
                tagsToSave.remove(index);
            }
        }
    }
}

void RgDialog::closeEvent(QCloseEvent *event)
{
    if (State_Idle!=state) {
        slotButtonClicked(Cancel);
        if (State_Idle!=state) {
            event->ignore();
        }
    } else {
        Dialog::closeEvent(event);
    }
}

#include "moc_rgdialog.cpp"


mc_foreacharrayIndex(idx, UnifiedConversationID, vConvID, updatedConversationsIDs) {
        if (!lastConversationsIDsSet->containsObject(vConvID)) {
            indexAdditions->addObject(Value::valueWithIntValue(idx));
        }
    }

// DejaLu

#include "HMUnifiedMailStorageView.h"

#include <libetpan/libetpan.h>

#include "HMMailStorageView.h"
#include "HMUnifiedMailStorageViewObserver.h"
#include "HMUtils.h"

using namespace hermes;
using namespace mailcore;

#define UPDATE_DELAY 0.1

namespace hermes {
    class UnifiedConversationID : public Object {
    public:
        unsigned int accountIndex;
        int64_t convID;

        virtual bool isEqual(Object * otherObject)
        {
            UnifiedConversationID * otherConvID = (UnifiedConversationID *) otherObject;
            return accountIndex == otherConvID->accountIndex && convID == otherConvID->convID;
        }

        virtual unsigned int hash()
        {
            unsigned int c = 5381;

            c = ((c << 5) + c) + accountIndex;
            c = ((c << 5) + c) + (convID >> 32);
            c = ((c << 5) + c) + (convID & 0xffffffff);

            return c;
        }

        virtual Object * copy()
        {
            UnifiedConversationID * result = new UnifiedConversationID;
            result->accountIndex = accountIndex;
            result->convID = convID;
            return result;
        }
    };
}

UnifiedMailStorageView::UnifiedMailStorageView()
{
    mViews = NULL;
    mConversations = new Array();
    mObservers = carray_new(4);
    mLastUpdateTimestamp = 0;
    mModifiedConversationsIDs = new Set();
    mScheduledUpdate = false;
}

UnifiedMailStorageView::~UnifiedMailStorageView()
{
    cancelDelayedPerformMethod((Object::Method) &UnifiedMailStorageView::updateNow, NULL);
    MC_SAFE_RELEASE(mModifiedConversationsIDs);
    mc_foreacharray(MailStorageView, view, mViews) {
        view->removeObserver(this);
    }
    carray_free(mObservers);
    MC_SAFE_RELEASE(mConversations);
    MC_SAFE_RELEASE(mViews);
}

void UnifiedMailStorageView::setStorageViews(mailcore::Array * /* MailStorageView */ views)
{
    bool hasConversations = false;

    {
        mc_foreacharray(MailStorageView, view, mViews) {
            view->removeObserver(this);
        }
    }
    MC_SAFE_REPLACE_RETAIN(Array, mViews, views);
    {
        mc_foreacharray(MailStorageView, view, mViews) {
            view->addObserver(this);
            if (view->conversationsCount() > 0) {
                hasConversations = true;
            }
        }
    }

    if (hasConversations) {
        mc_foreacharray(MailStorageView, view, mViews) {
            mailStorageViewChanged(view,
                                   NULL,
                                   NULL,
                                   NULL,
                                   NULL,
                                   Array::array());
        }
    }
}

mailcore::Array * UnifiedMailStorageView::storageViews()
{
    return mViews;
}

void UnifiedMailStorageView::addObserver(UnifiedMailStorageViewObserver * observer)
{
    carray_add(mObservers, observer, NULL);
}

void UnifiedMailStorageView::removeObserver(UnifiedMailStorageViewObserver * observer)
{
    for(unsigned int i = 0 ; i < carray_count(mObservers) ; i ++) {
        if (carray_get(mObservers, i) == observer) {
            carray_delete(mObservers, i);
            break;
        }
    }
}

unsigned int UnifiedMailStorageView::conversationsCount()
{
    return mConversations->count();
}

mailcore::HashMap * UnifiedMailStorageView::conversationsInfoAtIndex(unsigned int idx)
{
    return (HashMap *) mConversations->objectAtIndex(idx);
}

mailcore::HashMap * UnifiedMailStorageView::conversationsInfoForConversationID(unsigned int accountIndex,
                                                                               int64_t conversationID)
{
    MailStorageView * view = (MailStorageView *) mViews->objectAtIndex(accountIndex);
    HashMap * info = view->conversationsInfoForConversationID(conversationID);
    info = (HashMap *) info->copy();
    info->setObjectForKey(MCSTR("account"), Value::valueWithUnsignedIntValue(accountIndex));
    info->autorelease();
    return info;
}

bool UnifiedMailStorageView::isLoading()
{
    bool loading = false;
    mc_foreacharray(MailStorageView, view, mViews) {
        if (view->isLoading()) {
            loading = true;
        }
    }
    return loading;
}

static int compareConversation(void * a, void * b, void * context)
{
    HashMap * conversationA = (HashMap *) a;
    HashMap * conversationB = (HashMap *) b;
    Value * vDateA = (Value *) conversationA->objectForKey(MCSTR("timestamp"));
    Value * vDateB = (Value *) conversationB->objectForKey(MCSTR("timestamp"));
    int64_t delta = vDateB->longLongValue() - vDateA->longLongValue();
    if (delta < 0LL) {
        return -1;
    }
    else if (delta > 0LL) {
        return 1;
    }
    else {
        Value * vIdxA = (Value *) conversationA->objectForKey(MCSTR("account"));
        Value * vIdxB = (Value *) conversationA->objectForKey(MCSTR("account"));
        int deltaIdx = (int) vIdxB->unsignedIntValue() - (int) vIdxA->unsignedIntValue();
        if (deltaIdx < 0) {
            return -1;
        }
        else if (deltaIdx > 0) {
            return 1;
        }
        else {
            Value * vIDA = (Value *) conversationA->objectForKey(MCSTR("id"));
            Value * vIDB = (Value *) conversationB->objectForKey(MCSTR("id"));
            int64_t idDelta = vIDB->longLongValue() - vIDA->longLongValue();
            if (idDelta < 0LL) {
                return 1;
            }
            else if (idDelta > 0LL) {
                return -1;
            }
            else {
                return 0;
            }
        }
    }
}

void UnifiedMailStorageView::mailStorageViewChanged(MailStorageView * view,
                                                    mailcore::Array * deleted,
                                                    mailcore::Array * moved,
                                                    mailcore::Array * added,
                                                    mailcore::Array * modified,
                                                    mailcore::Array * modifiedIDs)
{
    int currentIndex = -1;
    mc_foreacharrayIndex(accountIdx, MailStorageView, currentView, mViews) {
        if (currentView == view) {
            currentIndex = accountIdx;
        }
    }
    MCAssert(currentIndex != -1);
    double currentTime = hermes::currentTime();
    mc_foreacharray(Value, vConvID, modifiedIDs) {
        UnifiedConversationID * convID = new UnifiedConversationID();
        convID->convID = vConvID->longLongValue();
        convID->accountIndex = currentIndex;
        mModifiedConversationsIDs->addObject(convID);
        MC_SAFE_RELEASE(convID);
    }
    if (currentTime - mLastUpdateTimestamp < UPDATE_DELAY) {
        scheduleUpdate(currentTime);
    }
    else {
        updateNow();
    }
}

void UnifiedMailStorageView::scheduleUpdate(double currentTime)
{
    if (mScheduledUpdate) {
        return;
    }
    mScheduledUpdate = true;
    performMethodAfterDelay((Object::Method) &UnifiedMailStorageView::updateNow, NULL, mLastUpdateTimestamp + UPDATE_DELAY - currentTime);
}

void UnifiedMailStorageView::updateNow()
{
    mScheduledUpdate = false;
    cancelDelayedPerformMethod((Object::Method) &UnifiedMailStorageView::updateNow, NULL);
    mLastUpdateTimestamp = hermes::currentTime();

    Array * updatedConversations = new Array();
    mc_foreacharrayIndex(accountIdx, MailStorageView, currentView, mViews) {
        for(unsigned int i = 0 ; i < currentView->conversationsCount() ; i ++) {
            HashMap * info = currentView->conversationsInfoAtIndex(i);
            info = (HashMap *) info->copy()->autorelease();
            info->setObjectForKey(MCSTR("account"), Value::valueWithUnsignedIntValue(accountIdx));
            updatedConversations->addObject(info);
        }
    }

    updatedConversations->sortArray(compareConversation, this);

    // compute diff
    Array * indexDeletions = new Array();
    Array * indexAdditions = new Array();
    Array * indexMoves = new Array();
    Array * indexModified = new Array();

    Array * lastConversationsIDs = new Array();
    {
        mc_foreacharray(HashMap, info, mConversations) {
            Value * vConvID = (Value *) info->objectForKey(MCSTR("id"));
            Value * vAccountIdx = (Value *) info->objectForKey(MCSTR("account"));
            UnifiedConversationID * convID = new UnifiedConversationID();
            convID->convID = vConvID->longLongValue();
            convID->accountIndex = vAccountIdx->unsignedIntValue();
            lastConversationsIDs->addObject(convID);
            MC_SAFE_RELEASE(convID);
        }
    }

    Array * updatedConversationsIDs = new Array();
    {
        mc_foreacharray(HashMap, info, updatedConversations) {
            Value * vConvID = (Value *) info->objectForKey(MCSTR("id"));
            Value * vAccountIdx = (Value *) info->objectForKey(MCSTR("account"));
            UnifiedConversationID * convID = new UnifiedConversationID();
            convID->convID = vConvID->longLongValue();
            convID->accountIndex = vAccountIdx->unsignedIntValue();
            updatedConversationsIDs->addObject(convID);
            MC_SAFE_RELEASE(convID);
        }
    }

    Set * lastConversationsIDsSet = new Set();
    lastConversationsIDsSet->addObjectsFromArray(lastConversationsIDs);
    Set * updatedConversationsIDsSet = new Set();
    updatedConversationsIDsSet->addObjectsFromArray(updatedConversationsIDs);

    Set * deletionsSet = new Set();
    {
        mc_foreacharray(UnifiedConversationID, vConvID, lastConversationsIDs) {
            if (!updatedConversationsIDsSet->containsObject(vConvID)) {
                deletionsSet->addObject(vConvID);
            }
        }
    }
    Set * additionsSet = new Set();
    {
        mc_foreacharray(UnifiedConversationID, vConvID, updatedConversationsIDs) {
            if (!lastConversationsIDsSet->containsObject(vConvID)) {
                additionsSet->addObject(vConvID);
            }
        }
    }

    // Deletions

    HashMap * /* UnifiedConversationID -> Value(int) */ beforeMovePositions = new HashMap();
    {
        for(int idx = (int) lastConversationsIDs->count() - 1 ; idx >= 0 ; idx --) {
            UnifiedConversationID * vConvID = (UnifiedConversationID *) lastConversationsIDs->objectAtIndex(idx);
            if (deletionsSet->containsObject(vConvID)) {
                indexDeletions->addObject(Value::valueWithIntValue(idx));
            }
        }
    }

    // Moves
    Array * /* UnifiedConversationID */ filteredUpdatedConversationsIDs = new Array();
    {
        mc_foreacharrayIndex(idx, UnifiedConversationID, vConvID, updatedConversationsIDs) {
            if (lastConversationsIDsSet->containsObject(vConvID)) {
                filteredUpdatedConversationsIDs->addObject(vConvID);
            }
        }
    }

    Array * /* UnifiedConversationID */ currentConvIDs = new Array();
    {
        mc_foreacharray(UnifiedConversationID, vConvID, lastConversationsIDs) {
            if (!deletionsSet->containsObject(vConvID)) {
                currentConvIDs->addObject(vConvID);
            }
        }
    }
    {
        mc_foreacharrayIndex(idx, UnifiedConversationID, vConvID, currentConvIDs) {
            beforeMovePositions->setObjectForKey(vConvID, Value::valueWithIntValue(idx));
        }
    }
    MCAssert(filteredUpdatedConversationsIDs->count() == currentConvIDs->count());
    {
        mc_foreacharrayIndex(idx, UnifiedConversationID, vConvID, filteredUpdatedConversationsIDs) {
            Value * vOldIndex = (Value *) beforeMovePositions->objectForKey(vConvID);
            if (vOldIndex->intValue() == idx) {
                continue;
            }
            Array * swapInfo = Array::array();
            swapInfo->addObject(vOldIndex);
            swapInfo->addObject(Value::valueWithIntValue(idx));
            indexMoves->addObject(swapInfo);

            if (idx + 1 != vOldIndex->intValue()) {
                swapInfo = Array::array();
                swapInfo->addObject(Value::valueWithIntValue(idx + 1));
                swapInfo->addObject(vOldIndex);
                indexMoves->addObject(swapInfo);
            }

            UnifiedConversationID * v1 = (UnifiedConversationID *) currentConvIDs->objectAtIndex(vOldIndex->intValue());
            v1->retain();
            UnifiedConversationID * v2 = (UnifiedConversationID *) currentConvIDs->objectAtIndex(idx);
            v2->retain();
            currentConvIDs->replaceObject(idx, v1);
            currentConvIDs->replaceObject(vOldIndex->intValue(), v2);
            v2->release();
            v1->release();

            beforeMovePositions->setObjectForKey(v2, vOldIndex);
            beforeMovePositions->setObjectForKey(v1, Value::valueWithIntValue(idx));
        }
    }

    MC_SAFE_RELEASE(currentConvIDs);
    MC_SAFE_RELEASE(filteredUpdatedConversationsIDs);

    // Additions

    {
        mc_foreacharrayIndex(idx, Value, vConvID, lastConversationsIDs) {
            if (mModifiedConversationsIDs->containsObject(vConvID)) {
                indexModified->addObject(Value::valueWithIntValue(idx));
            }
        }
    }

    MC_SAFE_REPLACE_RETAIN(Array, mConversations, updatedConversations);

    if (indexDeletions->count() + indexMoves->count() + indexAdditions->count() + indexModified->count() != 0) {
        notifyChangesToObserver(indexDeletions, indexMoves, indexAdditions, indexModified);
    }

    MC_SAFE_RELEASE(beforeMovePositions);

    MC_SAFE_RELEASE(additionsSet);
    MC_SAFE_RELEASE(deletionsSet);

    MC_SAFE_RELEASE(updatedConversationsIDsSet);
    MC_SAFE_RELEASE(lastConversationsIDsSet);

    MC_SAFE_RELEASE(updatedConversationsIDs);
    MC_SAFE_RELEASE(lastConversationsIDs);

    MC_SAFE_RELEASE(updatedConversations);

    MC_SAFE_RELEASE(indexDeletions);
    MC_SAFE_RELEASE(indexAdditions);
    MC_SAFE_RELEASE(indexMoves);
    MC_SAFE_RELEASE(indexModified);

    if (mModifiedConversationsIDs != NULL) {
#warning It happens for unknown reason. See http://example.com
        mModifiedConversationsIDs->removeAllObjects();
    }
}

void UnifiedMailStorageView::notifyChangesToObserver(Array * deleted, Array * moved, Array * added,
                                                     Array * modified)
{
    for(unsigned int i = 0 ; i < carray_count(mObservers) ; i ++) {
        UnifiedMailStorageViewObserver * observer = (UnifiedMailStorageViewObserver *) carray_get(mObservers, i);
        observer->mailStorageViewChanged(this, deleted, moved, added, modified);
    }
}


int CCPACSTrailingEdgeDevices::GetTrailingEdgeDeviceIndex(const std::string& UID) const
{
    for (int i=0; i < GetTrailingEdgeDeviceCount(); i++) {
        const std::string tmpUID(m_trailingEdgeDevices[i]->GetUID());
        if (tmpUID == UID) {
            return i+1;
        }
    }

    // UID not there
    throw CTiglError("Invalid UID in CCPACSTrailingEdgeDevices::GetTrailingEdgeDeviceIndex", TIGL_UID_ERROR);
}

/*
*
* Created: 2021-02-05 Jan Kleinert <example@nnthink.com>
*
*/
/**
* @file
* @brief  Implementation of CPACS trailing edge devices
*/

#include "CTiglError.h"
#include "CCPACSTrailingEdgeDevice.h"
#include "CCPACSTrailingEdgeDevices.h"

namespace tigl {

CCPACSTrailingEdgeDevices::CCPACSTrailingEdgeDevices(CCPACSControlSurfaces* parent, CTiglUIDManager* uidMgr)
    : generated::CPACSTrailingEdgeDevices(parent, uidMgr) {}

// Returns the total count of trailing edge devices in a configuration
int CCPACSTrailingEdgeDevices::GetTrailingEdgeDeviceCount() const
{
    return static_cast<int>(m_trailingEdgeDevices.size());
}

// Returns the trailing edge device for a given index.
CCPACSTrailingEdgeDevice& CCPACSTrailingEdgeDevices::GetTrailingEdgeDevice(int index) const
{
    index --;
    if (index < 0 || index >= GetTrailingEdgeDeviceCount()) {
        throw CTiglError("Invalid index in CCPACSTrailingEdgeDevices::GetTrailingEdgeDevice", TIGL_INDEX_ERROR);
    }
    return *m_trailingEdgeDevices[index];
}

// Returns the trailing edge device for a given UID.
CCPACSTrailingEdgeDevice& CCPACSTrailingEdgeDevices::GetTrailingEdgeDevice(const std::string& UID) const
{
    return *m_trailingEdgeDevices[GetTrailingEdgeDeviceIndex(UID)-1];
}

// Returns the trailing edge device index for a given UID.


bool DWARFDebugNames::NameIndex::dumpEntry(ScopedPrinter &W,
                                           uint32_t *Offset) const {
  uint32_t EntryId = *Offset;
  auto EntryOr = getEntry(Offset);
  if (!EntryOr) {
    handleAllErrors(EntryOr.takeError(), [](const SentinelError &) {},
                    [&W](const ErrorInfoBase &EI) { EI.log(W.startLine()); });
    return false;
  }

  DictScope EntryScope(W, ("Entry @ 0x" + Twine::utohexstr(EntryId)).str());
  EntryOr->dump(W);
  return true;
}

//                     The LLVM Compiler Infrastructure

#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h"

#include "llvm/ADT/SmallVector.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/DebugInfo/DWARF/DWARFRelocMap.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/DJB.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/raw_ostream.h"
#include <cstddef>
#include <cstdint>
#include <utility>

using namespace llvm;

namespace {
struct DwarfConstant {
  StringRef (*StringFn)(unsigned);
  StringRef Type;
  unsigned Value;
};

static raw_ostream &operator<<(raw_ostream &OS, const DwarfConstant &C) {
  StringRef Str = C.StringFn(C.Value);
  if (!Str.empty())
    return OS << Str;
  return OS << "DW_" << C.Type << "_Unknown_0x" << format("%x", C.Value);
}
} // namespace

static DwarfConstant formatTag(unsigned Tag) {
  return {dwarf::TagString, "TAG", Tag};
}

static DwarfConstant formatForm(unsigned Form) {
  return {dwarf::FormEncodingString, "FORM", Form};
}

static DwarfConstant formatIndex(unsigned Idx) {
  return {dwarf::IndexString, "IDX", Idx};
}

static DwarfConstant formatAtom(unsigned Atom) {
  return {dwarf::AtomTypeString, "ATOM", Atom};
}

DWARFAcceleratorTable::~DWARFAcceleratorTable() = default;

llvm::Error AppleAcceleratorTable::extract() {
  uint32_t Offset = 0;

  // Check that we can at least read the header.
  if (!AccelSection.isValidOffset(offsetof(Header, HeaderDataLength)+4))
    return make_error<StringError>("Section too small: cannot read header.",
                                   inconvertibleErrorCode());

  Hdr.Magic = AccelSection.getU32(&Offset);
  Hdr.Version = AccelSection.getU16(&Offset);
  Hdr.HashFunction = AccelSection.getU16(&Offset);
  Hdr.BucketCount = AccelSection.getU32(&Offset);
  Hdr.HashCount = AccelSection.getU32(&Offset);
  Hdr.HeaderDataLength = AccelSection.getU32(&Offset);

  // Check that we can read all the hashes and offsets from the
  // section (see SourceLevelDebugging.rst for the structure of the index).
  // We need to substract one because we're checking for an *offset* which is
  // equal to the size for an empty table and hence pointer after the section.
  if (!AccelSection.isValidOffset(sizeof(Hdr) + Hdr.HeaderDataLength +
                                  Hdr.BucketCount * 4 + Hdr.HashCount * 8 - 1))
    return make_error<StringError>(
        "Section too small: cannot read buckets and hashes.",
        inconvertibleErrorCode());

  HdrData.DIEOffsetBase = AccelSection.getU32(&Offset);
  uint32_t NumAtoms = AccelSection.getU32(&Offset);

  for (unsigned i = 0; i < NumAtoms; ++i) {
    uint16_t AtomType = AccelSection.getU16(&Offset);
    auto AtomForm = static_cast<dwarf::Form>(AccelSection.getU16(&Offset));
    HdrData.Atoms.push_back(std::make_pair(AtomType, AtomForm));
  }

  IsValid = true;
  return Error::success();
}

uint32_t AppleAcceleratorTable::getNumBuckets() { return Hdr.BucketCount; }
uint32_t AppleAcceleratorTable::getNumHashes() { return Hdr.HashCount; }
uint32_t AppleAcceleratorTable::getSizeHdr() { return sizeof(Hdr); }
uint32_t AppleAcceleratorTable::getHeaderDataLength() {
  return Hdr.HeaderDataLength;
}

ArrayRef<std::pair<AppleAcceleratorTable::HeaderData::AtomType,
                   AppleAcceleratorTable::HeaderData::Form>>
AppleAcceleratorTable::getAtomsDesc() {
  return HdrData.Atoms;
}

bool AppleAcceleratorTable::validateForms() {
  for (auto Atom : getAtomsDesc()) {
    DWARFFormValue FormValue(Atom.second);
    switch (Atom.first) {
    case dwarf::DW_ATOM_die_offset:
    case dwarf::DW_ATOM_die_tag:
    case dwarf::DW_ATOM_type_flags:
      if ((!FormValue.isFormClass(DWARFFormValue::FC_Constant) &&
           !FormValue.isFormClass(DWARFFormValue::FC_Flag)) ||
          FormValue.getForm() == dwarf::DW_FORM_sdata)
        return false;
      break;
    default:
      break;
    }
  }
  return true;
}

std::pair<uint32_t, dwarf::Tag>
AppleAcceleratorTable::readAtoms(uint32_t &HashDataOffset) {
  uint32_t DieOffset = dwarf::DW_INVALID_OFFSET;
  dwarf::Tag DieTag = dwarf::DW_TAG_null;
  DWARFFormParams FormParams = {Hdr.Version, 0, dwarf::DwarfFormat::DWARF32};

  for (auto Atom : getAtomsDesc()) {
    DWARFFormValue FormValue(Atom.second);
    FormValue.extractValue(AccelSection, &HashDataOffset, FormParams);
    switch (Atom.first) {
    case dwarf::DW_ATOM_die_offset:
      DieOffset = *FormValue.getAsUnsignedConstant();
      break;
    case dwarf::DW_ATOM_die_tag:
      DieTag = (dwarf::Tag)*FormValue.getAsUnsignedConstant();
      break;
    default:
      break;
    }
  }
  return {DieOffset, DieTag};
}

void AppleAcceleratorTable::Header::dump(ScopedPrinter &W) const {
  DictScope HeaderScope(W, "Header");
  W.printHex("Magic", Magic);
  W.printHex("Version", Version);
  W.printHex("Hash function", HashFunction);
  W.printNumber("Bucket count", BucketCount);
  W.printNumber("Hashes count", HashCount);
  W.printNumber("HeaderData length", HeaderDataLength);
}

bool AppleAcceleratorTable::dumpName(ScopedPrinter &W,
                                     SmallVectorImpl<DWARFFormValue> &AtomForms,
                                     uint32_t *DataOffset) const {
  DWARFFormParams FormParams = {Hdr.Version, 0, dwarf::DwarfFormat::DWARF32};
  uint32_t NameOffset = *DataOffset;
  if (!AccelSection.isValidOffsetForDataOfSize(*DataOffset, 4)) {
    W.printString("Incorrectly terminated list.");
    return false;
  }
  unsigned StringOffset = AccelSection.getRelocatedValue(4, DataOffset);
  if (!StringOffset)
    return false; // End of list

  DictScope NameScope(W, ("Name@0x" + Twine::utohexstr(NameOffset)).str());
  W.startLine() << format("String: 0x%08x", StringOffset);
  W.getOStream() << " \"" << StringSection.getCStr(&StringOffset) << "\"\n";

  unsigned NumData = AccelSection.getU32(DataOffset);
  for (unsigned Data = 0; Data < NumData; ++Data) {
    ListScope DataScope(W, ("Data " + Twine(Data)).str());
    unsigned i = 0;
    for (auto &Atom : AtomForms) {
      W.startLine() << format("Atom[%d]: ", i++);
      if (Atom.extractValue(AccelSection, DataOffset, FormParams))
        Atom.dump(W.getOStream());
      else
        W.getOStream() << "Error extracting the value";
      W.getOStream() << "\n";
    }
  }
  return true; // more entries follow
}

LLVM_DUMP_METHOD void AppleAcceleratorTable::dump(raw_ostream &OS) const {
  if (!IsValid)
    return;

  ScopedPrinter W(OS);

  Hdr.dump(W);

  W.printNumber("DIE offset base", HdrData.DIEOffsetBase);
  W.printNumber("Number of atoms", uint64_t(HdrData.Atoms.size()));
  SmallVector<DWARFFormValue, 3> AtomForms;
  {
    ListScope AtomsScope(W, "Atoms");
    unsigned i = 0;
    for (const auto &Atom : HdrData.Atoms) {
      DictScope AtomScope(W, ("Atom " + Twine(i++)).str());
      W.startLine() << "Type: " << formatAtom(Atom.first) << '\n';
      W.startLine() << "Form: " << formatForm(Atom.second) << '\n';
      AtomForms.push_back(DWARFFormValue(Atom.second));
    }
  }

  // Now go through the actual tables and dump them.
  uint32_t Offset = sizeof(Hdr) + Hdr.HeaderDataLength;
  unsigned HashesBase = Offset + Hdr.BucketCount * 4;
  unsigned OffsetsBase = HashesBase + Hdr.HashCount * 4;

  for (unsigned Bucket = 0; Bucket < Hdr.BucketCount; ++Bucket) {
    unsigned Index = AccelSection.getU32(&Offset);

    ListScope BucketScope(W, ("Bucket " + Twine(Bucket)).str());
    if (Index == UINT32_MAX) {
      W.printString("EMPTY");
      continue;
    }

    for (unsigned HashIdx = Index; HashIdx < Hdr.HashCount; ++HashIdx) {
      unsigned HashOffset = HashesBase + HashIdx*4;
      unsigned OffsetsOffset = OffsetsBase + HashIdx*4;
      uint32_t Hash = AccelSection.getU32(&HashOffset);

      if (Hash % Hdr.BucketCount != Bucket)
        break;

      unsigned DataOffset = AccelSection.getU32(&OffsetsOffset);
      ListScope HashScope(W, ("Hash 0x" + Twine::utohexstr(Hash)).str());
      if (!AccelSection.isValidOffset(DataOffset)) {
        W.printString("Invalid section offset");
        continue;
      }
      while (dumpName(W, AtomForms, &DataOffset))
        /*empty*/;
    }
  }
}

AppleAcceleratorTable::ValueIterator::ValueIterator(
    const AppleAcceleratorTable &AccelTable, unsigned Offset)
    : AccelTable(&AccelTable), DataOffset(Offset) {
  if (!AccelTable.AccelSection.isValidOffsetForDataOfSize(DataOffset, 4))
    return;

  for (const auto &Atom : AccelTable.HdrData.Atoms)
    AtomForms.push_back(DWARFFormValue(Atom.second));

  // Read the first entry.
  NumData = AccelTable.AccelSection.getU32(&DataOffset);
  Next();
}

void AppleAcceleratorTable::ValueIterator::Next() {
  assert(NumData > 0 && "attempted to increment iterator past the end");
  auto &AccelSection = AccelTable->AccelSection;
  if (Data >= NumData ||
      !AccelSection.isValidOffsetForDataOfSize(DataOffset, 4)) {
    NumData = 0;
    return;
  }
  DWARFFormParams FormParams = {AccelTable->Hdr.Version, 0,
                                dwarf::DwarfFormat::DWARF32};
  for (auto &Atom : AtomForms)
    Atom.extractValue(AccelSection, &DataOffset, FormParams);
  ++Data;
}

iterator_range<AppleAcceleratorTable::ValueIterator>
AppleAcceleratorTable::equal_range(StringRef Key) const {
  if (!IsValid)
    return make_range(ValueIterator(), ValueIterator());

  // Find the bucket.
  unsigned HashValue = djbHash(Key);
  unsigned Bucket = HashValue % Hdr.BucketCount;
  unsigned BucketBase = sizeof(Hdr) + Hdr.HeaderDataLength;
  unsigned HashesBase = BucketBase + Hdr.BucketCount * 4;
  unsigned OffsetsBase = HashesBase + Hdr.HashCount * 4;

  unsigned BucketOffset = BucketBase + Bucket * 4;
  unsigned Index = AccelSection.getU32(&BucketOffset);

  // Search through all hashes in the bucket.
  for (unsigned HashIdx = Index; HashIdx < Hdr.HashCount; ++HashIdx) {
    unsigned HashOffset = HashesBase + HashIdx * 4;
    unsigned OffsetsOffset = OffsetsBase + HashIdx * 4;
    uint32_t Hash = AccelSection.getU32(&HashOffset);

    if (Hash % Hdr.BucketCount != Bucket)
      // We are already in the next bucket.
      break;

    unsigned DataOffset = AccelSection.getU32(&OffsetsOffset);
    unsigned StringOffset = AccelSection.getRelocatedValue(4, &DataOffset);
    if (!StringOffset)
      break;

    // Finally, compare the key.
    if (Key == StringSection.getCStr(&StringOffset))
      return make_range({*this, DataOffset}, ValueIterator());
  }
  return make_range(ValueIterator(), ValueIterator());
}

void DWARFDebugNames::Header::dump(ScopedPrinter &W) const {
  DictScope HeaderScope(W, "Header");
  W.printHex("Length", UnitLength);
  W.printNumber("Version", Version);
  W.printHex("Padding", Padding);
  W.printNumber("CU count", CompUnitCount);
  W.printNumber("Local TU count", LocalTypeUnitCount);
  W.printNumber("Foreign TU count", ForeignTypeUnitCount);
  W.printNumber("Bucket count", BucketCount);
  W.printNumber("Name count", NameCount);
  W.printHex("Abbreviations table size", AbbrevTableSize);
  W.startLine() << "Augmentation: '" << AugmentationString << "'\n";
}

llvm::Error DWARFDebugNames::Header::extract(const DWARFDataExtractor &AS,
                                             uint32_t *Offset) {
  // Check that we can read the fixed-size part.
  if (!AS.isValidOffset(*Offset + sizeof(HeaderPOD) - 1))
    return make_error<StringError>("Section too small: cannot read header.",
                                   inconvertibleErrorCode());

  UnitLength = AS.getU32(Offset);
  Version = AS.getU16(Offset);
  Padding = AS.getU16(Offset);
  CompUnitCount = AS.getU32(Offset);
  LocalTypeUnitCount = AS.getU32(Offset);
  ForeignTypeUnitCount = AS.getU32(Offset);
  BucketCount = AS.getU32(Offset);
  NameCount = AS.getU32(Offset);
  AbbrevTableSize = AS.getU32(Offset);
  AugmentationStringSize = AS.getU32(Offset);

  if (!AS.isValidOffsetForDataOfSize(*Offset, AugmentationStringSize))
    return make_error<StringError>(
        "Section too small: cannot read header augmentation.",
        inconvertibleErrorCode());
  AugmentationString.resize(AugmentationStringSize);
  AS.getU8(Offset, reinterpret_cast<uint8_t *>(AugmentationString.data()),
           AugmentationStringSize);
  *Offset = alignTo(*Offset, 4);
  return Error::success();
}

void DWARFDebugNames::Abbrev::dump(ScopedPrinter &W) const {
  DictScope AbbrevScope(W, ("Abbreviation 0x" + Twine::utohexstr(Code)).str());
  W.startLine() << "Tag: " << formatTag(Tag) << '\n';

  for (const auto &Attr : Attributes) {
    W.startLine() << formatIndex(Attr.Index) << ": " << formatForm(Attr.Form)
                  << '\n';
  }
}

static constexpr DWARFDebugNames::AttributeEncoding sentinelAttrEnc() {
  return {dwarf::Index(0), dwarf::Form(0)};
}

static bool isSentinel(const DWARFDebugNames::AttributeEncoding &AE) {
  return AE == sentinelAttrEnc();
}

static DWARFDebugNames::Abbrev sentinelAbbrev() {
  return DWARFDebugNames::Abbrev(0, dwarf::Tag(0), {});
}

static bool isSentinel(const DWARFDebugNames::Abbrev &Abbr) {
  return Abbr.Code == 0;
}

DWARFDebugNames::Abbrev DWARFDebugNames::AbbrevMapInfo::getEmptyKey() {
  return sentinelAbbrev();
}

DWARFDebugNames::Abbrev DWARFDebugNames::AbbrevMapInfo::getTombstoneKey() {
  return DWARFDebugNames::Abbrev(~0, dwarf::Tag(0), {});
}

Expected<DWARFDebugNames::AttributeEncoding>
DWARFDebugNames::NameIndex::extractAttributeEncoding(uint32_t *Offset) {
  if (*Offset >= EntriesBase) {
    return make_error<StringError>("Incorrectly terminated abbreviation table.",
                                   inconvertibleErrorCode());
  }

  uint32_t Index = Section.AccelSection.getULEB128(Offset);
  uint32_t Form = Section.AccelSection.getULEB128(Offset);
  return AttributeEncoding(dwarf::Index(Index), dwarf::Form(Form));
}

Expected<std::vector<DWARFDebugNames::AttributeEncoding>>
DWARFDebugNames::NameIndex::extractAttributeEncodings(uint32_t *Offset) {
  std::vector<AttributeEncoding> Result;
  for (;;) {
    auto AttrEncOr = extractAttributeEncoding(Offset);
    if (!AttrEncOr)
      return AttrEncOr.takeError();
    if (isSentinel(*AttrEncOr))
      return std::move(Result);

    Result.emplace_back(*AttrEncOr);
  }
}

Expected<DWARFDebugNames::Abbrev>
DWARFDebugNames::NameIndex::extractAbbrev(uint32_t *Offset) {
  if (*Offset >= EntriesBase) {
    return make_error<StringError>("Incorrectly terminated abbreviation table.",
                                   inconvertibleErrorCode());
  }

  uint32_t Code = Section.AccelSection.getULEB128(Offset);
  if (Code == 0)
    return sentinelAbbrev();

  uint32_t Tag = Section.AccelSection.getULEB128(Offset);
  auto AttrEncOr = extractAttributeEncodings(Offset);
  if (!AttrEncOr)
    return AttrEncOr.takeError();
  return Abbrev(Code, dwarf::Tag(Tag), std::move(*AttrEncOr));
}

Error DWARFDebugNames::NameIndex::extract() {
  const DWARFDataExtractor &AS = Section.AccelSection;
  uint32_t Offset = Base;
  if (Error E = Hdr.extract(AS, &Offset))
    return E;

  CUsBase = Offset;
  Offset += Hdr.CompUnitCount * 4;
  Offset += Hdr.LocalTypeUnitCount * 4;
  Offset += Hdr.ForeignTypeUnitCount * 8;
  BucketsBase = Offset;
  Offset += Hdr.BucketCount * 4;
  HashesBase = Offset;
  if (Hdr.BucketCount > 0)
    Offset += Hdr.NameCount * 4;
  StringOffsetsBase = Offset;
  Offset += Hdr.NameCount * 4;
  EntryOffsetsBase = Offset;
  Offset += Hdr.NameCount * 4;

  if (!AS.isValidOffsetForDataOfSize(Offset, Hdr.AbbrevTableSize))
    return make_error<StringError>(
        "Section too small: cannot read abbreviations.",
        inconvertibleErrorCode());

  EntriesBase = Offset + Hdr.AbbrevTableSize;

  for (;;) {
    auto AbbrevOr = extractAbbrev(&Offset);
    if (!AbbrevOr)
      return AbbrevOr.takeError();
    if (isSentinel(*AbbrevOr))
      return Error::success();

    if (!Abbrevs.insert(std::move(*AbbrevOr)).second) {
      return make_error<StringError>("Duplicate abbreviation code.",
                                     inconvertibleErrorCode());
    }
  }
}

DWARFDebugNames::Entry::Entry(const Abbrev &Abbr) : Abbr(Abbr) {
  // This merely creates form values. It is up to the caller
  // (NameIndex::getEntry) to populate them.
  Values.reserve(Abbr.Attributes.size());
  for (const auto &Attr : Abbr.Attributes)
    Values.emplace_back(Attr.Form);
}

void DWARFDebugNames::Entry::dump(ScopedPrinter &W) const {
  W.printHex("Abbrev", Abbr.Code);
  W.startLine() << "Tag: " << formatTag(Abbr.Tag) << "\n";

  assert(Abbr.Attributes.size() == Values.size());
  for (uint32_t I = 0, E = Values.size(); I < E; ++I) {
    W.startLine() << formatIndex(Abbr.Attributes[I].Index) << ": ";
    Values[I].dump(W.getOStream());
    W.getOStream() << '\n';
  }
}

char DWARFDebugNames::SentinelError::ID;
std::error_code DWARFDebugNames::SentinelError::convertToErrorCode() const {
  return inconvertibleErrorCode();
}

uint32_t DWARFDebugNames::NameIndex::getCUOffset(uint32_t CU) const {
  assert(CU < Hdr.CompUnitCount);
  uint32_t Offset = CUsBase + 4 * CU;
  return Section.AccelSection.getRelocatedValue(4, &Offset);
}

uint32_t DWARFDebugNames::NameIndex::getLocalTUOffset(uint32_t TU) const {
  assert(TU < Hdr.LocalTypeUnitCount);
  uint32_t Offset = CUsBase + Hdr.CompUnitCount * 4;
  return Section.AccelSection.getRelocatedValue(4, &Offset);
}

uint64_t DWARFDebugNames::NameIndex::getForeignTUOffset(uint32_t TU) const {
  assert(TU < Hdr.ForeignTypeUnitCount);
  uint32_t Offset = CUsBase + (Hdr.CompUnitCount + Hdr.LocalTypeUnitCount) * 4;
  return Section.AccelSection.getU64(&Offset);
}

Expected<DWARFDebugNames::Entry>
DWARFDebugNames::NameIndex::getEntry(uint32_t *Offset) const {
  const DWARFDataExtractor &AS = Section.AccelSection;
  if (!AS.isValidOffset(*Offset))
    return make_error<StringError>("Incorrectly terminated entry list",
                                   inconvertibleErrorCode());

  uint32_t AbbrevCode = AS.getULEB128(Offset);
  if (AbbrevCode == 0)
    return make_error<SentinelError>();

  const auto AbbrevIt = Abbrevs.find_as(AbbrevCode);
  if (AbbrevIt == Abbrevs.end())
    return make_error<StringError>("Invalid abbreviation",
                                   inconvertibleErrorCode());

  Entry E(*AbbrevIt);

  DWARFFormParams FormParams = {Hdr.Version, 0, dwarf::DwarfFormat::DWARF32};
  for (auto &Value : E.Values) {
    if (!Value.extractValue(AS, Offset, FormParams))
      return make_error<StringError>("Error extracting index attribute values",
                                     inconvertibleErrorCode());
  }
  return std::move(E);
}

DWARFDebugNames::NameTableEntry
DWARFDebugNames::NameIndex::getNameTableEntry(uint32_t Index) const {
  assert(0 < Index && Index <= Hdr.NameCount);
  uint32_t StringOffsetOffset = StringOffsetsBase + 4 * (Index - 1);
  uint32_t EntryOffsetOffset = EntryOffsetsBase + 4 * (Index - 1);
  const DWARFDataExtractor &AS = Section.AccelSection;

  uint32_t StringOffset = AS.getRelocatedValue(4, &StringOffsetOffset);
  uint32_t EntryOffset = AS.getU32(&EntryOffsetOffset);
  EntryOffset += EntriesBase;
  return {StringOffset, EntryOffset};
}

uint32_t
DWARFDebugNames::NameIndex::getBucketArrayEntry(uint32_t Bucket) const {
  assert(Bucket < Hdr.BucketCount);
  uint32_t BucketOffset = BucketsBase + 4 * Bucket;
  return Section.AccelSection.getU32(&BucketOffset);
}

uint32_t DWARFDebugNames::NameIndex::getHashArrayEntry(uint32_t Index) const {
  assert(0 < Index && Index <= Hdr.NameCount);
  uint32_t HashOffset = HashesBase + 4 * (Index - 1);
  return Section.AccelSection.getU32(&HashOffset);
}

// Returns true if we should continue scanning for entries, false if this is the
// last (sentinel) entry). In case of a parsing error we also return false, as
// it's not possible to recover this entry list (but the other lists may still

void DWARFDebugNames::NameIndex::dumpName(ScopedPrinter &W, uint32_t Index,
                                          Optional<uint32_t> Hash) const {
  const DataExtractor &SS = Section.StringSection;
  NameTableEntry NTE = getNameTableEntry(Index);

  DictScope NameScope(W, ("Name " + Twine(Index)).str());
  if (Hash)
    W.printHex("Hash", *Hash);

  W.startLine() << format("String: 0x%08x", NTE.StringOffset);
  W.getOStream() << " \"" << SS.getCStr(&NTE.StringOffset) << "\"\n";

  while (dumpEntry(W, &NTE.EntryOffset))
    /*empty*/;
}

void DWARFDebugNames::NameIndex::dumpCUs(ScopedPrinter &W) const {
  ListScope CUScope(W, "Compilation Unit offsets");
  for (uint32_t CU = 0; CU < Hdr.CompUnitCount; ++CU)
    W.startLine() << format("CU[%u]: 0x%08x\n", CU, getCUOffset(CU));
}

void DWARFDebugNames::NameIndex::dumpLocalTUs(ScopedPrinter &W) const {
  if (Hdr.LocalTypeUnitCount == 0)
    return;

  ListScope TUScope(W, "Local Type Unit offsets");
  for (uint32_t TU = 0; TU < Hdr.LocalTypeUnitCount; ++TU)
    W.startLine() << format("LocalTU[%u]: 0x%08x\n", TU, getLocalTUOffset(TU));
}

void DWARFDebugNames::NameIndex::dumpForeignTUs(ScopedPrinter &W) const {
  if (Hdr.ForeignTypeUnitCount == 0)
    return;

  ListScope TUScope(W, "Foreign Type Unit signatures");
  for (uint32_t TU = 0; TU < Hdr.ForeignTypeUnitCount; ++TU) {
    W.startLine() << format("ForeignTU[%u]: 0x%016" PRIx64 "\n", TU,
                            getForeignTUOffset(TU));
  }
}

void DWARFDebugNames::NameIndex::dumpAbbreviations(ScopedPrinter &W) const {
  ListScope AbbrevsScope(W, "Abbreviations");
  for (const auto &Abbr : Abbrevs)
    Abbr.dump(W);
}

void DWARFDebugNames::NameIndex::dumpBucket(ScopedPrinter &W,
                                            uint32_t Bucket) const {
  ListScope BucketScope(W, ("Bucket " + Twine(Bucket)).str());
  uint32_t Index = getBucketArrayEntry(Bucket);
  if (Index == 0) {
    W.printString("EMPTY");
    return;
  }
  if (Index > Hdr.NameCount) {
    W.printString("Name index is invalid");
    return;
  }

  for (; Index <= Hdr.NameCount; ++Index) {
    uint32_t Hash = getHashArrayEntry(Index);
    if (Hash % Hdr.BucketCount != Bucket)
      break;

    dumpName(W, Index, Hash);
  }
}

LLVM_DUMP_METHOD void DWARFDebugNames::NameIndex::dump(ScopedPrinter &W) const {
  DictScope UnitScope(W, ("Name Index @ 0x" + Twine::utohexstr(Base)).str());
  Hdr.dump(W);
  dumpCUs(W);
  dumpLocalTUs(W);
  dumpForeignTUs(W);
  dumpAbbreviations(W);

  if (Hdr.BucketCount > 0) {
    for (uint32_t Bucket = 0; Bucket < Hdr.BucketCount; ++Bucket)
      dumpBucket(W, Bucket);
    return;
  }

  W.startLine() << "Hash table not present\n";
  for (uint32_t Index = 1; Index <= Hdr.NameCount; ++Index)
    dumpName(W, Index, None);
}

llvm::Error DWARFDebugNames::extract() {
  uint32_t Offset = 0;
  while (AccelSection.isValidOffset(Offset)) {
    NameIndex Next(*this, Offset);
    if (llvm::Error E = Next.extract())
      return E;
    Offset = Next.getNextUnitOffset();
    NameIndices.push_back(std::move(Next));
  }
  return Error::success();
}

LLVM_DUMP_METHOD void DWARFDebugNames::dump(raw_ostream &OS) const {
  ScopedPrinter W(OS);
  for (const NameIndex &NI : NameIndices)
    NI.dump(W);
}


switch (Atom.first) {
    case dwarf::DW_ATOM_die_offset:
    case dwarf::DW_ATOM_die_tag:
    case dwarf::DW_ATOM_type_flags:
      if ((!FormValue.isFormClass(DWARFFormValue::FC_Constant) &&
           !FormValue.isFormClass(DWARFFormValue::FC_Flag)) ||
          FormValue.getForm() == dwarf::DW_FORM_sdata)
        return false;
      break;
    default:
      break;
    }

//                     The LLVM Compiler Infrastructure

#include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h"

#include "llvm/ADT/SmallVector.h"
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/DebugInfo/DWARF/DWARFRelocMap.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/DJB.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/raw_ostream.h"
#include <cstddef>
#include <cstdint>
#include <utility>

using namespace llvm;

namespace {
struct DwarfConstant {
  StringRef (*StringFn)(unsigned);
  StringRef Type;
  unsigned Value;
};

static raw_ostream &operator<<(raw_ostream &OS, const DwarfConstant &C) {
  StringRef Str = C.StringFn(C.Value);
  if (!Str.empty())
    return OS << Str;
  return OS << "DW_" << C.Type << "_Unknown_0x" << format("%x", C.Value);
}
} // namespace

static DwarfConstant formatTag(unsigned Tag) {
  return {dwarf::TagString, "TAG", Tag};
}

static DwarfConstant formatForm(unsigned Form) {
  return {dwarf::FormEncodingString, "FORM", Form};
}

static DwarfConstant formatIndex(unsigned Idx) {
  return {dwarf::IndexString, "IDX", Idx};
}

static DwarfConstant formatAtom(unsigned Atom) {
  return {dwarf::AtomTypeString, "ATOM", Atom};
}

DWARFAcceleratorTable::~DWARFAcceleratorTable() = default;

llvm::Error AppleAcceleratorTable::extract() {
  uint32_t Offset = 0;

  // Check that we can at least read the header.
  if (!AccelSection.isValidOffset(offsetof(Header, HeaderDataLength)+4))
    return make_error<StringError>("Section too small: cannot read header.",
                                   inconvertibleErrorCode());

  Hdr.Magic = AccelSection.getU32(&Offset);
  Hdr.Version = AccelSection.getU16(&Offset);
  Hdr.HashFunction = AccelSection.getU16(&Offset);
  Hdr.BucketCount = AccelSection.getU32(&Offset);
  Hdr.HashCount = AccelSection.getU32(&Offset);
  Hdr.HeaderDataLength = AccelSection.getU32(&Offset);

  // Check that we can read all the hashes and offsets from the
  // section (see SourceLevelDebugging.rst for the structure of the index).
  // We need to substract one because we're checking for an *offset* which is
  // equal to the size for an empty table and hence pointer after the section.
  if (!AccelSection.isValidOffset(sizeof(Hdr) + Hdr.HeaderDataLength +
                                  Hdr.BucketCount * 4 + Hdr.HashCount * 8 - 1))
    return make_error<StringError>(
        "Section too small: cannot read buckets and hashes.",
        inconvertibleErrorCode());

  HdrData.DIEOffsetBase = AccelSection.getU32(&Offset);
  uint32_t NumAtoms = AccelSection.getU32(&Offset);

  for (unsigned i = 0; i < NumAtoms; ++i) {
    uint16_t AtomType = AccelSection.getU16(&Offset);
    auto AtomForm = static_cast<dwarf::Form>(AccelSection.getU16(&Offset));
    HdrData.Atoms.push_back(std::make_pair(AtomType, AtomForm));
  }

  IsValid = true;
  return Error::success();
}

uint32_t AppleAcceleratorTable::getNumBuckets() { return Hdr.BucketCount; }
uint32_t AppleAcceleratorTable::getNumHashes() { return Hdr.HashCount; }
uint32_t AppleAcceleratorTable::getSizeHdr() { return sizeof(Hdr); }
uint32_t AppleAcceleratorTable::getHeaderDataLength() {
  return Hdr.HeaderDataLength;
}

ArrayRef<std::pair<AppleAcceleratorTable::HeaderData::AtomType,
                   AppleAcceleratorTable::HeaderData::Form>>
AppleAcceleratorTable::getAtomsDesc() {
  return HdrData.Atoms;
}

bool AppleAcceleratorTable::validateForms() {
  for (auto Atom : getAtomsDesc()) {
    DWARFFormValue FormValue(Atom.second);

  }
  return true;
}

std::pair<uint32_t, dwarf::Tag>
AppleAcceleratorTable::readAtoms(uint32_t &HashDataOffset) {
  uint32_t DieOffset = dwarf::DW_INVALID_OFFSET;
  dwarf::Tag DieTag = dwarf::DW_TAG_null;
  DWARFFormParams FormParams = {Hdr.Version, 0, dwarf::DwarfFormat::DWARF32};

  for (auto Atom : getAtomsDesc()) {
    DWARFFormValue FormValue(Atom.second);
    FormValue.extractValue(AccelSection, &HashDataOffset, FormParams);
    switch (Atom.first) {
    case dwarf::DW_ATOM_die_offset:
      DieOffset = *FormValue.getAsUnsignedConstant();
      break;
    case dwarf::DW_ATOM_die_tag:
      DieTag = (dwarf::Tag)*FormValue.getAsUnsignedConstant();
      break;
    default:
      break;
    }
  }
  return {DieOffset, DieTag};
}

void AppleAcceleratorTable::Header::dump(ScopedPrinter &W) const {
  DictScope HeaderScope(W, "Header");
  W.printHex("Magic", Magic);
  W.printHex("Version", Version);
  W.printHex("Hash function", HashFunction);
  W.printNumber("Bucket count", BucketCount);
  W.printNumber("Hashes count", HashCount);
  W.printNumber("HeaderData length", HeaderDataLength);
}

bool AppleAcceleratorTable::dumpName(ScopedPrinter &W,
                                     SmallVectorImpl<DWARFFormValue> &AtomForms,
                                     uint32_t *DataOffset) const {
  DWARFFormParams FormParams = {Hdr.Version, 0, dwarf::DwarfFormat::DWARF32};
  uint32_t NameOffset = *DataOffset;
  if (!AccelSection.isValidOffsetForDataOfSize(*DataOffset, 4)) {
    W.printString("Incorrectly terminated list.");
    return false;
  }
  unsigned StringOffset = AccelSection.getRelocatedValue(4, DataOffset);
  if (!StringOffset)
    return false; // End of list

  DictScope NameScope(W, ("Name@0x" + Twine::utohexstr(NameOffset)).str());
  W.startLine() << format("String: 0x%08x", StringOffset);
  W.getOStream() << " \"" << StringSection.getCStr(&StringOffset) << "\"\n";

  unsigned NumData = AccelSection.getU32(DataOffset);
  for (unsigned Data = 0; Data < NumData; ++Data) {
    ListScope DataScope(W, ("Data " + Twine(Data)).str());
    unsigned i = 0;
    for (auto &Atom : AtomForms) {
      W.startLine() << format("Atom[%d]: ", i++);
      if (Atom.extractValue(AccelSection, DataOffset, FormParams))
        Atom.dump(W.getOStream());
      else
        W.getOStream() << "Error extracting the value";
      W.getOStream() << "\n";
    }
  }
  return true; // more entries follow
}

LLVM_DUMP_METHOD void AppleAcceleratorTable::dump(raw_ostream &OS) const {
  if (!IsValid)
    return;

  ScopedPrinter W(OS);

  Hdr.dump(W);

  W.printNumber("DIE offset base", HdrData.DIEOffsetBase);
  W.printNumber("Number of atoms", uint64_t(HdrData.Atoms.size()));
  SmallVector<DWARFFormValue, 3> AtomForms;
  {
    ListScope AtomsScope(W, "Atoms");
    unsigned i = 0;
    for (const auto &Atom : HdrData.Atoms) {
      DictScope AtomScope(W, ("Atom " + Twine(i++)).str());
      W.startLine() << "Type: " << formatAtom(Atom.first) << '\n';
      W.startLine() << "Form: " << formatForm(Atom.second) << '\n';
      AtomForms.push_back(DWARFFormValue(Atom.second));
    }
  }

  // Now go through the actual tables and dump them.
  uint32_t Offset = sizeof(Hdr) + Hdr.HeaderDataLength;
  unsigned HashesBase = Offset + Hdr.BucketCount * 4;
  unsigned OffsetsBase = HashesBase + Hdr.HashCount * 4;

  for (unsigned Bucket = 0; Bucket < Hdr.BucketCount; ++Bucket) {
    unsigned Index = AccelSection.getU32(&Offset);

    ListScope BucketScope(W, ("Bucket " + Twine(Bucket)).str());
    if (Index == UINT32_MAX) {
      W.printString("EMPTY");
      continue;
    }

    for (unsigned HashIdx = Index; HashIdx < Hdr.HashCount; ++HashIdx) {
      unsigned HashOffset = HashesBase + HashIdx*4;
      unsigned OffsetsOffset = OffsetsBase + HashIdx*4;
      uint32_t Hash = AccelSection.getU32(&HashOffset);

      if (Hash % Hdr.BucketCount != Bucket)
        break;

      unsigned DataOffset = AccelSection.getU32(&OffsetsOffset);
      ListScope HashScope(W, ("Hash 0x" + Twine::utohexstr(Hash)).str());
      if (!AccelSection.isValidOffset(DataOffset)) {
        W.printString("Invalid section offset");
        continue;
      }
      while (dumpName(W, AtomForms, &DataOffset))
        /*empty*/;
    }
  }
}

AppleAcceleratorTable::ValueIterator::ValueIterator(
    const AppleAcceleratorTable &AccelTable, unsigned Offset)
    : AccelTable(&AccelTable), DataOffset(Offset) {
  if (!AccelTable.AccelSection.isValidOffsetForDataOfSize(DataOffset, 4))
    return;

  for (const auto &Atom : AccelTable.HdrData.Atoms)
    AtomForms.push_back(DWARFFormValue(Atom.second));

  // Read the first entry.
  NumData = AccelTable.AccelSection.getU32(&DataOffset);
  Next();
}

void AppleAcceleratorTable::ValueIterator::Next() {
  assert(NumData > 0 && "attempted to increment iterator past the end");
  auto &AccelSection = AccelTable->AccelSection;
  if (Data >= NumData ||
      !AccelSection.isValidOffsetForDataOfSize(DataOffset, 4)) {
    NumData = 0;
    return;
  }
  DWARFFormParams FormParams = {AccelTable->Hdr.Version, 0,
                                dwarf::DwarfFormat::DWARF32};
  for (auto &Atom : AtomForms)
    Atom.extractValue(AccelSection, &DataOffset, FormParams);
  ++Data;
}

iterator_range<AppleAcceleratorTable::ValueIterator>
AppleAcceleratorTable::equal_range(StringRef Key) const {
  if (!IsValid)
    return make_range(ValueIterator(), ValueIterator());

  // Find the bucket.
  unsigned HashValue = djbHash(Key);
  unsigned Bucket = HashValue % Hdr.BucketCount;
  unsigned BucketBase = sizeof(Hdr) + Hdr.HeaderDataLength;
  unsigned HashesBase = BucketBase + Hdr.BucketCount * 4;
  unsigned OffsetsBase = HashesBase + Hdr.HashCount * 4;

  unsigned BucketOffset = BucketBase + Bucket * 4;
  unsigned Index = AccelSection.getU32(&BucketOffset);

  // Search through all hashes in the bucket.
  for (unsigned HashIdx = Index; HashIdx < Hdr.HashCount; ++HashIdx) {
    unsigned HashOffset = HashesBase + HashIdx * 4;
    unsigned OffsetsOffset = OffsetsBase + HashIdx * 4;
    uint32_t Hash = AccelSection.getU32(&HashOffset);

    if (Hash % Hdr.BucketCount != Bucket)
      // We are already in the next bucket.
      break;

    unsigned DataOffset = AccelSection.getU32(&OffsetsOffset);
    unsigned StringOffset = AccelSection.getRelocatedValue(4, &DataOffset);
    if (!StringOffset)
      break;

    // Finally, compare the key.
    if (Key == StringSection.getCStr(&StringOffset))
      return make_range({*this, DataOffset}, ValueIterator());
  }
  return make_range(ValueIterator(), ValueIterator());
}

void DWARFDebugNames::Header::dump(ScopedPrinter &W) const {
  DictScope HeaderScope(W, "Header");
  W.printHex("Length", UnitLength);
  W.printNumber("Version", Version);
  W.printHex("Padding", Padding);
  W.printNumber("CU count", CompUnitCount);
  W.printNumber("Local TU count", LocalTypeUnitCount);
  W.printNumber("Foreign TU count", ForeignTypeUnitCount);
  W.printNumber("Bucket count", BucketCount);
  W.printNumber("Name count", NameCount);
  W.printHex("Abbreviations table size", AbbrevTableSize);
  W.startLine() << "Augmentation: '" << AugmentationString << "'\n";
}

llvm::Error DWARFDebugNames::Header::extract(const DWARFDataExtractor &AS,
                                             uint32_t *Offset) {
  // Check that we can read the fixed-size part.
  if (!AS.isValidOffset(*Offset + sizeof(HeaderPOD) - 1))
    return make_error<StringError>("Section too small: cannot read header.",
                                   inconvertibleErrorCode());

  UnitLength = AS.getU32(Offset);
  Version = AS.getU16(Offset);
  Padding = AS.getU16(Offset);
  CompUnitCount = AS.getU32(Offset);
  LocalTypeUnitCount = AS.getU32(Offset);
  ForeignTypeUnitCount = AS.getU32(Offset);
  BucketCount = AS.getU32(Offset);
  NameCount = AS.getU32(Offset);
  AbbrevTableSize = AS.getU32(Offset);
  AugmentationStringSize = AS.getU32(Offset);

  if (!AS.isValidOffsetForDataOfSize(*Offset, AugmentationStringSize))
    return make_error<StringError>(
        "Section too small: cannot read header augmentation.",
        inconvertibleErrorCode());
  AugmentationString.resize(AugmentationStringSize);
  AS.getU8(Offset, reinterpret_cast<uint8_t *>(AugmentationString.data()),
           AugmentationStringSize);
  *Offset = alignTo(*Offset, 4);
  return Error::success();
}

void DWARFDebugNames::Abbrev::dump(ScopedPrinter &W) const {
  DictScope AbbrevScope(W, ("Abbreviation 0x" + Twine::utohexstr(Code)).str());
  W.startLine() << "Tag: " << formatTag(Tag) << '\n';

  for (const auto &Attr : Attributes) {
    W.startLine() << formatIndex(Attr.Index) << ": " << formatForm(Attr.Form)
                  << '\n';
  }
}

static constexpr DWARFDebugNames::AttributeEncoding sentinelAttrEnc() {
  return {dwarf::Index(0), dwarf::Form(0)};
}

static bool isSentinel(const DWARFDebugNames::AttributeEncoding &AE) {
  return AE == sentinelAttrEnc();
}

static DWARFDebugNames::Abbrev sentinelAbbrev() {
  return DWARFDebugNames::Abbrev(0, dwarf::Tag(0), {});
}

static bool isSentinel(const DWARFDebugNames::Abbrev &Abbr) {
  return Abbr.Code == 0;
}

DWARFDebugNames::Abbrev DWARFDebugNames::AbbrevMapInfo::getEmptyKey() {
  return sentinelAbbrev();
}

DWARFDebugNames::Abbrev DWARFDebugNames::AbbrevMapInfo::getTombstoneKey() {
  return DWARFDebugNames::Abbrev(~0, dwarf::Tag(0), {});
}

Expected<DWARFDebugNames::AttributeEncoding>
DWARFDebugNames::NameIndex::extractAttributeEncoding(uint32_t *Offset) {
  if (*Offset >= EntriesBase) {
    return make_error<StringError>("Incorrectly terminated abbreviation table.",
                                   inconvertibleErrorCode());
  }

  uint32_t Index = Section.AccelSection.getULEB128(Offset);
  uint32_t Form = Section.AccelSection.getULEB128(Offset);
  return AttributeEncoding(dwarf::Index(Index), dwarf::Form(Form));
}

Expected<std::vector<DWARFDebugNames::AttributeEncoding>>
DWARFDebugNames::NameIndex::extractAttributeEncodings(uint32_t *Offset) {
  std::vector<AttributeEncoding> Result;
  for (;;) {
    auto AttrEncOr = extractAttributeEncoding(Offset);
    if (!AttrEncOr)
      return AttrEncOr.takeError();
    if (isSentinel(*AttrEncOr))
      return std::move(Result);

    Result.emplace_back(*AttrEncOr);
  }
}

Expected<DWARFDebugNames::Abbrev>
DWARFDebugNames::NameIndex::extractAbbrev(uint32_t *Offset) {
  if (*Offset >= EntriesBase) {
    return make_error<StringError>("Incorrectly terminated abbreviation table.",
                                   inconvertibleErrorCode());
  }

  uint32_t Code = Section.AccelSection.getULEB128(Offset);
  if (Code == 0)
    return sentinelAbbrev();

  uint32_t Tag = Section.AccelSection.getULEB128(Offset);
  auto AttrEncOr = extractAttributeEncodings(Offset);
  if (!AttrEncOr)
    return AttrEncOr.takeError();
  return Abbrev(Code, dwarf::Tag(Tag), std::move(*AttrEncOr));
}

Error DWARFDebugNames::NameIndex::extract() {
  const DWARFDataExtractor &AS = Section.AccelSection;
  uint32_t Offset = Base;
  if (Error E = Hdr.extract(AS, &Offset))
    return E;

  CUsBase = Offset;
  Offset += Hdr.CompUnitCount * 4;
  Offset += Hdr.LocalTypeUnitCount * 4;
  Offset += Hdr.ForeignTypeUnitCount * 8;
  BucketsBase = Offset;
  Offset += Hdr.BucketCount * 4;
  HashesBase = Offset;
  if (Hdr.BucketCount > 0)
    Offset += Hdr.NameCount * 4;
  StringOffsetsBase = Offset;
  Offset += Hdr.NameCount * 4;
  EntryOffsetsBase = Offset;
  Offset += Hdr.NameCount * 4;

  if (!AS.isValidOffsetForDataOfSize(Offset, Hdr.AbbrevTableSize))
    return make_error<StringError>(
        "Section too small: cannot read abbreviations.",
        inconvertibleErrorCode());

  EntriesBase = Offset + Hdr.AbbrevTableSize;

  for (;;) {
    auto AbbrevOr = extractAbbrev(&Offset);
    if (!AbbrevOr)
      return AbbrevOr.takeError();
    if (isSentinel(*AbbrevOr))
      return Error::success();

    if (!Abbrevs.insert(std::move(*AbbrevOr)).second) {
      return make_error<StringError>("Duplicate abbreviation code.",
                                     inconvertibleErrorCode());
    }
  }
}

DWARFDebugNames::Entry::Entry(const Abbrev &Abbr) : Abbr(Abbr) {
  // This merely creates form values. It is up to the caller
  // (NameIndex::getEntry) to populate them.
  Values.reserve(Abbr.Attributes.size());
  for (const auto &Attr : Abbr.Attributes)
    Values.emplace_back(Attr.Form);
}

void DWARFDebugNames::Entry::dump(ScopedPrinter &W) const {
  W.printHex("Abbrev", Abbr.Code);
  W.startLine() << "Tag: " << formatTag(Abbr.Tag) << "\n";

  assert(Abbr.Attributes.size() == Values.size());
  for (uint32_t I = 0, E = Values.size(); I < E; ++I) {
    W.startLine() << formatIndex(Abbr.Attributes[I].Index) << ": ";
    Values[I].dump(W.getOStream());
    W.getOStream() << '\n';
  }
}

char DWARFDebugNames::SentinelError::ID;
std::error_code DWARFDebugNames::SentinelError::convertToErrorCode() const {
  return inconvertibleErrorCode();
}

uint32_t DWARFDebugNames::NameIndex::getCUOffset(uint32_t CU) const {
  assert(CU < Hdr.CompUnitCount);
  uint32_t Offset = CUsBase + 4 * CU;
  return Section.AccelSection.getRelocatedValue(4, &Offset);
}

uint32_t DWARFDebugNames::NameIndex::getLocalTUOffset(uint32_t TU) const {
  assert(TU < Hdr.LocalTypeUnitCount);
  uint32_t Offset = CUsBase + Hdr.CompUnitCount * 4;
  return Section.AccelSection.getRelocatedValue(4, &Offset);
}

uint64_t DWARFDebugNames::NameIndex::getForeignTUOffset(uint32_t TU) const {
  assert(TU < Hdr.ForeignTypeUnitCount);
  uint32_t Offset = CUsBase + (Hdr.CompUnitCount + Hdr.LocalTypeUnitCount) * 4;
  return Section.AccelSection.getU64(&Offset);
}

Expected<DWARFDebugNames::Entry>
DWARFDebugNames::NameIndex::getEntry(uint32_t *Offset) const {
  const DWARFDataExtractor &AS = Section.AccelSection;
  if (!AS.isValidOffset(*Offset))
    return make_error<StringError>("Incorrectly terminated entry list",
                                   inconvertibleErrorCode());

  uint32_t AbbrevCode = AS.getULEB128(Offset);
  if (AbbrevCode == 0)
    return make_error<SentinelError>();

  const auto AbbrevIt = Abbrevs.find_as(AbbrevCode);
  if (AbbrevIt == Abbrevs.end())
    return make_error<StringError>("Invalid abbreviation",
                                   inconvertibleErrorCode());

  Entry E(*AbbrevIt);

  DWARFFormParams FormParams = {Hdr.Version, 0, dwarf::DwarfFormat::DWARF32};
  for (auto &Value : E.Values) {
    if (!Value.extractValue(AS, Offset, FormParams))
      return make_error<StringError>("Error extracting index attribute values",
                                     inconvertibleErrorCode());
  }
  return std::move(E);
}

DWARFDebugNames::NameTableEntry
DWARFDebugNames::NameIndex::getNameTableEntry(uint32_t Index) const {
  assert(0 < Index && Index <= Hdr.NameCount);
  uint32_t StringOffsetOffset = StringOffsetsBase + 4 * (Index - 1);
  uint32_t EntryOffsetOffset = EntryOffsetsBase + 4 * (Index - 1);
  const DWARFDataExtractor &AS = Section.AccelSection;

  uint32_t StringOffset = AS.getRelocatedValue(4, &StringOffsetOffset);
  uint32_t EntryOffset = AS.getU32(&EntryOffsetOffset);
  EntryOffset += EntriesBase;
  return {StringOffset, EntryOffset};
}

uint32_t
DWARFDebugNames::NameIndex::getBucketArrayEntry(uint32_t Bucket) const {
  assert(Bucket < Hdr.BucketCount);
  uint32_t BucketOffset = BucketsBase + 4 * Bucket;
  return Section.AccelSection.getU32(&BucketOffset);
}

uint32_t DWARFDebugNames::NameIndex::getHashArrayEntry(uint32_t Index) const {
  assert(0 < Index && Index <= Hdr.NameCount);
  uint32_t HashOffset = HashesBase + 4 * (Index - 1);
  return Section.AccelSection.getU32(&HashOffset);
}

// Returns true if we should continue scanning for entries, false if this is the
// last (sentinel) entry). In case of a parsing error we also return false, as
// it's not possible to recover this entry list (but the other lists may still
bool DWARFDebugNames::NameIndex::dumpEntry(ScopedPrinter &W,
                                           uint32_t *Offset) const {
  uint32_t EntryId = *Offset;
  auto EntryOr = getEntry(Offset);
  if (!EntryOr) {
    handleAllErrors(EntryOr.takeError(), [](const SentinelError &) {},
                    [&W](const ErrorInfoBase &EI) { EI.log(W.startLine()); });
    return false;
  }

  DictScope EntryScope(W, ("Entry @ 0x" + Twine::utohexstr(EntryId)).str());
  EntryOr->dump(W);
  return true;
}

void DWARFDebugNames::NameIndex::dumpName(ScopedPrinter &W, uint32_t Index,
                                          Optional<uint32_t> Hash) const {
  const DataExtractor &SS = Section.StringSection;
  NameTableEntry NTE = getNameTableEntry(Index);

  DictScope NameScope(W, ("Name " + Twine(Index)).str());
  if (Hash)
    W.printHex("Hash", *Hash);

  W.startLine() << format("String: 0x%08x", NTE.StringOffset);
  W.getOStream() << " \"" << SS.getCStr(&NTE.StringOffset) << "\"\n";

  while (dumpEntry(W, &NTE.EntryOffset))
    /*empty*/;
}

void DWARFDebugNames::NameIndex::dumpCUs(ScopedPrinter &W) const {
  ListScope CUScope(W, "Compilation Unit offsets");
  for (uint32_t CU = 0; CU < Hdr.CompUnitCount; ++CU)
    W.startLine() << format("CU[%u]: 0x%08x\n", CU, getCUOffset(CU));
}

void DWARFDebugNames::NameIndex::dumpLocalTUs(ScopedPrinter &W) const {
  if (Hdr.LocalTypeUnitCount == 0)
    return;

  ListScope TUScope(W, "Local Type Unit offsets");
  for (uint32_t TU = 0; TU < Hdr.LocalTypeUnitCount; ++TU)
    W.startLine() << format("LocalTU[%u]: 0x%08x\n", TU, getLocalTUOffset(TU));
}

void DWARFDebugNames::NameIndex::dumpForeignTUs(ScopedPrinter &W) const {
  if (Hdr.ForeignTypeUnitCount == 0)
    return;

  ListScope TUScope(W, "Foreign Type Unit signatures");
  for (uint32_t TU = 0; TU < Hdr.ForeignTypeUnitCount; ++TU) {
    W.startLine() << format("ForeignTU[%u]: 0x%016" PRIx64 "\n", TU,
                            getForeignTUOffset(TU));
  }
}

void DWARFDebugNames::NameIndex::dumpAbbreviations(ScopedPrinter &W) const {
  ListScope AbbrevsScope(W, "Abbreviations");
  for (const auto &Abbr : Abbrevs)
    Abbr.dump(W);
}

void DWARFDebugNames::NameIndex::dumpBucket(ScopedPrinter &W,
                                            uint32_t Bucket) const {
  ListScope BucketScope(W, ("Bucket " + Twine(Bucket)).str());
  uint32_t Index = getBucketArrayEntry(Bucket);
  if (Index == 0) {
    W.printString("EMPTY");
    return;
  }
  if (Index > Hdr.NameCount) {
    W.printString("Name index is invalid");
    return;
  }

  for (; Index <= Hdr.NameCount; ++Index) {
    uint32_t Hash = getHashArrayEntry(Index);
    if (Hash % Hdr.BucketCount != Bucket)
      break;

    dumpName(W, Index, Hash);
  }
}

LLVM_DUMP_METHOD void DWARFDebugNames::NameIndex::dump(ScopedPrinter &W) const {
  DictScope UnitScope(W, ("Name Index @ 0x" + Twine::utohexstr(Base)).str());
  Hdr.dump(W);
  dumpCUs(W);
  dumpLocalTUs(W);
  dumpForeignTUs(W);
  dumpAbbreviations(W);

  if (Hdr.BucketCount > 0) {
    for (uint32_t Bucket = 0; Bucket < Hdr.BucketCount; ++Bucket)
      dumpBucket(W, Bucket);
    return;
  }

  W.startLine() << "Hash table not present\n";
  for (uint32_t Index = 1; Index <= Hdr.NameCount; ++Index)
    dumpName(W, Index, None);
}

llvm::Error DWARFDebugNames::extract() {
  uint32_t Offset = 0;
  while (AccelSection.isValidOffset(Offset)) {
    NameIndex Next(*this, Offset);
    if (llvm::Error E = Next.extract())
      return E;
    Offset = Next.getNextUnitOffset();
    NameIndices.push_back(std::move(Next));
  }
  return Error::success();
}

LLVM_DUMP_METHOD void DWARFDebugNames::dump(raw_ostream &OS) const {
  ScopedPrinter W(OS);
  for (const NameIndex &NI : NameIndices)
    NI.dump(W);
}


AudioProcessorPlayer::AudioProcessorPlayer(bool doDoublePrecisionProcessing)
    : processor (nullptr),
      sampleRate (0),
      blockSize (0),
      isPrepared (false),
      isDoublePrecision (doDoublePrecisionProcessing),
      numInputChans (0),
      numOutputChans (0)
{
}

AudioProcessorPlayer::~AudioProcessorPlayer()
{
    setProcessor (nullptr);
}

void AudioProcessorPlayer::setProcessor (AudioProcessor* const processorToPlay)
{
    if (processor != processorToPlay)
    {
        if (processorToPlay != nullptr && sampleRate > 0 && blockSize > 0)
        {
            processorToPlay->setPlayConfigDetails (numInputChans, numOutputChans, sampleRate, blockSize);

            const bool supportsDouble = processorToPlay->supportsDoublePrecisionProcessing() && isDoublePrecision;
            AudioProcessor::ProcessingPrecision precision = supportsDouble ? AudioProcessor::doublePrecision
                                                                           : AudioProcessor::singlePrecision;

            processorToPlay->setProcessingPrecision (precision);
            processorToPlay->prepareToPlay (sampleRate, blockSize);
        }

        AudioProcessor* oldOne;

        {
            const ScopedLock sl (lock);
            oldOne = isPrepared ? processor : nullptr;
            processor = processorToPlay;
            isPrepared = true;
        }

        if (oldOne != nullptr)
            oldOne->releaseResources();
    }
}

void AudioProcessorPlayer::setDoublePrecisionProcessing (bool doublePrecision)
{
    if (doublePrecision != isDoublePrecision)
    {
        const ScopedLock sl (lock);

        if (processor != nullptr)
        {
            processor->releaseResources();

            const bool supportsDouble = processor->supportsDoublePrecisionProcessing() && doublePrecision;
            AudioProcessor::ProcessingPrecision precision = supportsDouble ? AudioProcessor::doublePrecision
                                                                           : AudioProcessor::singlePrecision;

            processor->setProcessingPrecision (precision);
            processor->prepareToPlay (sampleRate, blockSize);
        }

        isDoublePrecision = doublePrecision;
    }
}

void AudioProcessorPlayer::audioDeviceIOCallback (const float** const inputChannelData,
                                                  const int numInputChannels,
                                                  float** const outputChannelData,
                                                  const int numOutputChannels,
                                                  const int numSamples)
{
    // these should have been prepared by audioDeviceAboutToStart()...
    jassert (sampleRate > 0 && blockSize > 0);

    incomingMidi.clear();
    messageCollector.removeNextBlockOfMessages (incomingMidi, numSamples);
    int totalNumChans = 0;

    if (numInputChannels > numOutputChannels)
    {
        // if there aren't enough output channels for the number of
        // inputs, we need to create some temporary extra ones (can't
        // use the input data in case it gets written to)
        tempBuffer.setSize (numInputChannels - numOutputChannels, numSamples,
                            false, false, true);

        for (int i = 0; i < numOutputChannels; ++i)
        {
            channels[totalNumChans] = outputChannelData[i];
            memcpy (channels[totalNumChans], inputChannelData[i], sizeof (float) * (size_t) numSamples);
            ++totalNumChans;
        }

        for (int i = numOutputChannels; i < numInputChannels; ++i)
        {
            channels[totalNumChans] = tempBuffer.getWritePointer (i - numOutputChannels);
            memcpy (channels[totalNumChans], inputChannelData[i], sizeof (float) * (size_t) numSamples);
            ++totalNumChans;
        }
    }
    else
    {
        for (int i = 0; i < numInputChannels; ++i)
        {
            channels[totalNumChans] = outputChannelData[i];
            memcpy (channels[totalNumChans], inputChannelData[i], sizeof (float) * (size_t) numSamples);
            ++totalNumChans;
        }

        for (int i = numInputChannels; i < numOutputChannels; ++i)
        {
            channels[totalNumChans] = outputChannelData[i];
            zeromem (channels[totalNumChans], sizeof (float) * (size_t) numSamples);
            ++totalNumChans;
        }
    }

    AudioSampleBuffer buffer (channels, totalNumChans, numSamples);

    {
        const ScopedLock sl (lock);

        if (processor != nullptr)
        {
            const ScopedLock sl2 (processor->getCallbackLock());

            if (! processor->isSuspended())
            {
                if (processor->isUsingDoublePrecision())
                {
                    conversionBuffer.makeCopyOf (buffer);
                    processor->processBlock (conversionBuffer, incomingMidi);
                    buffer.makeCopyOf (conversionBuffer);
                }
                else
                {
                    processor->processBlock (buffer, incomingMidi);
                }

                return;
            }
        }
    }

    for (int i = 0; i < numOutputChannels; ++i)
        FloatVectorOperations::clear (outputChannelData[i], numSamples);
}

void AudioProcessorPlayer::audioDeviceAboutToStart (AudioIODevice* const device)
{
    const double newSampleRate = device->getCurrentSampleRate();
    const int newBlockSize     = device->getCurrentBufferSizeSamples();
    const int numChansIn       = device->getActiveInputChannels().countNumberOfSetBits();
    const int numChansOut      = device->getActiveOutputChannels().countNumberOfSetBits();

    const ScopedLock sl (lock);

    sampleRate = newSampleRate;
    blockSize  = newBlockSize;
    numInputChans  = numChansIn;
    numOutputChans = numChansOut;

    messageCollector.reset (sampleRate);
    channels.calloc ((size_t) jmax (numChansIn, numChansOut) + 2);

    if (processor != nullptr)
    {
        if (isPrepared)
            processor->releaseResources();

        AudioProcessor* const oldProcessor = processor;
        setProcessor (nullptr);
        setProcessor (oldProcessor);
    }
}

void AudioProcessorPlayer::audioDeviceStopped()
{
    const ScopedLock sl (lock);

    if (processor != nullptr && isPrepared)
        processor->releaseResources();

    sampleRate = 0.0;
    blockSize = 0;
    isPrepared = false;
    tempBuffer.setSize (1, 1);
}

void AudioProcessorPlayer::handleIncomingMidiMessage (MidiInput*, const MidiMessage& message)
{
    messageCollector.addMessageToQueue (message);
}


switch (_key.getCycleType()) {
			case G_CYC_1CYCLE:
				_addBlendMode1Cycle(_program, _uniforms);
				break;
			case G_CYC_2CYCLE:
				_addBlendMode2Cycle(_program, _uniforms);
				break;
		}

#include "glsl_CombinerProgramUniformFactory.h"

#include <Config.h>
#include <Graphics/Parameters.h>
#include <Graphics/Context.h>

namespace glsl {

CombinerProgramUniformFactory::CombinerProgramUniformFactory(const opengl::GLInfo &_glInfo)
: m_glInfo(_glInfo) {
}

CombinerProgramUniformFactory::~CombinerProgramUniformFactory()
{
}

void CombinerProgramUniformFactory::buildUniforms(GLuint _program,
												  const CombinerInputs &_inputs,
												  const CombinerKey &_key,
												  UniformGroups &_uniforms) {

	_addNoiseTex(_program, _uniforms);
	_addScreenSpaceTriangleInfo(_program, _uniforms);
	_addRasterInfo(_program, _uniforms);
	_addViewportInfo(_program, _uniforms);

	if (!m_glInfo.isGLES2) {
		_addDepthTex(_program, _uniforms);
		_addDepthScale(_program, _uniforms);
	}

	if (_inputs.usesTexture()) {
		_addTextures(_program, _uniforms);

		if (config.video.multisampling != 0)
			_addMSAATextures(_program, _uniforms);

		_addFrameBufferInfo(_program, _uniforms);

		if (_inputs.usesLOD()) {
			_addMipmap(_program, _uniforms);
			if (config.generalEmulation.enableLOD != 0)
				_addMipmap2(_program, _uniforms);
		} else if (_key.getCycleType() < G_CYC_COPY) {
			_addTextureFetchMode(_program, _uniforms);
		}

		_addTexturePersp(_program, _uniforms);

		if (m_glInfo.isGLES2)
			_addTextureSize(_program, _uniforms, _inputs.usesTile(0), _inputs.usesTile(1));

		if (!_key.isRectKey())
			_addTextureParams(_program, _uniforms, _inputs.usesTile(0), _inputs.usesTile(1));

		_addClampWrapMirrorEngine(_program, _uniforms, _inputs.usesTile(0), _inputs.usesTile(1));
	}

	_addFog(_program, _uniforms);

	if (config.generalEmulation.enableLegacyBlending == 0) {

	}

	_addBlendCvg(_program, _uniforms);

	_addDitherMode(_program, _uniforms, _inputs.usesNoise());

	_addScreenScale(_program, _uniforms);

	_addAlphaTestInfo(_program, _uniforms);

	if ((config.generalEmulation.hacks & hack_RE2) != 0 && config.generalEmulation.enableFragmentDepthWrite != 0)
		_addZLutTexture(_program, _uniforms);

	if (config.frameBufferEmulation.N64DepthCompare != Config::dcDisable)
		_addDepthInfo(_program, _uniforms);
	else
		_addDepthSource(_program, _uniforms);

	if (config.generalEmulation.enableFragmentDepthWrite != 0 ||
		config.frameBufferEmulation.N64DepthCompare != Config::dcDisable)
		_addRenderTarget(_program, _uniforms);

	if (m_glInfo.isGLESX && m_glInfo.noPerspective) {
		_addClampMode(_program, _uniforms);
		_addPolygonOffset(_program, _uniforms);
	}

	_addScreenCoordsScale(_program, _uniforms);

	_addColors(_program, _uniforms);

	if (_key.isRectKey())
		_addRectColor(_program, _uniforms);

	if (_inputs.usesHwLighting())
		_addLights(_program, _uniforms);
}

}