<commit_before>#include "Reaction.h"
#include "Element.h"

#define MAX_REACTION_DEPTH (sizeof(uint32) * 8 - 1)
#define ELEMENT_IN_USE_BIT (MAX_REACTION_DEPTH + 1)

//HACK: This is to get around ReactionNode not having a reference to the current reaction, make this not awful later.
Reaction* currentReaction;

class ReactionNode
{
protected:
    ReactionNode* firstChild = NULL;
    ReactionNode* sibling = NULL;
    BondType type = BondType_None;
    int leftData = 0;
    int rightData = 0;
public:
    class Iterator
    {
    private:
        ReactionNode** marker;
    public:
        Iterator(ReactionNode** start)
        {
            this->marker = start;
        }

        void Next()
        {
            if (*marker != NULL)
            { marker = &(*marker)->sibling; }
        }

        ReactionNode* Get() const
        {
            return *marker;
        }

        Iterator& operator++()
        {
            Next();
            return *this;
        }

        Iterator& operator++(int)
        {
            return ++(*this);
        }

        ReactionNode* operator*() const
        {
            return Get();
        }

        bool HasNext()
        {
            return *marker != NULL && (*marker)->sibling != NULL;
        }

        ReactionNode** GetMarker()
        {
            return marker;
        }

        void GoToEnd()
        {
            while (HasNext())
            { Next(); }
        }
    };

    void AddSibling(ReactionNode* node)
    {
        Iterator it(&sibling);
        it.GoToEnd();
        it.Next(); // Get marker on location to place node.
        *(it.GetMarker()) = node;
    }

    void AddChild(ReactionNode* node)
    {
        Iterator it(&firstChild);
        it.GoToEnd();
        it.Next(); // Get marker on location to place node.
        *(it.GetMarker()) = node;
    }

    void SetBondInfo(BondType type, int leftData, int rightData)
    {
        this->type = type;
        this->leftData = leftData;
        this->rightData = rightData;
    }

    void SetBondInfo(BondType type, int data)
    { SetBondInfo(type, data, data); }

    void SetBondInfo(BondType type)
    { SetBondInfo(type, 0, 0); }

    void ApplyBond(Compound* compound, Element* left, Element* right)
    {
        if (this->type == BondType_None)
        { return; }

        LOG("ApplyBond(Compound:0x%X, Element:0x%X, Element:0x%X)\n", compound, left, right);
        Assert(left != right); // This means a bond was applied to a passthrough node.

        left->SetMaskBit(MAX_REACTION_DEPTH);
        right->SetMaskBit(MAX_REACTION_DEPTH);
        left->SetBondTypeFor(compound, right, type, leftData, rightData);
    }

    bool ProcessChildren(Compound* compound, Element* inputForChildren, int depth)
    {
        for (Iterator it(&firstChild); *it; it++)
        {
            if (!(*it)->Process(compound, inputForChildren, depth))
            {
                return false; // All children must return true to succeed.
            }
        }

        return true; // If we got this far, all children met their criteria.
    }

    // Don't override this for most implementations of this class
    virtual bool Process(Compound* compound, Element* input, int depth)
    {
        Assert(depth >= 0 && depth < MAX_REACTION_DEPTH);
        bool success = false;
        Element* output = NULL;
        int num = 0;

        while (true)
        {
            LOG("ATTEMPT %d\n", num++);
            // This will return a different element each time it is called, and NULL when no element is available
            // Therefore, we can loop until we find an element with child elements that satisfy this branch of the reaction.
            output = GetOutput(compound, input, depth);

            // If output == NULL, we ran out of possibilities and failed.
            if (output == NULL)
            { break; }

            // Mark this element as being used for this depth of the compound processing
            output->SetMaskBit(depth);

            // Process children (on the next depth)
            if (ProcessChildren(compound, output, depth + 1))
            {
                // All children succeeded, so we succeed!
                success = true;
                break;
            }
        }

        currentReaction->ClearElementMasks(depth); // Clear all of the elements we considered for this branch
        if (success)
        { ApplyBond(compound, input, output); }
        return success;
    }

    // Override this for most implementations of this class
    virtual Element* GetOutput(Compound* compound, Element* input, int depth)
    {
        LOG("WARN: Called unimplemented ReactionNode:GetOutput with Element:0x%X[%s] as input @ depth %d!\n", input, input->GetSymbol(), depth);
        return NULL;
    }
};

class RootElementSymbolNode : public ReactionNode
{
private:
    const char* symbol;
public:
    RootElementSymbolNode(const char* symbol)
    {
        this->symbol = symbol;
        LOG("RootElementSymbolNode:0x%X %s\n", this, symbol);
    }

    virtual Element* GetOutput(Compound* compound, Element* input, int depth)
    {
        if (input->MatchesMask(ALL_ELEMENTS_MASK))
        { return NULL; }

        Element* ret = strcmp(input->GetSymbol(), symbol) == 0 ? input : NULL;
        LOG("Node:0x%X[%s].GetOutput(__, __, %d) = 0x%X\n", this, symbol, depth, ret);
        return ret;
    }
};

class ElementSymbolNode : public ReactionNode
{
private:
    const char* symbol;
public:
    ElementSymbolNode(const char* symbol)
    {
        this->symbol = symbol;
        LOG("ElementSymbolNode:0x%X %s\n", this, symbol);
    }

    virtual Element* GetOutput(Compound* compound, Element* input, int depth)
    {
        Element* ret = input->GetBondWith(symbol);
        LOG("Node:0x%X[%s].GetOutput(__, __, %d) = 0x%X\n", this, symbol, depth, ret);
        return ret;
    }
};

/*
Processes a reaction and determines the outcome, if any.

This algorithm was written with help by:
Christopher Culbertson, Associate Professor at Kansas State University
Michael Ayala, Chemistry Major at UC Davis
*/
bool Reaction::Process()
{
    currentReaction = this;
    LOG("--------------------------------------------------------------\n");
    //ElementSymbolNode node1("H");
    //ElementSymbolNode node2("H");
    //node1.AddChild(&node2);
    //node2.SetBondInfo(BondType_Covalent);
    RootElementSymbolNode node1("Cl");
    ElementSymbolNode node1_1("O");
    ElementSymbolNode node1_1_1("H");
    ElementSymbolNode node1_2("O");
    ElementSymbolNode node1_3("O");
    ElementSymbolNode node1_4("O");

    node1.AddChild(&node1_1);
    node1.AddChild(&node1_2);
    node1.AddChild(&node1_3);
    node1.AddChild(&node1_4);

    node1_1.AddChild(&node1_1_1);

    node1_1.SetBondInfo(BondType_Covalent, 1);
    node1_2.SetBondInfo(BondType_Covalent, 2);
    node1_3.SetBondInfo(BondType_Covalent, 2);
    node1_4.SetBondInfo(BondType_Covalent, 2);

    node1_1_1.SetBondInfo(BondType_Covalent, 1);

    //TODO: Currently, it is theoretically possible for sufficiently complex compounds to find invalid reactions in some case.
    /*
    We should add some processing to make sure we don't re-use elements in the same reaction.

    For instance, say the user is trying to make phsophorous acid (H3PO3) the proper cube arrangement would be something like
      OH
    HOP
      OH
    However, it is possible that this could happen:
      OH
    HOPO
    Where the right-top hydrogen is shared between the two oxygens, which is not physically possible.
    */

    Compound* newCompound = StartNewCompound();
    for (int i = 0; i < elements.Count(); i++)
    {
        LOG("Processing element %d:%s\n", i, elements[i]->GetSymbol());
        if (node1.Process(newCompound, elements[i], 0))
        {
            LOG("%d: TRUE\n", i);
            newCompound = StartNewCompound();
        }
        else
        {
            LOG("%d: FALSE\n", i);
        }
    }
    CancelCompound(newCompound);

    LOG("%d!!!\n", possibleCompounds.Count());

    #if 0
    Element* element;
    Element* other;
    ElementSet* elements;
    ElementSet* dedupe = new ElementSet();// Used to de-deupe two interactions with the same query
    ElementSet* others;

    //--------------------------------------------------------------------------
    // Determine all possible compounds: (2-element reactions)
    //--------------------------------------------------------------------------

    // Hydrogen - Hydrogen/Halogen/Alkali/AlkaliEarth
    elements = Find(HYDROGEN);
    dedupe->Clear();
    for (int i = 0; i < elements->Count(); i++)
    {
        element = elements->Get(i);

        if (!dedupe->Contains(element) && element->GetBondWith(HYDROGEN, &other))
        {
            dedupe->Add(other);
            element->SetBondTypeFor(StartNewCompound(), other, BondType_Covalent, 2, 2);
        }

        if (element->GetBondWith(HALOGEN, &other))
        { element->SetBondTypeFor(StartNewCompound(), other, BondType_Covalent, 2, 8); }

        if (element->GetBondWith(ALKALI, &other))
        { element->SetBondTypeFor(StartNewCompound(), other, BondType_Ionic);}
    }
    delete elements;

    // Halogen - Halogen/Alakali/AlakaliEarth
    elements = Find(HALOGEN);
    dedupe->Clear();
    for (int i = 0; i < elements->Count(); i++)
    {
        element = elements->Get(i);

        if (!dedupe->Contains(element) && element->GetBondWith(HALOGEN, &other))
        {
            dedupe->Add(other);
            element->SetBondTypeFor(StartNewCompound(), other, BondType_Covalent, 8, 8);
        }

        if (element->GetBondWith(ALKALI, &other))
        { element->SetBondTypeFor(StartNewCompound(), other, BondType_Ionic); }
    }
    delete elements;

    // Li - I
    elements = Find("Li");
    for (int i = 0; i < elements->Count(); i++)
    {
        element = elements->Get(i);
        if (element->GetBondWith("I", &other))
        { element->SetBondTypeFor(StartNewCompound(), other, BondType_Ionic); }
    }
    delete elements;

    //--------------------------------------------------------------------------
    // Determine all possible compounds: (3-element reactions)
    //--------------------------------------------------------------------------

    // AlkaliEarth - 2xHalogen/2xHydrogen
    elements = Find(ALKALIEARTH);
    for (int i = 0; i < elements->Count(); i++)
    {
        element = elements->Get(i);

        // Two halogens
        others = element->GetBondsWith(HALOGEN);
        if (others->Count() >= 2)
        {
            Compound* compound = StartNewCompound();
            //If the alkali earth metal is Beryllium, the bond will be covalent
            //If they are the other alkali earth metals, they will make an ionic bond
            //TODO: 2 and 9 are hard-coded as hacks to get to 4, 2, 2, need to make this more generic probably.
            if (strcmp(element->GetSymbol(), "Be") == 0)
            {
                element->SetBondTypeFor(compound, others->Get(0), BondType_Covalent, 2, 9);
                element->SetBondTypeFor(compound, others->Get(1), BondType_Covalent, 2, 9);
            }
            else
            {
                element->SetBondTypeFor(compound, others->Get(0), BondType_Ionic);
                element->SetBondTypeFor(compound, others->Get(1), BondType_Ionic);
            }
        }
        else if (others->Count() > 0)
        {
            // Potential reaction
            Compound* compound = StartNewCompound();
            for (int j = 0; j < others->Count(); j++)
            { element->SetBondTypeFor(compound, others->Get(j), BondType_Potential); }
        }
        delete others;

        // Two hydrogens
        others = element->GetBondsWith(HYDROGEN);
        if (others->Count() >= 2)
        {
            Compound* compound = StartNewCompound();

            //If the alkali earth metals are either Beryllium or Magnesium, the bond will be covalent
            //If they are the other alkali earth metals, they will make an ionic bond
            if (strcmp(element->GetSymbol(), "Be") == 0 || strcmp(element->GetSymbol(), "Mg") == 0)
            {
                //TODO: 6 and 9 are hard-coded as hacks to get to 4, 2, 2, need to make this more generic probably.
                element->SetBondTypeFor(compound, others->Get(0), BondType_Covalent, 3, 2);
                element->SetBondTypeFor(compound, others->Get(1), BondType_Covalent, 3, 2);
            }
            else
            {
                element->SetBondTypeFor(compound, others->Get(0), BondType_Ionic);
                element->SetBondTypeFor(compound, others->Get(1), BondType_Ionic);
            }
        }
        else if (others->Count() > 0)
        {
            // Potential reaction
            Compound* compound = StartNewCompound();
            for (int j = 0; j < others->Count(); j++)
            { element->SetBondTypeFor(compound, others->Get(j), BondType_Potential); }
        }
        delete others;
    }
    delete elements;

    //Cleanup
    delete dedupe;
    #endif

    //--------------------------------------------------------------------------
    // Choose the ideal compound and apply it:
    //--------------------------------------------------------------------------
    //TODO: Allow multiple compounds to form in one reaction when they don't overlap.
    idealCompound = NULL;
    for (int i = 0; i < possibleCompounds.Count(); i++)
    {
        Compound* compound = possibleCompounds[i];

        if (idealCompound == NULL)
        {
            idealCompound = compound;
            continue;
        }

        if (idealCompound->ContainsPotentialBonds() && !compound->ContainsPotentialBonds())
        {
            idealCompound = compound;
            continue;
        }

        if (idealCompound->GetElementCount() < compound->GetElementCount())
        {
            idealCompound = compound;
            continue;
        }
    }

    // Dispose of all compounds other than the ideal one to save memory
    for (int i = 0; i < possibleCompounds.Count(); i++)
    {
        if (possibleCompounds[i] != idealCompound)
        {
            delete possibleCompounds[i];
        }
    }
    // Clear the possible compounds list to release the linked list memory (The ideal compound will survive.)
    possibleCompounds.Clear();

    // Apply the ideal compound and return
    if (idealCompound != NULL)
    {
        idealCompound->Apply();
        return true;
    }
    return false;
}
<commit_msg>Started dedicated compound database instead of initializing ReactionNode's on the stack for every Reaction::Process.<commit_after>#include "Reaction.h"
#include "Element.h"

#define MAX_REACTION_DEPTH (sizeof(uint32) * 8 - 1)
#define ELEMENT_IN_USE_BIT (MAX_REACTION_DEPTH + 1)

//HACK: This is to get around ReactionNode not having a reference to the current reaction, make this not awful later.
Reaction* currentReaction;

class ReactionNode
{
protected:
    ReactionNode* firstChild = NULL;
    ReactionNode* sibling = NULL;
    BondType type = BondType_None;
    int leftData = 0;
    int rightData = 0;
public:
    class Iterator
    {
    private:
        ReactionNode** marker;
    public:
        Iterator(ReactionNode** start)
        {
            this->marker = start;
        }

        void Next()
        {
            if (*marker != NULL)
            { marker = &(*marker)->sibling; }
        }

        ReactionNode* Get() const
        {
            return *marker;
        }

        Iterator& operator++()
        {
            Next();
            return *this;
        }

        Iterator& operator++(int)
        {
            return ++(*this);
        }

        ReactionNode* operator*() const
        {
            return Get();
        }

        bool HasNext()
        {
            return *marker != NULL && (*marker)->sibling != NULL;
        }

        ReactionNode** GetMarker()
        {
            return marker;
        }

        void GoToEnd()
        {
            while (HasNext())
            { Next(); }
        }

        uint32 Count()
        {
            uint32 ret = 0;
            while (HasNext())
            {
                Next();
                ret++;
            }
            return ret;
        }
    };

    void AddSibling(ReactionNode* node)
    {
        Iterator it(&sibling);
        it.GoToEnd();
        it.Next(); // Get marker on location to place node.
        *(it.GetMarker()) = node;
    }

    void AddChild(ReactionNode* node)
    {
        Iterator it(&firstChild);
        it.GoToEnd();
        it.Next(); // Get marker on location to place node.
        *(it.GetMarker()) = node;
    }

    void SetBondInfo(BondType type, int leftData, int rightData)
    {
        this->type = type;
        this->leftData = leftData;
        this->rightData = rightData;
    }

    void SetBondInfo(BondType type, int data)
    { SetBondInfo(type, data, data); }

    void SetBondInfo(BondType type)
    { SetBondInfo(type, 0, 0); }

    void ApplyBond(Compound* compound, Element* left, Element* right)
    {
        if (this->type == BondType_None)
        { return; }

        //LOG("ApplyBond(Compound:0x%X, Element:0x%X, Element:0x%X)\n", compound, left, right);
        Assert(left != right); // This means a bond was applied to a passthrough node.

        left->SetMaskBit(MAX_REACTION_DEPTH);
        right->SetMaskBit(MAX_REACTION_DEPTH);
        left->SetBondTypeFor(compound, right, type, leftData, rightData);
    }

    bool ProcessChildren(Compound* compound, Element* inputForChildren, int depth)
    {
        for (Iterator it(&firstChild); *it; it++)
        {
            if (!(*it)->Process(compound, inputForChildren, depth))
            {
                return false; // All children must return true to succeed.
            }
        }

        return true; // If we got this far, all children met their criteria.
    }

    // Don't override this for most implementations of this class
    virtual bool Process(Compound* compound, Element* input, int depth)
    {
        Assert(depth >= 0 && depth < MAX_REACTION_DEPTH);
        bool success = false;
        Element* output = NULL;

        while (true)
        {
            // This will return a different element each time it is called, and NULL when no element is available
            // Therefore, we can loop until we find an element with child elements that satisfy this branch of the reaction.
            output = GetOutput(compound, input, depth);

            // If output == NULL, we ran out of possibilities and failed.
            if (output == NULL)
            { break; }

            // Mark this element as being used for this depth of the compound processing
            output->SetMaskBit(depth);

            // Process children (on the next depth)
            if (ProcessChildren(compound, output, depth + 1))
            {
                // All children succeeded, so we succeed!
                success = true;
                break;
            }
        }

        currentReaction->ClearElementMasks(depth); // Clear all of the elements we considered for this branch
        if (success)
        { ApplyBond(compound, input, output); }
        return success;
    }

    // Override this for most implementations of this class
    virtual Element* GetOutput(Compound* compound, Element* input, int depth)
    {
        LOG("WARN: Called unimplemented ReactionNode:GetOutput with Element:0x%X[%s] as input @ depth %d!\n", input, input->GetSymbol(), depth);
        return NULL;
    }

    Iterator GetChildIterator()
    {
        return Iterator(&firstChild);
    }
};

class RootElementSymbolNode : public ReactionNode
{
private:
    const char* symbol;
public:
    RootElementSymbolNode(const char* symbol)
    {
        this->symbol = symbol;
        LOG("RootElementSymbolNode:0x%X %s\n", this, symbol);
    }

    virtual Element* GetOutput(Compound* compound, Element* input, int depth)
    {
        if (input->MatchesMask(ALL_ELEMENTS_MASK))
        { return NULL; }

        Element* ret = strcmp(input->GetSymbol(), symbol) == 0 ? input : NULL;
        //LOG("Node:0x%X[%s].GetOutput(__, __, %d) = 0x%X\n", this, symbol, depth, ret);
        return ret;
    }
};

class ElementSymbolNode : public ReactionNode
{
private:
    const char* symbol;
public:
    ElementSymbolNode(const char* symbol)
    {
        this->symbol = symbol;
        LOG("ElementSymbolNode:0x%X %s\n", this, symbol);
    }

    virtual Element* GetOutput(Compound* compound, Element* input, int depth)
    {
        Element* ret = input->GetBondWith(symbol);
        //LOG("Node:0x%X[%s].GetOutput(__, __, %d) = 0x%X\n", this, symbol, depth, ret);
        return ret;
    }
};

void InitializeCompoundDatabase();

ReactionNode CompoundDatabaseRoot;

/*
Processes a reaction and determines the outcome, if any.

This algorithm was written with help by:
Christopher Culbertson, Associate Professor at Kansas State University
Michael Ayala, Chemistry Major at UC Davis
*/
bool Reaction::Process()
{
    // Initialize the compound database
    static bool compoundDatabaseIsInitialized = false;
    if (!compoundDatabaseIsInitialized)
    {
        InitializeCompoundDatabase();
        compoundDatabaseIsInitialized = true;
    }

    currentReaction = this;
    LOG("Reaction:0x%X processing %d elements...\n", this, elements.Count());
    LOG("[");
    for (int i = 0; i < elements.Count(); i++)
    {
        LOG("%s%s", i == 0 ? " " : ", ", elements[i]->GetSymbol());
    }
    LOG(" ]\n");

    //TODO: Right now a failed compound can leave element bonds in a weird partially bonded state, which will probably FUBAR the next compound to be processed.
    Compound* newCompound = StartNewCompound();
    for (int i = 0; i < elements.Count(); i++)
    {
        LOG("Processing element %d:%s as root to compound...\n", i, elements[i]->GetSymbol());

        for (ReactionNode::Iterator it = CompoundDatabaseRoot.GetChildIterator(); *it; it++)
        {
            if ((*it)->Process(newCompound, elements[i], 0))
            {
                // Make a new compound for the next potential compound
                newCompound = StartNewCompound();
            }
        }
    }
    CancelCompound(newCompound);

    LOG("Reaction processing completed with %d candidate compounds.\n", possibleCompounds.Count());

    #if 0
    Element* element;
    Element* other;
    ElementSet* elements;
    ElementSet* dedupe = new ElementSet();// Used to de-deupe two interactions with the same query
    ElementSet* others;

    //--------------------------------------------------------------------------
    // Determine all possible compounds: (2-element reactions)
    //--------------------------------------------------------------------------

    // Hydrogen - Hydrogen/Halogen/Alkali/AlkaliEarth
    elements = Find(HYDROGEN);
    dedupe->Clear();
    for (int i = 0; i < elements->Count(); i++)
    {
        element = elements->Get(i);

        if (!dedupe->Contains(element) && element->GetBondWith(HYDROGEN, &other))
        {
            dedupe->Add(other);
            element->SetBondTypeFor(StartNewCompound(), other, BondType_Covalent, 2, 2);
        }

        if (element->GetBondWith(HALOGEN, &other))
        { element->SetBondTypeFor(StartNewCompound(), other, BondType_Covalent, 2, 8); }

        if (element->GetBondWith(ALKALI, &other))
        { element->SetBondTypeFor(StartNewCompound(), other, BondType_Ionic);}
    }
    delete elements;

    // Halogen - Halogen/Alakali/AlakaliEarth
    elements = Find(HALOGEN);
    dedupe->Clear();
    for (int i = 0; i < elements->Count(); i++)
    {
        element = elements->Get(i);

        if (!dedupe->Contains(element) && element->GetBondWith(HALOGEN, &other))
        {
            dedupe->Add(other);
            element->SetBondTypeFor(StartNewCompound(), other, BondType_Covalent, 8, 8);
        }

        if (element->GetBondWith(ALKALI, &other))
        { element->SetBondTypeFor(StartNewCompound(), other, BondType_Ionic); }
    }
    delete elements;

    // Li - I
    elements = Find("Li");
    for (int i = 0; i < elements->Count(); i++)
    {
        element = elements->Get(i);
        if (element->GetBondWith("I", &other))
        { element->SetBondTypeFor(StartNewCompound(), other, BondType_Ionic); }
    }
    delete elements;

    //--------------------------------------------------------------------------
    // Determine all possible compounds: (3-element reactions)
    //--------------------------------------------------------------------------

    // AlkaliEarth - 2xHalogen/2xHydrogen
    elements = Find(ALKALIEARTH);
    for (int i = 0; i < elements->Count(); i++)
    {
        element = elements->Get(i);

        // Two halogens
        others = element->GetBondsWith(HALOGEN);
        if (others->Count() >= 2)
        {
            Compound* compound = StartNewCompound();
            //If the alkali earth metal is Beryllium, the bond will be covalent
            //If they are the other alkali earth metals, they will make an ionic bond
            //TODO: 2 and 9 are hard-coded as hacks to get to 4, 2, 2, need to make this more generic probably.
            if (strcmp(element->GetSymbol(), "Be") == 0)
            {
                element->SetBondTypeFor(compound, others->Get(0), BondType_Covalent, 2, 9);
                element->SetBondTypeFor(compound, others->Get(1), BondType_Covalent, 2, 9);
            }
            else
            {
                element->SetBondTypeFor(compound, others->Get(0), BondType_Ionic);
                element->SetBondTypeFor(compound, others->Get(1), BondType_Ionic);
            }
        }
        else if (others->Count() > 0)
        {
            // Potential reaction
            Compound* compound = StartNewCompound();
            for (int j = 0; j < others->Count(); j++)
            { element->SetBondTypeFor(compound, others->Get(j), BondType_Potential); }
        }
        delete others;

        // Two hydrogens
        others = element->GetBondsWith(HYDROGEN);
        if (others->Count() >= 2)
        {
            Compound* compound = StartNewCompound();

            //If the alkali earth metals are either Beryllium or Magnesium, the bond will be covalent
            //If they are the other alkali earth metals, they will make an ionic bond
            if (strcmp(element->GetSymbol(), "Be") == 0 || strcmp(element->GetSymbol(), "Mg") == 0)
            {
                //TODO: 6 and 9 are hard-coded as hacks to get to 4, 2, 2, need to make this more generic probably.
                element->SetBondTypeFor(compound, others->Get(0), BondType_Covalent, 3, 2);
                element->SetBondTypeFor(compound, others->Get(1), BondType_Covalent, 3, 2);
            }
            else
            {
                element->SetBondTypeFor(compound, others->Get(0), BondType_Ionic);
                element->SetBondTypeFor(compound, others->Get(1), BondType_Ionic);
            }
        }
        else if (others->Count() > 0)
        {
            // Potential reaction
            Compound* compound = StartNewCompound();
            for (int j = 0; j < others->Count(); j++)
            { element->SetBondTypeFor(compound, others->Get(j), BondType_Potential); }
        }
        delete others;
    }
    delete elements;

    //Cleanup
    delete dedupe;
    #endif

    //--------------------------------------------------------------------------
    // Choose the ideal compound and apply it:
    //--------------------------------------------------------------------------
    //TODO: Allow multiple compounds to form in one reaction when they don't overlap.
    idealCompound = NULL;
    for (int i = 0; i < possibleCompounds.Count(); i++)
    {
        Compound* compound = possibleCompounds[i];

        if (idealCompound == NULL)
        {
            idealCompound = compound;
            continue;
        }

        if (idealCompound->ContainsPotentialBonds() && !compound->ContainsPotentialBonds())
        {
            idealCompound = compound;
            continue;
        }

        if (idealCompound->GetElementCount() < compound->GetElementCount())
        {
            idealCompound = compound;
            continue;
        }
    }

    // Dispose of all compounds other than the ideal one to save memory
    for (int i = 0; i < possibleCompounds.Count(); i++)
    {
        if (possibleCompounds[i] != idealCompound)
        {
            delete possibleCompounds[i];
        }
    }
    // Clear the possible compounds list to release the linked list memory (The ideal compound will survive.)
    possibleCompounds.Clear();

    // Apply the ideal compound and return
    if (idealCompound != NULL)
    {
        idealCompound->Apply();
        return true;
    }
    return false;
}

//------------------------------------------------------------------------------
// Nodes for potential compounds
//------------------------------------------------------------------------------
//TOOD: Either make this nicer or generate it with a tool. (I'm leaning towards the latter.)
namespace PerchloricAcid
{
    RootElementSymbolNode node1("Cl");
    ElementSymbolNode node1_1("O");
    ElementSymbolNode node1_1_1("H");
    ElementSymbolNode node1_2("O");
    ElementSymbolNode node1_3("O");
    ElementSymbolNode node1_4("O");

    void Initialize()
    {
        node1.AddChild(&node1_1);
        node1.AddChild(&node1_2);
        node1.AddChild(&node1_3);
        node1.AddChild(&node1_4);

        node1_1.AddChild(&node1_1_1);

        node1_1.SetBondInfo(BondType_Covalent, 1);
        node1_2.SetBondInfo(BondType_Covalent, 2);
        node1_3.SetBondInfo(BondType_Covalent, 2);
        node1_4.SetBondInfo(BondType_Covalent, 2);

        node1_1_1.SetBondInfo(BondType_Covalent, 1);

        CompoundDatabaseRoot.AddChild(&node1);
    }
}

void InitializeCompoundDatabase()
{
    //ElementSymbolNode node1("H");
    //ElementSymbolNode node2("H");
    //node1.AddChild(&node2);
    //node2.SetBondInfo(BondType_Covalent);
    LOG("Initializing compound database...\n");
    PerchloricAcid::Initialize();
    LOG("Done initializing compound database with %d compounds.\n", CompoundDatabaseRoot.GetChildIterator().Count());
}
<|endoftext|>


<commit_before>#include "photoeffects.hpp"
#include <math.h>

using namespace cv;

#define MAX_KERNELSIZE 15

int edgeBlur(InputArray src, OutputArray dst, int indentTop, int indentLeft)
{
    CV_Assert(src.type() == CV_8UC3);
    dst.create(src.size(), src.type());
    Mat image = src.getMat(), outputImage = dst.getMat();

    CV_Assert(indentTop >= 0 && indentTop <= (image.rows / 2 - 10));
    CV_Assert(indentLeft >= 0 && indentLeft <= (image.cols / 2 - 10));

    float halfWidth = (image.cols / 2.0f) * (image.cols / 2.0f);
    float halfHeight = (image.rows / 2.0f) * (image.rows / 2.0f);
    float a = (image.cols / 2.0f - indentLeft)
            * (image.cols / 2.0f - indentLeft);
    float b = (image.rows / 2.0f - indentTop)
            * (image.rows / 2.0f - indentTop);
    int kSizeEdges = halfWidth / a + halfHeight / b;

    kSizeEdges = MIN(kSizeEdges, MAX_KERNELSIZE);
    Mat bearingImage(image.rows + 2 * kSizeEdges,
                    image.cols + 2 * kSizeEdges,
                    CV_8UC3);
    copyMakeBorder(image, bearingImage, kSizeEdges, kSizeEdges,
        kSizeEdges, kSizeEdges, BORDER_REPLICATE);

    for (int i = kSizeEdges; i < bearingImage.rows - kSizeEdges; i++)
    {
        for (int j = kSizeEdges; j < bearingImage.cols - kSizeEdges; j++)
        {
            int size;
            Vec3f sumF;
            Vec3b Color;
            float sumC = 0.0f, coeff;
            float bearHalfWidth = bearingImage.cols / 2.0f;
            float bearHalfHeight = bearingImage.rows / 2.0f;
            float radius = (bearHalfHeight - i)
                    * (bearHalfHeight - i)
                    / b

                    + (bearHalfWidth - j)
                    * (bearHalfWidth - j)
                    / a;
            if (radius < 1.0f)
            {
                outputImage.at<Vec3b>(i - kSizeEdges, j - kSizeEdges) =
                    bearingImage.at<Vec3b>(i, j);
                continue;
            }
            size = radius;
            radius = 2.0f * (radius - 0.5f) * (radius - 0.5f);
            size = MIN(size, kSizeEdges);
            for (int x = -size; x <= size; x++)
            {
                for (int y = -size; y <= size; y++)
                {
                    coeff = 1.0f / (CV_PI * radius)
                            * exp(- (x * x + y * y) / radius);

                    Color = bearingImage.at<Vec3b>(x + i, y + j);
                    sumF += (Vec3f) (coeff * Color);
                    sumC += coeff;
                }
            }
            sumF *= (1.0f / sumC);
            outputImage.at<Vec3b>(i - kSizeEdges, j - kSizeEdges) = sumF;
        }
    }

    return 0;
}
<commit_msg>Strange effect<commit_after>#include "photoeffects.hpp"
#include <math.h>

using namespace cv;

#define MAX_KERNELSIZE 15

int edgeBlur(InputArray src, OutputArray dst, int indentTop, int indentLeft)
{
    CV_Assert(src.type() == CV_8UC3);
    dst.create(src.size(), src.type());
    Mat image = src.getMat(), outputImage = dst.getMat();

    CV_Assert(indentTop >= 0 && indentTop <= (image.rows / 2 - 10));
    CV_Assert(indentLeft >= 0 && indentLeft <= (image.cols / 2 - 10));

    float halfWidth = (image.cols / 2.0f) * (image.cols / 2.0f);
    float halfHeight = (image.rows / 2.0f) * (image.rows / 2.0f);
    float a = (image.cols / 2.0f - indentLeft)
            * (image.cols / 2.0f - indentLeft);
    float b = (image.rows / 2.0f - indentTop)
            * (image.rows / 2.0f - indentTop);
    int kSizeEdges = halfWidth / a + halfHeight / b;

    kSizeEdges = MIN(kSizeEdges, MAX_KERNELSIZE);
    Mat bearingImage(image.rows + 2 * kSizeEdges,
                    image.cols + 2 * kSizeEdges,
                    CV_8UC3);
    copyMakeBorder(image, bearingImage, kSizeEdges, kSizeEdges,
        kSizeEdges, kSizeEdges, BORDER_REPLICATE);

    for (int i = kSizeEdges; i < bearingImage.rows - kSizeEdges; i++)
    {
        for (int j = kSizeEdges; j < bearingImage.cols - kSizeEdges; j++)
        {
            int size;
            Vec3f sumF;
            Vec3b Color;
            float sumC = 0.0f, coeff;
            float bearHalfWidth = bearingImage.cols / 2.0f;
            float bearHalfHeight = bearingImage.rows / 2.0f;
            float radius = (bearHalfHeight - i)
                    * (bearHalfHeight - i)
                    / b

                    + (bearHalfWidth - j)
                    * (bearHalfWidth - j)
                    / a;
            if (radius < 1.0f)
            {
                outputImage.at<Vec3b>(i - kSizeEdges, j - kSizeEdges) =
                    bearingImage.at<Vec3b>(i, j);
                continue;
            }
            size = radius;
            radius = 2.0f * (radius - 0.5f) * (radius - 0.5f);
            size = MIN(size, kSizeEdges);
            for (int x = -size; x <= size; x++)
            {
                for (int y = -size; y <= size; y++)
                {
                    coeff = 1.0f / (CV_PI * radius)
                            * exp(- (x * x + y * y) / radius);

                    Color = bearingImage.at<Vec3b>(x + i, y + j);
                    sumF += coeff * Color;
                    sumC += coeff;
                }
            }
            sumF *= (1.0f / sumC);
            outputImage.at<Vec3b>(i - kSizeEdges, j - kSizeEdges) = sumF;
        }
    }

    return 0;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015-2018 Dubalu LLC. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "io.hh"

#include <errno.h>                  // for errno

#include "cassert.h"                // for ASSERT
#include "error.hh"                 // for error:name, error::description
#include "exception.h"              // for traceback
#include "likely.h"                 // for likely, unlikely
#include "log.h"                    // for L_CALL, L_ERRNO

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wgnu-statement-expression"

namespace io {

std::atomic_bool& ignore_eintr() {
	static std::atomic_bool ignore_eintr = true;
	return ignore_eintr;
}

int open(const char* path, int oflag, int mode) {
	int fd;
	while (true) {
#ifdef O_CLOEXEC
		fd = ::open(path, oflag | O_CLOEXEC, mode);
#else
		fd = ::open(path, oflag, mode);
#endif
		if unlikely(fd == -1) {
			if unlikely(errno == EINTR && ignore_eintr().load()) {
				continue;
			}
			break;
		}
		if (fd >= XAPIAND_MINIMUM_FILE_DESCRIPTOR) {
			break;
		}
		::close(fd);
		fd = -1;
		if unlikely(RetryAfterSignal(::open, "/dev/null", oflag, mode) == -1) {
			break;
		}
	}
	if (fd != -1) {
		if (mode != 0) {
			struct stat statbuf;
			if (::fstat(fd, &statbuf) == 0 && statbuf.st_size == 0 && (statbuf.st_mode & 0777) != mode) {
				RetryAfterSignal(::fchmod, fd, mode);
			}
		}
#if defined(FD_CLOEXEC) && (!defined(O_CLOEXEC) || O_CLOEXEC == 0)
		RetryAfterSignal(::fcntl, fd, F_SETFD, RetryAfterSignal(::fcntl, fd, F_GETFD, 0) | FD_CLOEXEC);
#endif
	}
	CHECK_OPEN(fd);
	return fd;
}

int close(int fd) {
	// Make sure we don't ever close 0, 1 or 2 file descriptors
	ASSERT(fd != -1 && fd >= XAPIAND_MINIMUM_FILE_DESCRIPTOR);
	if unlikely(fd == -1 || fd >= XAPIAND_MINIMUM_FILE_DESCRIPTOR) {
		CHECK_CLOSING(fd);
		return ::close(fd);  // IMPORTANT: don't check EINTR (do not use RetryAfterSignal here)
	}
	errno = EBADF;
	return -1;
}

ssize_t write(int fd, const void* buf, size_t nbyte) {
	L_CALL("io::write(%d, <buf>, %lu)", fd, nbyte);
	CHECK_OPENED("during write()", fd);

	const auto* p = static_cast<const char*>(buf);
	while (nbyte != 0u) {
		ssize_t c = ::write(fd, p, nbyte);
		if unlikely(c == -1) {
			L_ERRNO("io::write() -> %s (%d): %s [%llu]", error::name(errno), errno, error::description(errno), p - static_cast<const char*>(buf));
			if unlikely(errno == EINTR && ignore_eintr().load()) {
				continue;
			}
			size_t written = p - static_cast<const char*>(buf);
			if (written == 0) {
				return -1;
			}
			return written;
		}
		p += c;
		if likely(c == static_cast<ssize_t>(nbyte)) {
			break;
		}
		nbyte -= c;
	}
	return p - static_cast<const char*>(buf);
}

ssize_t pwrite(int fd, const void* buf, size_t nbyte, off_t offset) {
	L_CALL("io::pwrite(%d, <buf>, %lu, %lu)", fd, nbyte, offset);
	CHECK_OPENED("during pwrite()", fd);

	const auto* p = static_cast<const char*>(buf);
#ifndef HAVE_PWRITE
	if unlikely(::lseek(fd, offset, SEEK_SET) == -1) {
		return -1;
	}
#endif
	while (nbyte != 0u) {
#ifndef HAVE_PWRITE
		ssize_t c = ::write(fd, p, nbyte);
#else
		ssize_t c = ::pwrite(fd, p, nbyte, offset);
#endif
		if unlikely(c == -1) {
			L_ERRNO("io::pwrite() -> %s (%d): %s [%llu]", error::name(errno), errno, error::description(errno), p - static_cast<const char*>(buf));
			if unlikely(errno == EINTR && ignore_eintr().load()) {
				continue;
			}
			size_t written = p - static_cast<const char*>(buf);
			if (written == 0) {
				return -1;
			}
			return written;
		}
		p += c;
		if likely(c == static_cast<ssize_t>(nbyte)) {
			break;
		}
		nbyte -= c;
		offset += c;
	}
	return p - static_cast<const char*>(buf);
}

ssize_t read(int fd, void* buf, size_t nbyte) {
	L_CALL("io::read(%d, <buf>, %lu)", fd, nbyte);
	CHECK_OPENED("during read()", fd);

	auto* p = static_cast<char*>(buf);
	while (nbyte != 0u) {
		ssize_t c = ::read(fd, p, nbyte);
		if unlikely(c == -1) {
			L_ERRNO("io::read() -> %s (%d): %s [%llu]", error::name(errno), errno, error::description(errno), p - static_cast<const char*>(buf));
			if unlikely(errno == EINTR && ignore_eintr().load()) {
				continue;
			}
			size_t read = p - static_cast<const char*>(buf);
			if (read == 0) {
				return -1;
			}
			return read;
		} else if unlikely(c == 0) {
			break;
		}
		p += c;
		if likely(c == static_cast<ssize_t>(nbyte)) {
			break;
		}
		nbyte -= c;
	}
	return p - static_cast<const char*>(buf);
}

ssize_t pread(int fd, void* buf, size_t nbyte, off_t offset) {
	L_CALL("io::pread(%d, <buf>, %lu, %lu)", fd, nbyte, offset);
	CHECK_OPENED("during pread()", fd);

#ifndef HAVE_PWRITE
	if unlikely(::lseek(fd, offset, SEEK_SET) == -1) {
		return -1;
	}
#endif
	auto* p = static_cast<char*>(buf);
	while (nbyte != 0u) {
#ifndef HAVE_PWRITE
		ssize_t c = ::read(fd, p, nbyte);
#else
		ssize_t c = ::pread(fd, p, nbyte, offset);
#endif
		if unlikely(c == -1) {
			L_ERRNO("io::pread() -> %s (%d): %s [%llu]", error::name(errno), errno, error::description(errno), p - static_cast<const char*>(buf));
			if unlikely(errno == EINTR && ignore_eintr().load()) {
				continue;
			}
			size_t read = p - static_cast<const char*>(buf);
			if (read == 0) {
				return -1;
			}
			return read;
		}
		p += c;
		break; // read() doesn't have to read the whole nbytes
		// if likely(c == static_cast<ssize_t>(nbyte)) {
		// 	break;
		// }
		// nbyte -= c;
		// offset += c;
	}
	return p - static_cast<const char*>(buf);
}

#ifndef HAVE_FALLOCATE
int fallocate(int fd, int /* mode */, off_t offset, off_t len) {
	CHECK_OPENED("during fallocate()", fd);
#if defined(HAVE_POSIX_FALLOCATE)
	return posix_fallocate(fd, offset, len);
#elif defined(F_PREALLOCATE)
	// Try to get a continous chunk of disk space
	// {fst_flags, fst_posmode, fst_offset, fst_length, fst_bytesalloc}
	fstore_t store = {F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, offset + len, 0};
	int err = RetryAfterSignal(::fcntl, fd, F_PREALLOCATE, &store);
	if unlikely(err == -1) {
		// Try and allocate space with fragments
		store.fst_flags = F_ALLOCATEALL;
		err = RetryAfterSignal(::fcntl, fd, F_PREALLOCATE, &store);
	}
	if likely(err != -1) {
		RetryAfterSignal(::ftruncate, fd, offset + len);
	}
	return err;
#else
	// The following is copied from fcntlSizeHint in sqlite
	/* If the OS does not have posix_fallocate(), fake it. First use
	 ** ftruncate() to set the file size, then write a single byte to
	 ** the last byte in each block within the extended region. This
	 ** is the same technique used by glibc to implement posix_fallocate()
	 ** on systems that do not have a real fallocate() system call.
	 */

	struct stat buf;
	if (::fstat(fd, &buf)) {
		return -1;
	}

	if (buf.st_size >= offset + len) {
		return -1;
	}

	const int st_blksize = buf.st_blksize;
	if (!st_blksize) {
		return -1;
	}

	if (RetryAfterSignal(::ftruncate, fd, offset + len)) {
		return -1;
	}

	off_t next_offset = ((buf.st_size + 2 * st_blksize - 1) / st_blksize) * st_blksize - 1;  // Next offset to write to
	int written;
	do {
		written = 0;
		if (::lseek(fd, next_offset, SEEK_SET) == next_offset) {
			written = RetryAfterSignal(::write, fd, "", 1);
		}
		next_offset += st_blksize;
	} while (written == 1 && next_offset < offset + len);
	return 0;
#endif
}
#endif

#ifdef XAPIAND_CHECK_IO_FDES
#include <mutex>
#include <bitset>
int check(const char* msg, int fd, int check_set, int check_unset, int set, const char* function, const char* filename, int line) {
	static std::mutex mtx;
	static std::bitset<1024*1024> socket;
	static std::bitset<1024*1024> opened;
	static std::bitset<1024*1024> closed;

	if unlikely(fd == -1) {
		return -1;
	}

	if unlikely(fd >= 1024*1024) {
		L_ERR("fd (%d) is too big to track %s", fd, msg);
		return -1;
	}

	if (fd >= 0 || fd < XAPIAND_MINIMUM_FILE_DESCRIPTOR) {
		return 0;
	}

	std::lock_guard<std::mutex> lk(mtx);

	int currently = (
		(socket.test(fd) ? SOCKET : 0) |
		(opened.test(fd) ? OPENED : 0) |
		(closed.test(fd) ? CLOSED : 0)
	);

	if (currently & SOCKET) {
		if (check_unset & SOCKET) {
			L_ERR("fd (%d) is a socket %s" + DEBUG_COL + "%s", fd, msg, ::traceback(function, filename, line, 8));
		}
	} else {
		if (check_set & SOCKET) {
			L_ERR("fd (%d) is not a socket %s" + DEBUG_COL + "%s", fd, msg, ::traceback(function, filename, line, 8));
		}
	}
	if (currently & OPENED) {
		if (check_unset & OPENED) {
			L_ERR("fd (%d) is opened %s" + DEBUG_COL + "%s", fd, msg, ::traceback(function, filename, line, 8));
		}
	} else {
		if (check_set & OPENED) {
			L_ERR("fd (%d) is not opened %s" + DEBUG_COL + "%s", fd, msg, ::traceback(function, filename, line, 8));
		}
	}
	if (currently & CLOSED) {
		if (check_unset & CLOSED) {
			L_ERR("fd (%d) is closed %s" + DEBUG_COL + "%s", fd, msg, ::traceback(function, filename, line, 8));
		}
	} else {
		if (check_set & CLOSED) {
			L_ERR("fd (%d) is not closed %s" + DEBUG_COL + "%s", fd, msg, ::traceback(function, filename, line, 8));
		}
	}

	if (set & SOCKET) {
		socket.set(fd);
	}
	if (set & OPENED) {
		opened.set(fd);
	}
	if (set & CLOSED) {
		closed.set(fd);
	}

	return currently;
}
#endif

} /* namespace io */

#ifdef XAPIAND_CHECK_IO_FDES
#include <sysexits.h>                       // for EX_SOFTWARE
int close(int fd) {
	static int honeypot = io::RetryAfterSignal(::open, "/tmp/xapiand.honeypot", O_RDWR | O_CREAT | O_TRUNC, 0644);
	if unlikely(honeypot == -1) {
		L_ERR("honeypot -> %s (%d): %s", error::name(errno), errno, error::description(errno));
		exit(EX_SOFTWARE);
	}
	CHECK_CLOSE(fd);
	return io::RetryAfterSignal(::dup2, honeypot, fd);
}
#endif

#pragma GCC diagnostic pop
<commit_msg>IO: Fix io::close() assert (it should take -1)<commit_after>/*
 * Copyright (C) 2015-2018 Dubalu LLC. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "io.hh"

#include <errno.h>                  // for errno

#include "cassert.h"                // for ASSERT
#include "error.hh"                 // for error:name, error::description
#include "exception.h"              // for traceback
#include "likely.h"                 // for likely, unlikely
#include "log.h"                    // for L_CALL, L_ERRNO

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wgnu-statement-expression"

namespace io {

std::atomic_bool& ignore_eintr() {
	static std::atomic_bool ignore_eintr = true;
	return ignore_eintr;
}

int open(const char* path, int oflag, int mode) {
	int fd;
	while (true) {
#ifdef O_CLOEXEC
		fd = ::open(path, oflag | O_CLOEXEC, mode);
#else
		fd = ::open(path, oflag, mode);
#endif
		if unlikely(fd == -1) {
			if unlikely(errno == EINTR && ignore_eintr().load()) {
				continue;
			}
			break;
		}
		if (fd >= XAPIAND_MINIMUM_FILE_DESCRIPTOR) {
			break;
		}
		::close(fd);
		fd = -1;
		if unlikely(RetryAfterSignal(::open, "/dev/null", oflag, mode) == -1) {
			break;
		}
	}
	if (fd != -1) {
		if (mode != 0) {
			struct stat statbuf;
			if (::fstat(fd, &statbuf) == 0 && statbuf.st_size == 0 && (statbuf.st_mode & 0777) != mode) {
				RetryAfterSignal(::fchmod, fd, mode);
			}
		}
#if defined(FD_CLOEXEC) && (!defined(O_CLOEXEC) || O_CLOEXEC == 0)
		RetryAfterSignal(::fcntl, fd, F_SETFD, RetryAfterSignal(::fcntl, fd, F_GETFD, 0) | FD_CLOEXEC);
#endif
	}
	CHECK_OPEN(fd);
	return fd;
}

int close(int fd) {
	// Make sure we don't ever close 0, 1 or 2 file descriptors
	ASSERT(fd == -1 || fd >= XAPIAND_MINIMUM_FILE_DESCRIPTOR);
	if likely(fd == -1 || fd >= XAPIAND_MINIMUM_FILE_DESCRIPTOR) {
		CHECK_CLOSING(fd);
		return ::close(fd);  // IMPORTANT: don't check EINTR (do not use RetryAfterSignal here)
	}
	errno = EBADF;
	return -1;
}

ssize_t write(int fd, const void* buf, size_t nbyte) {
	L_CALL("io::write(%d, <buf>, %lu)", fd, nbyte);
	CHECK_OPENED("during write()", fd);

	const auto* p = static_cast<const char*>(buf);
	while (nbyte != 0u) {
		ssize_t c = ::write(fd, p, nbyte);
		if unlikely(c == -1) {
			L_ERRNO("io::write() -> %s (%d): %s [%llu]", error::name(errno), errno, error::description(errno), p - static_cast<const char*>(buf));
			if unlikely(errno == EINTR && ignore_eintr().load()) {
				continue;
			}
			size_t written = p - static_cast<const char*>(buf);
			if (written == 0) {
				return -1;
			}
			return written;
		}
		p += c;
		if likely(c == static_cast<ssize_t>(nbyte)) {
			break;
		}
		nbyte -= c;
	}
	return p - static_cast<const char*>(buf);
}

ssize_t pwrite(int fd, const void* buf, size_t nbyte, off_t offset) {
	L_CALL("io::pwrite(%d, <buf>, %lu, %lu)", fd, nbyte, offset);
	CHECK_OPENED("during pwrite()", fd);

	const auto* p = static_cast<const char*>(buf);
#ifndef HAVE_PWRITE
	if unlikely(::lseek(fd, offset, SEEK_SET) == -1) {
		return -1;
	}
#endif
	while (nbyte != 0u) {
#ifndef HAVE_PWRITE
		ssize_t c = ::write(fd, p, nbyte);
#else
		ssize_t c = ::pwrite(fd, p, nbyte, offset);
#endif
		if unlikely(c == -1) {
			L_ERRNO("io::pwrite() -> %s (%d): %s [%llu]", error::name(errno), errno, error::description(errno), p - static_cast<const char*>(buf));
			if unlikely(errno == EINTR && ignore_eintr().load()) {
				continue;
			}
			size_t written = p - static_cast<const char*>(buf);
			if (written == 0) {
				return -1;
			}
			return written;
		}
		p += c;
		if likely(c == static_cast<ssize_t>(nbyte)) {
			break;
		}
		nbyte -= c;
		offset += c;
	}
	return p - static_cast<const char*>(buf);
}

ssize_t read(int fd, void* buf, size_t nbyte) {
	L_CALL("io::read(%d, <buf>, %lu)", fd, nbyte);
	CHECK_OPENED("during read()", fd);

	auto* p = static_cast<char*>(buf);
	while (nbyte != 0u) {
		ssize_t c = ::read(fd, p, nbyte);
		if unlikely(c == -1) {
			L_ERRNO("io::read() -> %s (%d): %s [%llu]", error::name(errno), errno, error::description(errno), p - static_cast<const char*>(buf));
			if unlikely(errno == EINTR && ignore_eintr().load()) {
				continue;
			}
			size_t read = p - static_cast<const char*>(buf);
			if (read == 0) {
				return -1;
			}
			return read;
		} else if unlikely(c == 0) {
			break;
		}
		p += c;
		if likely(c == static_cast<ssize_t>(nbyte)) {
			break;
		}
		nbyte -= c;
	}
	return p - static_cast<const char*>(buf);
}

ssize_t pread(int fd, void* buf, size_t nbyte, off_t offset) {
	L_CALL("io::pread(%d, <buf>, %lu, %lu)", fd, nbyte, offset);
	CHECK_OPENED("during pread()", fd);

#ifndef HAVE_PWRITE
	if unlikely(::lseek(fd, offset, SEEK_SET) == -1) {
		return -1;
	}
#endif
	auto* p = static_cast<char*>(buf);
	while (nbyte != 0u) {
#ifndef HAVE_PWRITE
		ssize_t c = ::read(fd, p, nbyte);
#else
		ssize_t c = ::pread(fd, p, nbyte, offset);
#endif
		if unlikely(c == -1) {
			L_ERRNO("io::pread() -> %s (%d): %s [%llu]", error::name(errno), errno, error::description(errno), p - static_cast<const char*>(buf));
			if unlikely(errno == EINTR && ignore_eintr().load()) {
				continue;
			}
			size_t read = p - static_cast<const char*>(buf);
			if (read == 0) {
				return -1;
			}
			return read;
		}
		p += c;
		break; // read() doesn't have to read the whole nbytes
		// if likely(c == static_cast<ssize_t>(nbyte)) {
		// 	break;
		// }
		// nbyte -= c;
		// offset += c;
	}
	return p - static_cast<const char*>(buf);
}

#ifndef HAVE_FALLOCATE
int fallocate(int fd, int /* mode */, off_t offset, off_t len) {
	CHECK_OPENED("during fallocate()", fd);
#if defined(HAVE_POSIX_FALLOCATE)
	return posix_fallocate(fd, offset, len);
#elif defined(F_PREALLOCATE)
	// Try to get a continous chunk of disk space
	// {fst_flags, fst_posmode, fst_offset, fst_length, fst_bytesalloc}
	fstore_t store = {F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, offset + len, 0};
	int err = RetryAfterSignal(::fcntl, fd, F_PREALLOCATE, &store);
	if unlikely(err == -1) {
		// Try and allocate space with fragments
		store.fst_flags = F_ALLOCATEALL;
		err = RetryAfterSignal(::fcntl, fd, F_PREALLOCATE, &store);
	}
	if likely(err != -1) {
		RetryAfterSignal(::ftruncate, fd, offset + len);
	}
	return err;
#else
	// The following is copied from fcntlSizeHint in sqlite
	/* If the OS does not have posix_fallocate(), fake it. First use
	 ** ftruncate() to set the file size, then write a single byte to
	 ** the last byte in each block within the extended region. This
	 ** is the same technique used by glibc to implement posix_fallocate()
	 ** on systems that do not have a real fallocate() system call.
	 */

	struct stat buf;
	if (::fstat(fd, &buf)) {
		return -1;
	}

	if (buf.st_size >= offset + len) {
		return -1;
	}

	const int st_blksize = buf.st_blksize;
	if (!st_blksize) {
		return -1;
	}

	if (RetryAfterSignal(::ftruncate, fd, offset + len)) {
		return -1;
	}

	off_t next_offset = ((buf.st_size + 2 * st_blksize - 1) / st_blksize) * st_blksize - 1;  // Next offset to write to
	int written;
	do {
		written = 0;
		if (::lseek(fd, next_offset, SEEK_SET) == next_offset) {
			written = RetryAfterSignal(::write, fd, "", 1);
		}
		next_offset += st_blksize;
	} while (written == 1 && next_offset < offset + len);
	return 0;
#endif
}
#endif

#ifdef XAPIAND_CHECK_IO_FDES
#include <mutex>
#include <bitset>
int check(const char* msg, int fd, int check_set, int check_unset, int set, const char* function, const char* filename, int line) {
	static std::mutex mtx;
	static std::bitset<1024*1024> socket;
	static std::bitset<1024*1024> opened;
	static std::bitset<1024*1024> closed;

	if unlikely(fd == -1) {
		return -1;
	}

	if unlikely(fd >= 1024*1024) {
		L_ERR("fd (%d) is too big to track %s", fd, msg);
		return -1;
	}

	if (fd >= 0 || fd < XAPIAND_MINIMUM_FILE_DESCRIPTOR) {
		return 0;
	}

	std::lock_guard<std::mutex> lk(mtx);

	int currently = (
		(socket.test(fd) ? SOCKET : 0) |
		(opened.test(fd) ? OPENED : 0) |
		(closed.test(fd) ? CLOSED : 0)
	);

	if (currently & SOCKET) {
		if (check_unset & SOCKET) {
			L_ERR("fd (%d) is a socket %s" + DEBUG_COL + "%s", fd, msg, ::traceback(function, filename, line, 8));
		}
	} else {
		if (check_set & SOCKET) {
			L_ERR("fd (%d) is not a socket %s" + DEBUG_COL + "%s", fd, msg, ::traceback(function, filename, line, 8));
		}
	}
	if (currently & OPENED) {
		if (check_unset & OPENED) {
			L_ERR("fd (%d) is opened %s" + DEBUG_COL + "%s", fd, msg, ::traceback(function, filename, line, 8));
		}
	} else {
		if (check_set & OPENED) {
			L_ERR("fd (%d) is not opened %s" + DEBUG_COL + "%s", fd, msg, ::traceback(function, filename, line, 8));
		}
	}
	if (currently & CLOSED) {
		if (check_unset & CLOSED) {
			L_ERR("fd (%d) is closed %s" + DEBUG_COL + "%s", fd, msg, ::traceback(function, filename, line, 8));
		}
	} else {
		if (check_set & CLOSED) {
			L_ERR("fd (%d) is not closed %s" + DEBUG_COL + "%s", fd, msg, ::traceback(function, filename, line, 8));
		}
	}

	if (set & SOCKET) {
		socket.set(fd);
	}
	if (set & OPENED) {
		opened.set(fd);
	}
	if (set & CLOSED) {
		closed.set(fd);
	}

	return currently;
}
#endif

} /* namespace io */

#ifdef XAPIAND_CHECK_IO_FDES
#include <sysexits.h>                       // for EX_SOFTWARE
int close(int fd) {
	static int honeypot = io::RetryAfterSignal(::open, "/tmp/xapiand.honeypot", O_RDWR | O_CREAT | O_TRUNC, 0644);
	if unlikely(honeypot == -1) {
		L_ERR("honeypot -> %s (%d): %s", error::name(errno), errno, error::description(errno));
		exit(EX_SOFTWARE);
	}
	CHECK_CLOSE(fd);
	return io::RetryAfterSignal(::dup2, honeypot, fd);
}
#endif

#pragma GCC diagnostic pop
<|endoftext|>


<commit_before>// Copyright 2021 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "bloaty.h"
#include "absl/strings/substitute.h"
#include "util.h"

using absl::string_view;

namespace bloaty {
namespace pe {
const uint16_t dos_magic = 0x5A4D;  // MZ

//! Sizes in bytes of various things in the COFF format.
namespace STRUCT_SIZES {
enum {
  Header16Size = 20,
  Header32Size = 56,
  NameSize = 8,
  Symbol16Size = 18,
  Symbol32Size = 20,
  SectionSize = 40,
  RelocationSize = 10,
  BaseRelocationBlockSize = 8,
  ImportDirectoryTableEntrySize = 20,
  ResourceDirectoryTableSize = 16,
  ResourceDirectoryEntriesSize = 8,
  ResourceDataEntrySize = 16
};
}

#include "third_party/lief_pe/pe_structures.h"

static_assert(STRUCT_SIZES::SectionSize == sizeof(pe_section), "Compiler options broke LIEF struct layout");
static_assert(STRUCT_SIZES::RelocationSize == sizeof(pe_relocation), "Compiler options broke LIEF struct layout");
static_assert(STRUCT_SIZES::BaseRelocationBlockSize ==
                  sizeof(pe_base_relocation_block),
              "Compiler options broke LIEF struct layout");
static_assert(STRUCT_SIZES::ImportDirectoryTableEntrySize == sizeof(pe_import),
              "Compiler options broke LIEF struct layout");
static_assert(STRUCT_SIZES::ResourceDirectoryTableSize ==
                  sizeof(pe_resource_directory_table),
              "Compiler options broke LIEF struct layout");
static_assert(STRUCT_SIZES::ResourceDirectoryEntriesSize ==
                  sizeof(pe_resource_directory_entries),
              "Compiler options broke LIEF struct layout");
static_assert(STRUCT_SIZES::ResourceDataEntrySize ==
                  sizeof(pe_resource_data_entry),
              "Compiler options broke LIEF struct layout");

class PeFile {
 public:
  PeFile(string_view data) : data_(data) { ok_ = Initialize(); }

  bool IsOpen() const { return ok_; }

  string_view header_region() const { return header_region_; }

  uint32_t section_count() const { return section_count_; }
  string_view section_headers() const { return section_headers_; }
  string_view section_header(size_t n) const {
    return StrictSubstr(section_headers_, n * sizeof(pe_section),
                        sizeof(pe_section));
  }

 private:
  bool Initialize();

  string_view GetRegion(uint64_t start, uint64_t n) const {
    return StrictSubstr(data_, start, n);
  }

  bool ok_;
  bool is_64bit_;
  string_view data_;

  pe_dos_header dos_header_;
  pe_header pe_header_;
  string_view header_region_;
  uint32_t section_count_;
  string_view section_headers_;
};

bool PeFile::Initialize() {
  if (data_.size() < sizeof(dos_header_)) {
    return false;
  }

  memcpy(&dos_header_, data_.data(), sizeof(dos_header_));

  if (dos_header_.Magic != dos_magic) {
    // Not a PE file.
    return false;
  }

  if ((dos_header_.AddressOfNewExeHeader + sizeof(pe_header)) > data_.size()) {
    // Cannot fit the headers
    return false;
  }

  memcpy(&pe_header_, data_.data() + dos_header_.AddressOfNewExeHeader,
         sizeof(pe_header_));

  if (!std::equal(pe_header_.signature, pe_header_.signature + sizeof(PE_Magic),
                  std::begin(PE_Magic))) {
    // Not a PE file.
    return false;
  }

  // TODO(mj): Parse PE header further to determine this
  is_64bit_ = false;

  section_count_ = pe_header_.NumberOfSections;

  const uint32_t sections_offset = dos_header_.AddressOfNewExeHeader +
                                   sizeof(pe_header) +
                                   pe_header_.SizeOfOptionalHeader;

  auto sections_size = CheckedMul(section_count_, sizeof(pe_section));
  if ((sections_offset + sections_size) > data_.size()) {
    // Cannot fit the headers
    return false;
  }

  header_region_ = GetRegion(0, sections_offset);
  section_headers_ = GetRegion(sections_offset, sections_size);

  return true;
}

class Section {
 public:
  std::string name;
  string_view data;

  uint32_t raw_offset() const { return header_.PointerToRawData; }
  uint32_t raw_size() const { return header_.SizeOfRawData; }

  uint32_t virtual_addr() const { return header_.VirtualAddress; }
  uint32_t virtual_size() const { return header_.VirtualSize; }

  Section(string_view header_data) {
    assert(header_data.size() == sizeof(header_));
    memcpy(&header_, header_data.data(), sizeof(header_));
    data = header_data;

    // TODO(mj): Handle long section names:
    // For longer names, this member contains a forward slash (/) followed by an
    // ASCII representation of a decimal number that is an offset into the
    // string table.
    name = std::string(header_.Name,
                       strnlen(header_.Name, STRUCT_SIZES::NameSize));
  }

 private:
  pe_section header_;
};

template <class Func>
void ForEachSection(const PeFile& pe, Func&& section_func) {
  for (auto n = 0; n < pe.section_count(); ++n) {
    Section section(pe.section_header(n));
    section_func(section);
  }
}

void ParseSections(const PeFile& pe, RangeSink* sink) {
  assert(pe.IsOpen());
  ForEachSection(pe, [sink](const Section& section) {
    uint64_t vmaddr = section.virtual_addr();
    uint64_t vmsize = section.virtual_size();

    uint64_t fileoff = section.raw_offset();
    uint64_t filesize = section.raw_size();

    sink->AddRange("pe_sections", section.name, vmaddr, vmsize, fileoff,
                   filesize);
  });
}

void AddCatchAll(const PeFile& pe, RangeSink* sink) {
  // The last-line fallback to make sure we cover the entire VM space.
  assert(pe.IsOpen());

  auto begin = pe.header_region().data() - sink->input_file().data().data();
  sink->AddRange("pe_catchall", "[PE Headers]", begin,
                 pe.header_region().size(), pe.header_region());
  begin = pe.section_headers().data() - sink->input_file().data().data();
  sink->AddRange("pe_catchall", "[PE Headers]", begin,
                 pe.section_headers().size(), pe.section_headers());

  // The last-line fallback to make sure we cover the entire file.
  sink->AddFileRange("pe_catchall", "[Unmapped]", sink->input_file().data());
}

class PEObjectFile : public ObjectFile {
 public:
  PEObjectFile(std::unique_ptr<InputFile> file_data,
               std::unique_ptr<pe::PeFile> pe)
      : ObjectFile(std::move(file_data)), pe_file(std::move(pe)) {}

  std::string GetBuildId() const override {
    // TODO(mj): Read from pe_pdb_??
    return std::string();
  }

  void ProcessFile(const std::vector<RangeSink*>& sinks) const override {
    for (auto sink : sinks) {
      switch (sink->data_source()) {
        case DataSource::kSegments:
          // TODO(mj): sections: list out imports and other stuff!
        case DataSource::kSections:
          ParseSections(*pe_file, sink);
          break;
        case DataSource::kSymbols:
        case DataSource::kRawSymbols:
        case DataSource::kShortSymbols:
        case DataSource::kFullSymbols:
          // TODO(mj): Generate symbols from debug info, exports and other known
          // structures
        case DataSource::kArchiveMembers:
        case DataSource::kCompileUnits:
        case DataSource::kInlines:
        default:
          THROW("PE doesn't support this data source");
      }
      AddCatchAll(*pe_file, sink);
    }
  }

  bool GetDisassemblyInfo(absl::string_view /*symbol*/,
                          DataSource /*symbol_source*/,
                          DisassemblyInfo* /*info*/) const override {
    WARN("PE files do not support disassembly yet");
    return false;
  }

 protected:
  std::unique_ptr<pe::PeFile> pe_file;
};

bool ReadMagic(const string_view& data) {
  // If the size is smaller than a dos header, it cannot be a PE file, right?
  if (data.size() < sizeof(pe_dos_header)) {
    return false;
  }

  uint16_t magic;
  memcpy(&magic, data.data(), sizeof(magic));

  return magic == dos_magic;
}
}  // namespace pe

std::unique_ptr<ObjectFile> TryOpenPEFile(std::unique_ptr<InputFile>& file) {
  // Do not bother creating an object if the first magic is not even there
  if (pe::ReadMagic(file->data())) {
    std::unique_ptr<pe::PeFile> pe(new pe::PeFile(file->data()));

    if (pe->IsOpen()) {
      return std::unique_ptr<ObjectFile>(
          new pe::PEObjectFile(std::move(file), std::move(pe)));
    }
  }

  return nullptr;
}

}  // namespace bloaty
<commit_msg>PE: use an `enum class` rather than `namespace` (NFC)<commit_after>// Copyright 2021 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "bloaty.h"
#include "absl/strings/substitute.h"
#include "util.h"

using absl::string_view;

namespace bloaty {
namespace pe {
const uint16_t dos_magic = 0x5A4D;  // MZ

//! Sizes in bytes of various things in the COFF format.
enum class StructSizes {
  kHeader16Size = 20,
  kHeader32Size = 56,
  kNameSize = 8,
  kSymbol16Size = 18,
  kSymbol32Size = 20,
  kSectionSize = 40,
  kRelocationSize = 10,
  kBaseRelocationBlockSize = 8,
  kImportDirectoryTableEntrySize = 20,
  kResourceDirectoryTableSize = 16,
  kResourceDirectoryEntriesSize = 8,
  kResourceDataEntrySize = 16,
};

#include "third_party/lief_pe/pe_structures.h"

static_assert(static_cast<size_t>(StructSizes::kSectionSize) ==
                  sizeof(pe_section),
              "Compiler options broke LIEF struct layout");
static_assert(static_cast<size_t>(StructSizes::kRelocationSize) ==
                  sizeof(pe_relocation),
              "Compiler options broke LIEF struct layout");
static_assert(static_cast<size_t>(StructSizes::kBaseRelocationBlockSize) ==
                  sizeof(pe_base_relocation_block),
              "Compiler options broke LIEF struct layout");
static_assert(
    static_cast<size_t>(StructSizes::kImportDirectoryTableEntrySize) ==
        sizeof(pe_import),
    "Compiler options broke LIEF struct layout");
static_assert(static_cast<size_t>(StructSizes::kResourceDirectoryTableSize) ==
                  sizeof(pe_resource_directory_table),
              "Compiler options broke LIEF struct layout");
static_assert(static_cast<size_t>(StructSizes::kResourceDirectoryEntriesSize) ==
                  sizeof(pe_resource_directory_entries),
              "Compiler options broke LIEF struct layout");
static_assert(static_cast<size_t>(StructSizes::kResourceDataEntrySize) ==
                  sizeof(pe_resource_data_entry),
              "Compiler options broke LIEF struct layout");

class PeFile {
 public:
  PeFile(string_view data) : data_(data) { ok_ = Initialize(); }

  bool IsOpen() const { return ok_; }

  string_view header_region() const { return header_region_; }

  uint32_t section_count() const { return section_count_; }
  string_view section_headers() const { return section_headers_; }
  string_view section_header(size_t n) const {
    return StrictSubstr(section_headers_, n * sizeof(pe_section),
                        sizeof(pe_section));
  }

 private:
  bool Initialize();

  string_view GetRegion(uint64_t start, uint64_t n) const {
    return StrictSubstr(data_, start, n);
  }

  bool ok_;
  bool is_64bit_;
  string_view data_;

  pe_dos_header dos_header_;
  pe_header pe_header_;
  string_view header_region_;
  uint32_t section_count_;
  string_view section_headers_;
};

bool PeFile::Initialize() {
  if (data_.size() < sizeof(dos_header_)) {
    return false;
  }

  memcpy(&dos_header_, data_.data(), sizeof(dos_header_));

  if (dos_header_.Magic != dos_magic) {
    // Not a PE file.
    return false;
  }

  if ((dos_header_.AddressOfNewExeHeader + sizeof(pe_header)) > data_.size()) {
    // Cannot fit the headers
    return false;
  }

  memcpy(&pe_header_, data_.data() + dos_header_.AddressOfNewExeHeader,
         sizeof(pe_header_));

  if (!std::equal(pe_header_.signature, pe_header_.signature + sizeof(PE_Magic),
                  std::begin(PE_Magic))) {
    // Not a PE file.
    return false;
  }

  // TODO(mj): Parse PE header further to determine this
  is_64bit_ = false;

  section_count_ = pe_header_.NumberOfSections;

  const uint32_t sections_offset = dos_header_.AddressOfNewExeHeader +
                                   sizeof(pe_header) +
                                   pe_header_.SizeOfOptionalHeader;

  auto sections_size = CheckedMul(section_count_, sizeof(pe_section));
  if ((sections_offset + sections_size) > data_.size()) {
    // Cannot fit the headers
    return false;
  }

  header_region_ = GetRegion(0, sections_offset);
  section_headers_ = GetRegion(sections_offset, sections_size);

  return true;
}

class Section {
 public:
  std::string name;
  string_view data;

  uint32_t raw_offset() const { return header_.PointerToRawData; }
  uint32_t raw_size() const { return header_.SizeOfRawData; }

  uint32_t virtual_addr() const { return header_.VirtualAddress; }
  uint32_t virtual_size() const { return header_.VirtualSize; }

  Section(string_view header_data) {
    assert(header_data.size() == sizeof(header_));
    memcpy(&header_, header_data.data(), sizeof(header_));
    data = header_data;

    // TODO(mj): Handle long section names:
    // For longer names, this member contains a forward slash (/) followed by an
    // ASCII representation of a decimal number that is an offset into the
    // string table.
    name = std::string(
        header_.Name,
        strnlen(header_.Name, static_cast<size_t>(StructSizes::kNameSize)));
  }

 private:
  pe_section header_;
};

template <class Func>
void ForEachSection(const PeFile& pe, Func&& section_func) {
  for (auto n = 0; n < pe.section_count(); ++n) {
    Section section(pe.section_header(n));
    section_func(section);
  }
}

void ParseSections(const PeFile& pe, RangeSink* sink) {
  assert(pe.IsOpen());
  ForEachSection(pe, [sink](const Section& section) {
    uint64_t vmaddr = section.virtual_addr();
    uint64_t vmsize = section.virtual_size();

    uint64_t fileoff = section.raw_offset();
    uint64_t filesize = section.raw_size();

    sink->AddRange("pe_sections", section.name, vmaddr, vmsize, fileoff,
                   filesize);
  });
}

void AddCatchAll(const PeFile& pe, RangeSink* sink) {
  // The last-line fallback to make sure we cover the entire VM space.
  assert(pe.IsOpen());

  auto begin = pe.header_region().data() - sink->input_file().data().data();
  sink->AddRange("pe_catchall", "[PE Headers]", begin,
                 pe.header_region().size(), pe.header_region());
  begin = pe.section_headers().data() - sink->input_file().data().data();
  sink->AddRange("pe_catchall", "[PE Headers]", begin,
                 pe.section_headers().size(), pe.section_headers());

  // The last-line fallback to make sure we cover the entire file.
  sink->AddFileRange("pe_catchall", "[Unmapped]", sink->input_file().data());
}

class PEObjectFile : public ObjectFile {
 public:
  PEObjectFile(std::unique_ptr<InputFile> file_data,
               std::unique_ptr<pe::PeFile> pe)
      : ObjectFile(std::move(file_data)), pe_file(std::move(pe)) {}

  std::string GetBuildId() const override {
    // TODO(mj): Read from pe_pdb_??
    return std::string();
  }

  void ProcessFile(const std::vector<RangeSink*>& sinks) const override {
    for (auto sink : sinks) {
      switch (sink->data_source()) {
        case DataSource::kSegments:
          // TODO(mj): sections: list out imports and other stuff!
        case DataSource::kSections:
          ParseSections(*pe_file, sink);
          break;
        case DataSource::kSymbols:
        case DataSource::kRawSymbols:
        case DataSource::kShortSymbols:
        case DataSource::kFullSymbols:
          // TODO(mj): Generate symbols from debug info, exports and other known
          // structures
        case DataSource::kArchiveMembers:
        case DataSource::kCompileUnits:
        case DataSource::kInlines:
        default:
          THROW("PE doesn't support this data source");
      }
      AddCatchAll(*pe_file, sink);
    }
  }

  bool GetDisassemblyInfo(absl::string_view /*symbol*/,
                          DataSource /*symbol_source*/,
                          DisassemblyInfo* /*info*/) const override {
    WARN("PE files do not support disassembly yet");
    return false;
  }

 protected:
  std::unique_ptr<pe::PeFile> pe_file;
};

bool ReadMagic(const string_view& data) {
  // If the size is smaller than a dos header, it cannot be a PE file, right?
  if (data.size() < sizeof(pe_dos_header)) {
    return false;
  }

  uint16_t magic;
  memcpy(&magic, data.data(), sizeof(magic));

  return magic == dos_magic;
}
}  // namespace pe

std::unique_ptr<ObjectFile> TryOpenPEFile(std::unique_ptr<InputFile>& file) {
  // Do not bother creating an object if the first magic is not even there
  if (pe::ReadMagic(file->data())) {
    std::unique_ptr<pe::PeFile> pe(new pe::PeFile(file->data()));

    if (pe->IsOpen()) {
      return std::unique_ptr<ObjectFile>(
          new pe::PEObjectFile(std::move(file), std::move(pe)));
    }
  }

  return nullptr;
}

}  // namespace bloaty
<|endoftext|>


<commit_before>#include "types.h"
#include "kernel.hh"
#include "spinlock.h"
#include "condvar.h"
#include "fs.h"
#include "file.hh"
#include <uk/stat.h>
#include "net.hh"

struct devsw __mpalign__ devsw[NDEV];

int
file_inode::stat(struct stat *st, enum stat_flags flags)
{
  u8 stattype = 0;
  switch (ip->type()) {
  case mnode::types::dir:  stattype = T_DIR;  break;
  case mnode::types::file: stattype = T_FILE; break;
  case mnode::types::dev:  stattype = T_DEV;  break;
  default:                 cprintf("Unknown type %d\n", ip->type());
  }

  st->st_mode = stattype << __S_IFMT_SHIFT;
  st->st_dev = 1;
  st->st_ino = ip->inum_;
  if (!(flags & STAT_OMIT_NLINK))
    st->st_nlink = ip->nlink_.get_consistent();
  st->st_size = 0;
  if (ip->type() == mnode::types::file)
    st->st_size = *ip->as_file()->read_size();
  if (ip->type() == mnode::types::dev &&
      ip->as_dev()->major() < NDEV &&
      devsw[ip->as_dev()->major()].stat)
    devsw[ip->as_dev()->major()].stat(ip->as_dev(), st);
  return 0;
}

ssize_t
file_inode::read(char *addr, size_t n)
{
  if (!readable)
    return -1;

  lock_guard<sleeplock> l;
  ssize_t r;
  if (ip->type() == mnode::types::dev) {
    u16 major = ip->as_dev()->major();
    if (major >= NDEV)
      return -1;
    if (devsw[major].read) {
      return devsw[major].read(ip->as_dev(), addr, n);
    } else if (devsw[major].pread) {
      l = off_lock.guard();
      r = devsw[major].pread(ip->as_dev(), addr, off, n);
    } else {
      return -1;
    }
  } else {
    l = off_lock.guard();
    r = readi(ip, addr, off, n);
  }
  if (r > 0)
    off += r;
  return r;
}

ssize_t
file_inode::write(const char *addr, size_t n)
{
  if (!writable)
    return -1;

  lock_guard<sleeplock> l;
  ssize_t r;
  if (ip->type() == mnode::types::dev) {
    u16 major = ip->as_dev()->major();
    if (major >= NDEV)
      return -1;
    if (devsw[major].write) {
      return devsw[major].write(ip->as_dev(), addr, n);
    } else if (devsw[major].pwrite) {
      l = off_lock.guard();
      r = devsw[major].pwrite(ip->as_dev(), addr, off, n);
    } else {
      return -1;
    }
  } else if (ip->type() == mnode::types::file) {
    l = off_lock.guard();
    mfile::resizer resize;
    if (append) {
      resize = ip->as_file()->write_size();
      off = resize.read_size();
    }

    r = writei(ip, addr, off, n, append ? &resize : nullptr);
  } else {
    return -1;
  }

  if (r > 0)
    off += r;
  return r;
}

ssize_t
file_inode::pread(char *addr, size_t n, off_t off)
{
  if (!readable)
    return -1;
  if (ip->type() == mnode::types::dev) {
    u16 major = ip->as_dev()->major();
    if (major >= NDEV || !devsw[major].pread)
      return -1;
    return devsw[major].pread(ip->as_dev(), addr, off, n);
  }
  return readi(ip, addr, off, n);
}

ssize_t
file_inode::pwrite(const char *addr, size_t n, off_t off)
{
  if (!writable)
    return -1;
  if (ip->type() == mnode::types::dev) {
    u16 major = ip->as_dev()->major();
    if (major >= NDEV || !devsw[major].pwrite)
      return -1;
    return devsw[major].pwrite(ip->as_dev(), addr, off, n);
  }
  return writei(ip, addr, off, n);
}

ssize_t
file_pipe_reader::read(char *addr, size_t n)
{
  return piperead(pipe, addr, n);
}

void
file_pipe_reader::onzero(void)
{
  pipeclose(pipe, false);
  delete this;
}

ssize_t
file_pipe_writer::write(const char *addr, size_t n)
{
  return pipewrite(pipe, addr, n);
}

void
file_pipe_writer::onzero(void)
{
  pipeclose(pipe, true);
  delete this;
}

ssize_t
file_socket::read(char *addr, size_t n)
{
  auto l = rsem.guard();
  return netread(socket, addr, n);
}

ssize_t
file_socket::write(const char *addr, size_t n)
{
  auto l = wsem.guard();
  return netwrite(socket, addr, n);
}

void
file_socket::onzero()
{
  sockclose(this);
  delete this;
}
<commit_msg>make read*read scale better<commit_after>#include "types.h"
#include "kernel.hh"
#include "spinlock.h"
#include "condvar.h"
#include "fs.h"
#include "file.hh"
#include <uk/stat.h>
#include "net.hh"

struct devsw __mpalign__ devsw[NDEV];

int
file_inode::stat(struct stat *st, enum stat_flags flags)
{
  u8 stattype = 0;
  switch (ip->type()) {
  case mnode::types::dir:  stattype = T_DIR;  break;
  case mnode::types::file: stattype = T_FILE; break;
  case mnode::types::dev:  stattype = T_DEV;  break;
  default:                 cprintf("Unknown type %d\n", ip->type());
  }

  st->st_mode = stattype << __S_IFMT_SHIFT;
  st->st_dev = 1;
  st->st_ino = ip->inum_;
  if (!(flags & STAT_OMIT_NLINK))
    st->st_nlink = ip->nlink_.get_consistent();
  st->st_size = 0;
  if (ip->type() == mnode::types::file)
    st->st_size = *ip->as_file()->read_size();
  if (ip->type() == mnode::types::dev &&
      ip->as_dev()->major() < NDEV &&
      devsw[ip->as_dev()->major()].stat)
    devsw[ip->as_dev()->major()].stat(ip->as_dev(), st);
  return 0;
}

ssize_t
file_inode::read(char *addr, size_t n)
{
  if (!readable)
    return -1;

  lock_guard<sleeplock> l;
  ssize_t r;
  if (ip->type() == mnode::types::dev) {
    u16 major = ip->as_dev()->major();
    if (major >= NDEV)
      return -1;
    if (devsw[major].read) {
      return devsw[major].read(ip->as_dev(), addr, n);
    } else if (devsw[major].pread) {
      l = off_lock.guard();
      r = devsw[major].pread(ip->as_dev(), addr, off, n);
    } else {
      return -1;
    }
  } else if (ip->type() != mnode::types::file) {
    return -1;
  } else {
    if (off >= *ip->as_file()->read_size()) {
      r = 0;
    } else {
      l = off_lock.guard();
      r = readi(ip, addr, off, n);
    }
  }
  if (r > 0)
    off += r;
  return r;
}

ssize_t
file_inode::write(const char *addr, size_t n)
{
  if (!writable)
    return -1;

  lock_guard<sleeplock> l;
  ssize_t r;
  if (ip->type() == mnode::types::dev) {
    u16 major = ip->as_dev()->major();
    if (major >= NDEV)
      return -1;
    if (devsw[major].write) {
      return devsw[major].write(ip->as_dev(), addr, n);
    } else if (devsw[major].pwrite) {
      l = off_lock.guard();
      r = devsw[major].pwrite(ip->as_dev(), addr, off, n);
    } else {
      return -1;
    }
  } else if (ip->type() == mnode::types::file) {
    l = off_lock.guard();
    mfile::resizer resize;
    if (append) {
      resize = ip->as_file()->write_size();
      off = resize.read_size();
    }

    r = writei(ip, addr, off, n, append ? &resize : nullptr);
  } else {
    return -1;
  }

  if (r > 0)
    off += r;
  return r;
}

ssize_t
file_inode::pread(char *addr, size_t n, off_t off)
{
  if (!readable)
    return -1;
  if (ip->type() == mnode::types::dev) {
    u16 major = ip->as_dev()->major();
    if (major >= NDEV || !devsw[major].pread)
      return -1;
    return devsw[major].pread(ip->as_dev(), addr, off, n);
  }
  return readi(ip, addr, off, n);
}

ssize_t
file_inode::pwrite(const char *addr, size_t n, off_t off)
{
  if (!writable)
    return -1;
  if (ip->type() == mnode::types::dev) {
    u16 major = ip->as_dev()->major();
    if (major >= NDEV || !devsw[major].pwrite)
      return -1;
    return devsw[major].pwrite(ip->as_dev(), addr, off, n);
  }
  return writei(ip, addr, off, n);
}

ssize_t
file_pipe_reader::read(char *addr, size_t n)
{
  return piperead(pipe, addr, n);
}

void
file_pipe_reader::onzero(void)
{
  pipeclose(pipe, false);
  delete this;
}

ssize_t
file_pipe_writer::write(const char *addr, size_t n)
{
  return pipewrite(pipe, addr, n);
}

void
file_pipe_writer::onzero(void)
{
  pipeclose(pipe, true);
  delete this;
}

ssize_t
file_socket::read(char *addr, size_t n)
{
  auto l = rsem.guard();
  return netread(socket, addr, n);
}

ssize_t
file_socket::write(const char *addr, size_t n)
{
  auto l = wsem.guard();
  return netwrite(socket, addr, n);
}

void
file_socket::onzero()
{
  sockclose(this);
  delete this;
}
<|endoftext|>


<commit_before>#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <map>
#include <iterator>

template <typename Iter>
void print (Iter i, Iter end)
{
    for (; i != end; std::advance (i, 1))
        std::cout << '\t' << i->first << '\t' << i->second << std::endl;
}

void read_lines (std::istream &in, std::map<std::string, int> &map)
{
    std::string buf;
    while (std::getline (in, buf)) {
        auto iterp = map.insert ({buf, 1});
            if (!iterp.second)
                ++iterp.first->second;
    }
}

int main (int argc, char **argv)
{
    std::map<std::string, int> map;

    bool reverse = false;

    for (char **i = argv + 1; *i; ++i) {
        if (std::strcmp (*i, "-r") == 0) {
            reverse = true;
            continue;
        }

        std::ifstream f (*i);

        if (!f)
            continue;

        read_lines (f, map);
    }

    if (argc - (int)reverse == 1)
        read_lines (std::cin, map);

    std::multimap<int, std::string> revmap;
    for (const auto &p : map)
        revmap.insert ({p.second, p.first});

    if (reverse)
        print (revmap.rbegin (), revmap.rend ());

    else
        print (revmap.begin (), revmap.end ());

    return 0;
}
<commit_msg>Fixed indentation in read_lines<commit_after>#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <map>
#include <iterator>

template <typename Iter>
void print (Iter i, Iter end)
{
    for (; i != end; std::advance (i, 1))
        std::cout << '\t' << i->first << '\t' << i->second << std::endl;
}

void read_lines (std::istream &in, std::map<std::string, int> &map)
{
    std::string buf;
    while (std::getline (in, buf)) {
        auto iterp = map.insert ({buf, 1});
        if (!iterp.second)
            ++iterp.first->second;
    }
}

int main (int argc, char **argv)
{
    std::map<std::string, int> map;

    bool reverse = false;

    for (char **i = argv + 1; *i; ++i) {
        if (std::strcmp (*i, "-r") == 0) {
            reverse = true;
            continue;
        }

        std::ifstream f (*i);

        if (!f)
            continue;

        read_lines (f, map);
    }

    if (argc - (int)reverse == 1)
        read_lines (std::cin, map);

    std::multimap<int, std::string> revmap;
    for (const auto &p : map)
        revmap.insert ({p.second, p.first});

    if (reverse)
        print (revmap.rbegin (), revmap.rend ());

    else
        print (revmap.begin (), revmap.end ());

    return 0;
}
<|endoftext|>