<commit_before>/*
 * Copyright (c) 2010, 2012-2013 ARM Limited
 * All rights reserved
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Copyright (c) 2002-2005 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Ali Saidi
 */

#ifndef __ARCH_ARM_PAGETABLE_H__
#define __ARCH_ARM_PAGETABLE_H__

#include <cstdint>

#include "arch/arm/isa_traits.hh"
#include "arch/arm/utility.hh"
#include "arch/arm/vtophys.hh"
#include "sim/serialize.hh"

namespace ArmISA {

struct VAddr
{
    VAddr(Addr a) { panic("not implemented yet."); }
};

// ITB/DTB page table entry
struct PTE
{
    void serialize(std::ostream &os)
    {
        panic("Need to implement PTE serialization\n");
    }

    void unserialize(Checkpoint *cp, const std::string &section)
    {
        panic("Need to implement PTE serialization\n");
    }

};

// Lookup level
enum LookupLevel {
    L0 = 0,  // AArch64 only
    L1,
    L2,
    L3,
    MAX_LOOKUP_LEVELS
};

// ITB/DTB table entry
struct TlbEntry
{
  public:
    enum class MemoryType : std::uint8_t {
        StronglyOrdered,
        Device,
        Normal
    };

    enum class DomainType : std::uint8_t {
        NoAccess = 0,
        Client,
        Reserved,
        Manager
    };

    // Matching variables
    Addr pfn;
    Addr size;              // Size of this entry, == Type of TLB Rec
    Addr vpn;               // Virtual Page Number
    uint64_t attributes;    // Memory attributes formatted for PAR

    LookupLevel lookupLevel;    // Lookup level where the descriptor was fetched
                                // from.  Used to set the FSR for faults
                                // occurring while the long desc. format is in
                                // use (AArch32 w/ LPAE and AArch64)

    uint16_t asid;          // Address Space Identifier
    uint8_t vmid;           // Virtual machine Identifier
    uint8_t N;              // Number of bits in pagesize
    uint8_t innerAttrs;
    uint8_t outerAttrs;
    uint8_t ap;             // Access permissions bits
    uint8_t hap;            // Hyp access permissions bits
    DomainType domain;         // Access Domain

    MemoryType mtype;

    // True if the long descriptor format is used for this entry (LPAE only)
    bool longDescFormat; // @todo use this in the update attribute bethod

    bool isHyp;
    bool global;
    bool valid;

    // True if the entry targets the non-secure physical address space
    bool ns;
    // True if the entry was brought in from a non-secure page table
    bool nstid;
    // Exception level on insert, AARCH64 EL0&1, AARCH32 -> el=1
    uint8_t el;

    // Type of memory
    bool nonCacheable;     // Can we wrap this in mtype?

    // Memory Attributes
    bool shareable;
    bool outerShareable;

    // Access permissions
    bool xn;                // Execute Never
    bool pxn;               // Privileged Execute Never (LPAE only)

    //Construct an entry that maps to physical address addr for SE mode
    TlbEntry(Addr _asn, Addr _vaddr, Addr _paddr,
             bool uncacheable, bool read_only) :
         pfn(_paddr >> PageShift), size(PageBytes - 1), vpn(_vaddr >> PageShift),
         attributes(0), lookupLevel(L1), asid(_asn), vmid(0), N(0),
         innerAttrs(0), outerAttrs(0), ap(read_only ? 0x3 : 0), hap(0x3),
         domain(DomainType::Client),  mtype(MemoryType::StronglyOrdered),
         longDescFormat(false), isHyp(false), global(false), valid(true),
         ns(true), nstid(true), el(0), nonCacheable(uncacheable),
         shareable(false), outerShareable(false), xn(0), pxn(0)
    {
        // no restrictions by default, hap = 0x3

        // @todo Check the memory type
        if (read_only)
            warn("ARM TlbEntry does not support read-only mappings\n");
    }

    TlbEntry() :
         pfn(0), size(0), vpn(0), attributes(0), lookupLevel(L1), asid(0),
         vmid(0), N(0), innerAttrs(0), outerAttrs(0), ap(0), hap(0x3),
         domain(DomainType::Client), mtype(MemoryType::StronglyOrdered),
         longDescFormat(false), isHyp(false), global(false), valid(true),
         ns(true), nstid(true), el(0), nonCacheable(false),
         shareable(false), outerShareable(false), xn(0), pxn(0)
    {
        // no restrictions by default, hap = 0x3

        // @todo Check the memory type
    }

    void
    updateVaddr(Addr new_vaddr)
    {
        vpn = new_vaddr >> PageShift;
    }

    Addr
    pageStart() const
    {
        return pfn << PageShift;
    }

    bool
    match(Addr va, uint8_t _vmid, bool hypLookUp, bool secure_lookup,
          uint8_t target_el) const
    {
        return match(va, 0, _vmid, hypLookUp, secure_lookup, true, target_el);
    }

    bool
    match(Addr va, uint16_t asn, uint8_t _vmid, bool hypLookUp,
          bool secure_lookup, bool ignore_asn, uint8_t target_el) const
    {
        bool match = false;
        Addr v = vpn << N;

        if (valid && va >= v && va <= v + size && (secure_lookup == !nstid) &&
            (hypLookUp == isHyp))
        {
            if (target_el == 2 || target_el == 3)
                match = (el == target_el);
            else
                match = (el == 0) || (el == 1);
            if (match && !ignore_asn) {
                match = global || (asn == asid);
            }
            if (match && nstid) {
                match = isHyp || (_vmid == vmid);
            }
        }
        return match;
    }

    Addr
    pAddr(Addr va) const
    {
        return (pfn << N) | (va & size);
    }

    void
    updateAttributes()
    {
        uint64_t mask;
        uint64_t newBits;

        // chec bit 11 to determine if its currently LPAE or VMSA format.
        if ( attributes & (1 << 11) ) {
            newBits = ((outerShareable ? 0x2 :
                      shareable         ? 0x3 : 0) << 7);
            mask = 0x180;
        } else {
            /** Formatting for Physical Address Register (PAR)
             *  Only including lower bits (TLB info here)
             *  PAR (32-bit format):
             *  PA   [31:12]
             *  LPAE [11] (Large Physical Address Extension)
             *  TLB info [10:1]
             *      NOS  [10] (Not Outer Sharable)
             *      NS   [9]  (Non-Secure)
             *      --   [8]  (Implementation Defined)
             *      SH   [7]  (Sharable)
             *      Inner[6:4](Inner memory attributes)
             *      Outer[3:2](Outer memory attributes)
             *      SS   [1]  (SuperSection)
             *      F    [0]  (Fault, Fault Status in [6:1] if faulted)
            */
            newBits = ((outerShareable ? 0:1) << 10) |
                      ((shareable ? 1:0) << 7) |
                      (innerAttrs << 4) |
                      (outerAttrs << 2);
                      // TODO: Supersection bit
            mask = 0x4FC;
        }
        // common bits
        newBits |= ns << 9;  // NS bit
        mask    |= 1  << 9;
        // add in the new bits
        attributes &= ~mask;
        attributes |= newBits;
    }

    void
    setAttributes(bool lpae)
    {
        attributes = lpae ? (1 << 11) : 0;
        updateAttributes();
    }

    std::string
    print() const
    {
        return csprintf("%#x, asn %d vmn %d hyp %d ppn %#x size: %#x ap:%d "
                        "ns:%d nstid:%d g:%d el:%d", vpn << N, asid, vmid,
                        isHyp, pfn << N, size, ap, ns, nstid, global, el);
    }

    void
    serialize(std::ostream &os)
    {
        SERIALIZE_SCALAR(longDescFormat);
        SERIALIZE_SCALAR(pfn);
        SERIALIZE_SCALAR(size);
        SERIALIZE_SCALAR(vpn);
        SERIALIZE_SCALAR(asid);
        SERIALIZE_SCALAR(vmid);
        SERIALIZE_SCALAR(isHyp);
        SERIALIZE_SCALAR(N);
        SERIALIZE_SCALAR(global);
        SERIALIZE_SCALAR(valid);
        SERIALIZE_SCALAR(ns);
        SERIALIZE_SCALAR(nstid);
        SERIALIZE_SCALAR(nonCacheable);
        SERIALIZE_ENUM(lookupLevel);
        SERIALIZE_ENUM(mtype);
        SERIALIZE_SCALAR(innerAttrs);
        SERIALIZE_SCALAR(outerAttrs);
        SERIALIZE_SCALAR(shareable);
        SERIALIZE_SCALAR(outerShareable);
        SERIALIZE_SCALAR(attributes);
        SERIALIZE_SCALAR(xn);
        SERIALIZE_SCALAR(pxn);
        SERIALIZE_SCALAR(ap);
        SERIALIZE_SCALAR(hap);
        uint8_t domain_ = static_cast<uint8_t>(domain);
        paramOut(os, "domain", domain_);
    }
    void
    unserialize(Checkpoint *cp, const std::string &section)
    {
        UNSERIALIZE_SCALAR(longDescFormat);
        UNSERIALIZE_SCALAR(pfn);
        UNSERIALIZE_SCALAR(size);
        UNSERIALIZE_SCALAR(vpn);
        UNSERIALIZE_SCALAR(asid);
        UNSERIALIZE_SCALAR(vmid);
        UNSERIALIZE_SCALAR(isHyp);
        UNSERIALIZE_SCALAR(N);
        UNSERIALIZE_SCALAR(global);
        UNSERIALIZE_SCALAR(valid);
        UNSERIALIZE_SCALAR(ns);
        UNSERIALIZE_SCALAR(nstid);
        UNSERIALIZE_SCALAR(nonCacheable);
        UNSERIALIZE_ENUM(lookupLevel);
        UNSERIALIZE_ENUM(mtype);
        UNSERIALIZE_SCALAR(innerAttrs);
        UNSERIALIZE_SCALAR(outerAttrs);
        UNSERIALIZE_SCALAR(shareable);
        UNSERIALIZE_SCALAR(outerShareable);
        UNSERIALIZE_SCALAR(attributes);
        UNSERIALIZE_SCALAR(xn);
        UNSERIALIZE_SCALAR(pxn);
        UNSERIALIZE_SCALAR(ap);
        UNSERIALIZE_SCALAR(hap);
        uint8_t domain_;
        paramIn(cp, section, "domain", domain_);
        domain = static_cast<DomainType>(domain_);
    }

};

}
#endif // __ARCH_ARM_PAGETABLE_H__

<commit_msg>Add C bit to TLB entry.<commit_after>/*
 * Copyright (c) 2010, 2012-2013 ARM Limited
 * All rights reserved
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Copyright (c) 2002-2005 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Ali Saidi
 */

#ifndef __ARCH_ARM_PAGETABLE_H__
#define __ARCH_ARM_PAGETABLE_H__

#include <cstdint>

#include "arch/arm/isa_traits.hh"
#include "arch/arm/utility.hh"
#include "arch/arm/vtophys.hh"
#include "sim/serialize.hh"

namespace ArmISA {

struct VAddr
{
    VAddr(Addr a) { panic("not implemented yet."); }
};

// ITB/DTB page table entry
struct PTE
{
    void serialize(std::ostream &os)
    {
        panic("Need to implement PTE serialization\n");
    }

    void unserialize(Checkpoint *cp, const std::string &section)
    {
        panic("Need to implement PTE serialization\n");
    }

};

// Lookup level
enum LookupLevel {
    L0 = 0,  // AArch64 only
    L1,
    L2,
    L3,
    MAX_LOOKUP_LEVELS
};

// ITB/DTB table entry
struct TlbEntry
{
  public:
    enum class MemoryType : std::uint8_t {
        StronglyOrdered,
        Device,
        Normal
    };

    enum class DomainType : std::uint8_t {
        NoAccess = 0,
        Client,
        Reserved,
        Manager
    };

    // Matching variables
    Addr pfn;
    Addr size;              // Size of this entry, == Type of TLB Rec
    Addr vpn;               // Virtual Page Number
    uint64_t attributes;    // Memory attributes formatted for PAR

    LookupLevel lookupLevel;    // Lookup level where the descriptor was fetched
                                // from.  Used to set the FSR for faults
                                // occurring while the long desc. format is in
                                // use (AArch32 w/ LPAE and AArch64)

    uint16_t asid;          // Address Space Identifier
    uint8_t vmid;           // Virtual machine Identifier
    uint8_t N;              // Number of bits in pagesize
    uint8_t innerAttrs;
    uint8_t outerAttrs;
    uint8_t ap;             // Access permissions bits
    uint8_t hap;            // Hyp access permissions bits
    DomainType domain;         // Access Domain

    MemoryType mtype;

    // True if the long descriptor format is used for this entry (LPAE only)
    bool longDescFormat; // @todo use this in the update attribute bethod

    bool isHyp;
    bool global;
    bool valid;

    // True if the entry targets the non-secure physical address space
    bool ns;
    // True if the entry was brought in from a non-secure page table
    bool nstid;
    // Exception level on insert, AARCH64 EL0&1, AARCH32 -> el=1
    uint8_t el;

    // Type of memory
    bool nonCacheable;     // Can we wrap this in mtype?

    // Memory Attributes
    bool shareable;
    bool outerShareable;

    // Access permissions
    bool xn;                // Execute Never
    bool pxn;               // Privileged Execute Never (LPAE only)

    //Iso-X bit
    bool cbit;              // C Bit for Iso-X

    //Construct an entry that maps to physical address addr for SE mode
    TlbEntry(Addr _asn, Addr _vaddr, Addr _paddr,
             bool uncacheable, bool read_only) :
         pfn(_paddr >> PageShift), size(PageBytes - 1), vpn(_vaddr >> PageShift),
         attributes(0), lookupLevel(L1), asid(_asn), vmid(0), N(0),
         innerAttrs(0), outerAttrs(0), ap(read_only ? 0x3 : 0), hap(0x3),
         domain(DomainType::Client),  mtype(MemoryType::StronglyOrdered),
         longDescFormat(false), isHyp(false), global(false), valid(true),
         ns(true), nstid(true), el(0), nonCacheable(uncacheable),
         shareable(false), outerShareable(false), xn(0), pxn(0)
    {
        // no restrictions by default, hap = 0x3

        // @todo Check the memory type
        if (read_only)
            warn("ARM TlbEntry does not support read-only mappings\n");
    }

    TlbEntry() :
         pfn(0), size(0), vpn(0), attributes(0), lookupLevel(L1), asid(0),
         vmid(0), N(0), innerAttrs(0), outerAttrs(0), ap(0), hap(0x3),
         domain(DomainType::Client), mtype(MemoryType::StronglyOrdered),
         longDescFormat(false), isHyp(false), global(false), valid(true),
         ns(true), nstid(true), el(0), nonCacheable(false),
         shareable(false), outerShareable(false), xn(0), pxn(0)
    {
        // no restrictions by default, hap = 0x3

        // @todo Check the memory type
    }

    void
    updateVaddr(Addr new_vaddr)
    {
        vpn = new_vaddr >> PageShift;
    }

    Addr
    pageStart() const
    {
        return pfn << PageShift;
    }

    bool
    match(Addr va, uint8_t _vmid, bool hypLookUp, bool secure_lookup,
          uint8_t target_el) const
    {
        return match(va, 0, _vmid, hypLookUp, secure_lookup, true, target_el);
    }

    bool
    match(Addr va, uint16_t asn, uint8_t _vmid, bool hypLookUp,
          bool secure_lookup, bool ignore_asn, uint8_t target_el) const
    {
        bool match = false;
        Addr v = vpn << N;

        if (valid && va >= v && va <= v + size && (secure_lookup == !nstid) &&
            (hypLookUp == isHyp))
        {
            if (target_el == 2 || target_el == 3)
                match = (el == target_el);
            else
                match = (el == 0) || (el == 1);
            if (match && !ignore_asn) {
                match = global || (asn == asid);
            }
            if (match && nstid) {
                match = isHyp || (_vmid == vmid);
            }
        }
        return match;
    }

    Addr
    pAddr(Addr va) const
    {
        return (pfn << N) | (va & size);
    }

    void
    updateAttributes()
    {
        uint64_t mask;
        uint64_t newBits;

        // chec bit 11 to determine if its currently LPAE or VMSA format.
        if ( attributes & (1 << 11) ) {
            newBits = ((outerShareable ? 0x2 :
                      shareable         ? 0x3 : 0) << 7);
            mask = 0x180;
        } else {
            /** Formatting for Physical Address Register (PAR)
             *  Only including lower bits (TLB info here)
             *  PAR (32-bit format):
             *  PA   [31:12]
             *  LPAE [11] (Large Physical Address Extension)
             *  TLB info [10:1]
             *      NOS  [10] (Not Outer Sharable)
             *      NS   [9]  (Non-Secure)
             *      --   [8]  (Implementation Defined)
             *      SH   [7]  (Sharable)
             *      Inner[6:4](Inner memory attributes)
             *      Outer[3:2](Outer memory attributes)
             *      SS   [1]  (SuperSection)
             *      F    [0]  (Fault, Fault Status in [6:1] if faulted)
            */
            newBits = ((outerShareable ? 0:1) << 10) |
                      ((shareable ? 1:0) << 7) |
                      (innerAttrs << 4) |
                      (outerAttrs << 2);
                      // TODO: Supersection bit
            mask = 0x4FC;
        }
        // common bits
        newBits |= ns << 9;  // NS bit
        mask    |= 1  << 9;
        // add in the new bits
        attributes &= ~mask;
        attributes |= newBits;
    }

    void
    setAttributes(bool lpae)
    {
        attributes = lpae ? (1 << 11) : 0;
        updateAttributes();
    }

    std::string
    print() const
    {
        return csprintf("%#x, asn %d vmn %d hyp %d ppn %#x size: %#x ap:%d "
                        "ns:%d nstid:%d g:%d el:%d", vpn << N, asid, vmid,
                        isHyp, pfn << N, size, ap, ns, nstid, global, el);
    }

    void
    serialize(std::ostream &os)
    {
        SERIALIZE_SCALAR(longDescFormat);
        SERIALIZE_SCALAR(pfn);
        SERIALIZE_SCALAR(size);
        SERIALIZE_SCALAR(vpn);
        SERIALIZE_SCALAR(asid);
        SERIALIZE_SCALAR(vmid);
        SERIALIZE_SCALAR(isHyp);
        SERIALIZE_SCALAR(N);
        SERIALIZE_SCALAR(global);
        SERIALIZE_SCALAR(valid);
        SERIALIZE_SCALAR(ns);
        SERIALIZE_SCALAR(nstid);
        SERIALIZE_SCALAR(nonCacheable);
        SERIALIZE_ENUM(lookupLevel);
        SERIALIZE_ENUM(mtype);
        SERIALIZE_SCALAR(innerAttrs);
        SERIALIZE_SCALAR(outerAttrs);
        SERIALIZE_SCALAR(shareable);
        SERIALIZE_SCALAR(outerShareable);
        SERIALIZE_SCALAR(attributes);
        SERIALIZE_SCALAR(xn);
        SERIALIZE_SCALAR(pxn);
        SERIALIZE_SCALAR(ap);
        SERIALIZE_SCALAR(hap);
        uint8_t domain_ = static_cast<uint8_t>(domain);
        paramOut(os, "domain", domain_);
    }
    void
    unserialize(Checkpoint *cp, const std::string &section)
    {
        UNSERIALIZE_SCALAR(longDescFormat);
        UNSERIALIZE_SCALAR(pfn);
        UNSERIALIZE_SCALAR(size);
        UNSERIALIZE_SCALAR(vpn);
        UNSERIALIZE_SCALAR(asid);
        UNSERIALIZE_SCALAR(vmid);
        UNSERIALIZE_SCALAR(isHyp);
        UNSERIALIZE_SCALAR(N);
        UNSERIALIZE_SCALAR(global);
        UNSERIALIZE_SCALAR(valid);
        UNSERIALIZE_SCALAR(ns);
        UNSERIALIZE_SCALAR(nstid);
        UNSERIALIZE_SCALAR(nonCacheable);
        UNSERIALIZE_ENUM(lookupLevel);
        UNSERIALIZE_ENUM(mtype);
        UNSERIALIZE_SCALAR(innerAttrs);
        UNSERIALIZE_SCALAR(outerAttrs);
        UNSERIALIZE_SCALAR(shareable);
        UNSERIALIZE_SCALAR(outerShareable);
        UNSERIALIZE_SCALAR(attributes);
        UNSERIALIZE_SCALAR(xn);
        UNSERIALIZE_SCALAR(pxn);
        UNSERIALIZE_SCALAR(ap);
        UNSERIALIZE_SCALAR(hap);
        uint8_t domain_;
        paramIn(cp, section, "domain", domain_);
        domain = static_cast<DomainType>(domain_);
    }

};

}
#endif // __ARCH_ARM_PAGETABLE_H__

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2007 The Hewlett-Packard Development Company
 * All rights reserved.
 *
 * Redistribution and use of this software in source and binary forms,
 * with or without modification, are permitted provided that the
 * following conditions are met:
 *
 * The software must be used only for Non-Commercial Use which means any
 * use which is NOT directed to receiving any direct monetary
 * compensation for, or commercial advantage from such use.  Illustrative
 * examples of non-commercial use are academic research, personal study,
 * teaching, education and corporate research & development.
 * Illustrative examples of commercial use are distributing products for
 * commercial advantage and providing services using the software for
 * commercial advantage.
 *
 * If you wish to use this software or functionality therein that may be
 * covered by patents for commercial use, please contact:
 *     Director of Intellectual Property Licensing
 *     Office of Strategy and Technology
 *     Hewlett-Packard Company
 *     1501 Page Mill Road
 *     Palo Alto, California  94304
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.  Redistributions
 * in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or
 * other materials provided with the distribution.  Neither the name of
 * the COPYRIGHT HOLDER(s), HEWLETT-PACKARD COMPANY, nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.  No right of
 * sublicense is granted herewith.  Derivatives of the software and
 * output created using the software may be prepared, but only for
 * Non-Commercial Uses.  Derivatives of the software may be shared with
 * others provided: (i) the others agree to abide by the list of
 * conditions herein which includes the Non-Commercial Use restrictions;
 * and (ii) such Derivatives of the software include the above copyright
 * notice to acknowledge the contribution from this software where
 * applicable, this list of conditions and the disclaimer below.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Gabe Black
 */

#ifndef __ARCH_X86_FLOATREGS_HH__
#define __ARCH_X86_FLOATREGS_HH__

#include "arch/x86/x86_traits.hh"
#include "base/bitunion.hh"

namespace X86ISA
{
    enum FloatRegIndex
    {
        // MMX/X87 registers
        FLOATREG_MMX_BASE,
        FLOATREG_FPR_BASE = FLOATREG_MMX_BASE,
        FLOATREG_MMX0 = FLOATREG_MMX_BASE,
        FLOATREG_MMX1,
        FLOATREG_MMX2,
        FLOATREG_MMX3,
        FLOATREG_MMX4,
        FLOATREG_MMX5,
        FLOATREG_MMX6,
        FLOATREG_MMX7,

        FLOATREG_FPR0 = FLOATREG_FPR_BASE,
        FLOATREG_FPR1,
        FLOATREG_FPR2,
        FLOATREG_FPR3,
        FLOATREG_FPR4,
        FLOATREG_FPR5,
        FLOATREG_FPR6,
        FLOATREG_FPR7,

        FLOATREG_XMM_BASE = FLOATREG_MMX_BASE + NumMMXRegs,
        FLOATREG_XMM0_LOW = FLOATREG_XMM_BASE,
        FLOATREG_XMM0_HIGH,
        FLOATREG_XMM1_LOW,
        FLOATREG_XMM1_HIGH,
        FLOATREG_XMM2_LOW,
        FLOATREG_XMM2_HIGH,
        FLOATREG_XMM3_LOW,
        FLOATREG_XMM3_HIGH,
        FLOATREG_XMM4_LOW,
        FLOATREG_XMM4_HIGH,
        FLOATREG_XMM5_LOW,
        FLOATREG_XMM5_HIGH,
        FLOATREG_XMM6_LOW,
        FLOATREG_XMM6_HIGH,
        FLOATREG_XMM7_LOW,
        FLOATREG_XMM7_HIGH,
        FLOATREG_XMM8_LOW,
        FLOATREG_XMM8_HIGH,
        FLOATREG_XMM9_LOW,
        FLOATREG_XMM9_HIGH,
        FLOATREG_XMM10_LOW,
        FLOATREG_XMM10_HIGH,
        FLOATREG_XMM11_LOW,
        FLOATREG_XMM11_HIGH,
        FLOATREG_XMM12_LOW,
        FLOATREG_XMM12_HIGH,
        FLOATREG_XMM13_LOW,
        FLOATREG_XMM13_HIGH,
        FLOATREG_XMM14_LOW,
        FLOATREG_XMM14_HIGH,
        FLOATREG_XMM15_LOW,
        FLOATREG_XMM15_HIGH,

        FLOATREG_MICROFP_BASE = FLOATREG_XMM_BASE + 2 * NumXMMRegs,
        FLOATREG_MICROFP0 = FLOATREG_MICROFP_BASE,
        FLOATREG_MICROFP1,
        FLOATREG_MICROFP2,
        FLOATREG_MICROFP3,
        FLOATREG_MICROFP4,
        FLOATREG_MICROFP5,
        FLOATREG_MICROFP6,
        FLOATREG_MICROFP7,

        NUM_FLOATREGS = FLOATREG_MICROFP_BASE + NumMicroFpRegs
    };

    static inline FloatRegIndex
    FLOATREG_MMX(int index)
    {
        return (FloatRegIndex)(FLOATREG_MMX_BASE + index);
    }

    static inline FloatRegIndex
    FLOATREG_FPR(int index)
    {
        return (FloatRegIndex)(FLOATREG_FPR_BASE + index);
    }

    static inline FloatRegIndex
    FLOATREG_XMM_LOW(int index)
    {
        return (FloatRegIndex)(FLOATREG_XMM_BASE + 2 * index);
    }

    static inline FloatRegIndex
    FLOATREG_XMM_HIGH(int index)
    {
        return (FloatRegIndex)(FLOATREG_XMM_BASE + 2 * index + 1);
    }

    static inline FloatRegIndex
    FLOATREG_MICROFP(int index)
    {
        return (FloatRegIndex)(FLOATREG_MICROFP_BASE + index);
    }

    static inline FloatRegIndex
    FLOATREG_STACK(int index, int top)
    {
        return (FloatRegIndex)(NUM_FLOATREGS + ((top + index + 8) % 8));
    }
};

#endif // __ARCH_X86_FLOATREGS_HH__
<commit_msg>X86: Fix x87 stack register indexing.<commit_after>/*
 * Copyright (c) 2007 The Hewlett-Packard Development Company
 * All rights reserved.
 *
 * Redistribution and use of this software in source and binary forms,
 * with or without modification, are permitted provided that the
 * following conditions are met:
 *
 * The software must be used only for Non-Commercial Use which means any
 * use which is NOT directed to receiving any direct monetary
 * compensation for, or commercial advantage from such use.  Illustrative
 * examples of non-commercial use are academic research, personal study,
 * teaching, education and corporate research & development.
 * Illustrative examples of commercial use are distributing products for
 * commercial advantage and providing services using the software for
 * commercial advantage.
 *
 * If you wish to use this software or functionality therein that may be
 * covered by patents for commercial use, please contact:
 *     Director of Intellectual Property Licensing
 *     Office of Strategy and Technology
 *     Hewlett-Packard Company
 *     1501 Page Mill Road
 *     Palo Alto, California  94304
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.  Redistributions
 * in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or
 * other materials provided with the distribution.  Neither the name of
 * the COPYRIGHT HOLDER(s), HEWLETT-PACKARD COMPANY, nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.  No right of
 * sublicense is granted herewith.  Derivatives of the software and
 * output created using the software may be prepared, but only for
 * Non-Commercial Uses.  Derivatives of the software may be shared with
 * others provided: (i) the others agree to abide by the list of
 * conditions herein which includes the Non-Commercial Use restrictions;
 * and (ii) such Derivatives of the software include the above copyright
 * notice to acknowledge the contribution from this software where
 * applicable, this list of conditions and the disclaimer below.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Gabe Black
 */

#ifndef __ARCH_X86_FLOATREGS_HH__
#define __ARCH_X86_FLOATREGS_HH__

#include "arch/x86/x86_traits.hh"
#include "base/bitunion.hh"

namespace X86ISA
{
    enum FloatRegIndex
    {
        // MMX/X87 registers
        FLOATREG_MMX_BASE,
        FLOATREG_FPR_BASE = FLOATREG_MMX_BASE,
        FLOATREG_MMX0 = FLOATREG_MMX_BASE,
        FLOATREG_MMX1,
        FLOATREG_MMX2,
        FLOATREG_MMX3,
        FLOATREG_MMX4,
        FLOATREG_MMX5,
        FLOATREG_MMX6,
        FLOATREG_MMX7,

        FLOATREG_FPR0 = FLOATREG_FPR_BASE,
        FLOATREG_FPR1,
        FLOATREG_FPR2,
        FLOATREG_FPR3,
        FLOATREG_FPR4,
        FLOATREG_FPR5,
        FLOATREG_FPR6,
        FLOATREG_FPR7,

        FLOATREG_XMM_BASE = FLOATREG_MMX_BASE + NumMMXRegs,
        FLOATREG_XMM0_LOW = FLOATREG_XMM_BASE,
        FLOATREG_XMM0_HIGH,
        FLOATREG_XMM1_LOW,
        FLOATREG_XMM1_HIGH,
        FLOATREG_XMM2_LOW,
        FLOATREG_XMM2_HIGH,
        FLOATREG_XMM3_LOW,
        FLOATREG_XMM3_HIGH,
        FLOATREG_XMM4_LOW,
        FLOATREG_XMM4_HIGH,
        FLOATREG_XMM5_LOW,
        FLOATREG_XMM5_HIGH,
        FLOATREG_XMM6_LOW,
        FLOATREG_XMM6_HIGH,
        FLOATREG_XMM7_LOW,
        FLOATREG_XMM7_HIGH,
        FLOATREG_XMM8_LOW,
        FLOATREG_XMM8_HIGH,
        FLOATREG_XMM9_LOW,
        FLOATREG_XMM9_HIGH,
        FLOATREG_XMM10_LOW,
        FLOATREG_XMM10_HIGH,
        FLOATREG_XMM11_LOW,
        FLOATREG_XMM11_HIGH,
        FLOATREG_XMM12_LOW,
        FLOATREG_XMM12_HIGH,
        FLOATREG_XMM13_LOW,
        FLOATREG_XMM13_HIGH,
        FLOATREG_XMM14_LOW,
        FLOATREG_XMM14_HIGH,
        FLOATREG_XMM15_LOW,
        FLOATREG_XMM15_HIGH,

        FLOATREG_MICROFP_BASE = FLOATREG_XMM_BASE + 2 * NumXMMRegs,
        FLOATREG_MICROFP0 = FLOATREG_MICROFP_BASE,
        FLOATREG_MICROFP1,
        FLOATREG_MICROFP2,
        FLOATREG_MICROFP3,
        FLOATREG_MICROFP4,
        FLOATREG_MICROFP5,
        FLOATREG_MICROFP6,
        FLOATREG_MICROFP7,

        NUM_FLOATREGS = FLOATREG_MICROFP_BASE + NumMicroFpRegs
    };

    static inline FloatRegIndex
    FLOATREG_MMX(int index)
    {
        return (FloatRegIndex)(FLOATREG_MMX_BASE + index);
    }

    static inline FloatRegIndex
    FLOATREG_FPR(int index)
    {
        return (FloatRegIndex)(FLOATREG_FPR_BASE + index);
    }

    static inline FloatRegIndex
    FLOATREG_XMM_LOW(int index)
    {
        return (FloatRegIndex)(FLOATREG_XMM_BASE + 2 * index);
    }

    static inline FloatRegIndex
    FLOATREG_XMM_HIGH(int index)
    {
        return (FloatRegIndex)(FLOATREG_XMM_BASE + 2 * index + 1);
    }

    static inline FloatRegIndex
    FLOATREG_MICROFP(int index)
    {
        return (FloatRegIndex)(FLOATREG_MICROFP_BASE + index);
    }

    static inline FloatRegIndex
    FLOATREG_STACK(int index, int top)
    {
        return FLOATREG_FPR((top + index + 8) % 8);
    }
};

#endif // __ARCH_X86_FLOATREGS_HH__
<|endoftext|>


<commit_before>// Copyright (c) 2011-2014 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "utilitydialog.h"

#include "ui_aboutdialog.h"
#include "ui_helpmessagedialog.h"

#include "bitcoingui.h"
#include "clientmodel.h"
#include "guiutil.h"

#include "clientversion.h"
#include "init.h"
#include "util.h"

#include <QLabel>
#include <QVBoxLayout>

/** "About" dialog box */
AboutDialog::AboutDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::AboutDialog)
{
    ui->setupUi(this);

    // Set current copyright year
    ui->copyrightLabel->setText(tr("Copyright") + QString(" © 2009-%1 ").arg(COPYRIGHT_YEAR) + tr("The Bitcoin Core developers"));
}

void AboutDialog::setModel(ClientModel *model)
{
    if(model)
    {
        QString version = model->formatFullVersion();
        /* On x86 add a bit specifier to the version so that users can distinguish between
         * 32 and 64 bit builds. On other architectures, 32/64 bit may be more ambigious.
         */
#if defined(__x86_64__)
        version += tr(" (%1-bit)").arg(64);
#elif defined(__i386__ )
        version += tr(" (%1-bit)").arg(32);
#endif
        ui->versionLabel->setText(version);
    }
}

AboutDialog::~AboutDialog()
{
    delete ui;
}

void AboutDialog::on_buttonBox_accepted()
{
    close();
}

/** "Help message" dialog box */
HelpMessageDialog::HelpMessageDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::HelpMessageDialog)
{
    ui->setupUi(this);
    GUIUtil::restoreWindowGeometry("nHelpMessageDialogWindow", this->size(), this);

    header = tr("Dogecoin Core") + " " + tr("version") + " " +
        QString::fromStdString(FormatFullVersion()) + "\n\n" +
        tr("Usage:") + "\n" +
        "  bitcoin-qt [" + tr("command-line options") + "]                     " + "\n";

    coreOptions = QString::fromStdString(HelpMessage(HMM_BITCOIN_QT));

    uiOptions = tr("UI options") + ":\n" +
        "  -lang=<lang>           " + tr("Set language, for example \"de_DE\" (default: system locale)") + "\n" +
        "  -min                   " + tr("Start minimized") + "\n" +
        "  -splash                " + tr("Show splash screen on startup (default: 1)") + "\n" +
        "  -choosedatadir         " + tr("Choose data directory on startup (default: 0)");

    ui->helpMessageLabel->setFont(GUIUtil::bitcoinAddressFont());

    // Set help message text
    ui->helpMessageLabel->setText(header + "\n" + coreOptions + "\n" + uiOptions);
}

HelpMessageDialog::~HelpMessageDialog()
{
    GUIUtil::saveWindowGeometry("nHelpMessageDialogWindow", this);
    delete ui;
}

void HelpMessageDialog::printToConsole()
{
    // On other operating systems, the expected action is to print the message to the console.
    QString strUsage = header + "\n" + coreOptions + "\n" + uiOptions + "\n";
    fprintf(stdout, "%s", strUsage.toStdString().c_str());
}

void HelpMessageDialog::showOrPrint()
{
#if defined(WIN32)
        // On Windows, show a message box, as there is no stderr/stdout in windowed applications
        exec();
#else
        // On other operating systems, print help text to console
        printToConsole();
#endif
}

void HelpMessageDialog::on_okButton_accepted()
{
    close();
}

/** "Shutdown" window */
void ShutdownWindow::showShutdownWindow(BitcoinGUI *window)
{
    if (!window)
        return;

    // Show a simple window indicating shutdown status
    QWidget *shutdownWindow = new QWidget();
    QVBoxLayout *layout = new QVBoxLayout();
    layout->addWidget(new QLabel(
        tr("Dogecoin Core is shutting down...") + "

" +
        tr("Do not shut down the computer until this window disappears.")));
    shutdownWindow->setLayout(layout);

    // Center shutdown window at where main window was
    const QPoint global = window->mapToGlobal(window->rect().center());
    shutdownWindow->move(global.x() - shutdownWindow->width() / 2, global.y() - shutdownWindow->height() / 2);
    shutdownWindow->show();
}
<commit_msg>Actually update AboutDialog<commit_after>// Copyright (c) 2011-2014 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "utilitydialog.h"

#include "ui_aboutdialog.h"
#include "ui_helpmessagedialog.h"

#include "bitcoingui.h"
#include "clientmodel.h"
#include "guiutil.h"

#include "clientversion.h"
#include "init.h"
#include "util.h"

#include <QLabel>
#include <QVBoxLayout>

/** "About" dialog box */
AboutDialog::AboutDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::AboutDialog)
{
    ui->setupUi(this);

    // Set current copyright year
    ui->copyrightLabel->setText(tr("Copyright") + QString(" © 2009-%1 ").arg(COPYRIGHT_YEAR) + tr("The Bitcoin Core developers") + QString("
") + tr("Copyright") + QString(" © 2013-%1 ").arg(COPYRIGHT_YEAR) + tr("The Dogecoin developers"));
}

void AboutDialog::setModel(ClientModel *model)
{
    if(model)
    {
        QString version = model->formatFullVersion();
        /* On x86 add a bit specifier to the version so that users can distinguish between
         * 32 and 64 bit builds. On other architectures, 32/64 bit may be more ambigious.
         */
#if defined(__x86_64__)
        version += tr(" (%1-bit)").arg(64);
#elif defined(__i386__ )
        version += tr(" (%1-bit)").arg(32);
#endif
        ui->versionLabel->setText(version);
    }
}

AboutDialog::~AboutDialog()
{
    delete ui;
}

void AboutDialog::on_buttonBox_accepted()
{
    close();
}

/** "Help message" dialog box */
HelpMessageDialog::HelpMessageDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::HelpMessageDialog)
{
    ui->setupUi(this);
    GUIUtil::restoreWindowGeometry("nHelpMessageDialogWindow", this->size(), this);

    header = tr("Dogecoin Core") + " " + tr("version") + " " +
        QString::fromStdString(FormatFullVersion()) + "\n\n" +
        tr("Usage:") + "\n" +
        "  bitcoin-qt [" + tr("command-line options") + "]                     " + "\n";

    coreOptions = QString::fromStdString(HelpMessage(HMM_BITCOIN_QT));

    uiOptions = tr("UI options") + ":\n" +
        "  -lang=<lang>           " + tr("Set language, for example \"de_DE\" (default: system locale)") + "\n" +
        "  -min                   " + tr("Start minimized") + "\n" +
        "  -splash                " + tr("Show splash screen on startup (default: 1)") + "\n" +
        "  -choosedatadir         " + tr("Choose data directory on startup (default: 0)");

    ui->helpMessageLabel->setFont(GUIUtil::bitcoinAddressFont());

    // Set help message text
    ui->helpMessageLabel->setText(header + "\n" + coreOptions + "\n" + uiOptions);
}

HelpMessageDialog::~HelpMessageDialog()
{
    GUIUtil::saveWindowGeometry("nHelpMessageDialogWindow", this);
    delete ui;
}

void HelpMessageDialog::printToConsole()
{
    // On other operating systems, the expected action is to print the message to the console.
    QString strUsage = header + "\n" + coreOptions + "\n" + uiOptions + "\n";
    fprintf(stdout, "%s", strUsage.toStdString().c_str());
}

void HelpMessageDialog::showOrPrint()
{
#if defined(WIN32)
        // On Windows, show a message box, as there is no stderr/stdout in windowed applications
        exec();
#else
        // On other operating systems, print help text to console
        printToConsole();
#endif
}

void HelpMessageDialog::on_okButton_accepted()
{
    close();
}

/** "Shutdown" window */
void ShutdownWindow::showShutdownWindow(BitcoinGUI *window)
{
    if (!window)
        return;

    // Show a simple window indicating shutdown status
    QWidget *shutdownWindow = new QWidget();
    QVBoxLayout *layout = new QVBoxLayout();
    layout->addWidget(new QLabel(
        tr("Dogecoin Core is shutting down...") + "

" +
        tr("Do not shut down the computer until this window disappears.")));
    shutdownWindow->setLayout(layout);

    // Center shutdown window at where main window was
    const QPoint global = window->mapToGlobal(window->rect().center());
    shutdownWindow->move(global.x() - shutdownWindow->width() / 2, global.y() - shutdownWindow->height() / 2);
    shutdownWindow->show();
}
<|endoftext|>


<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2011 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

//$Id:  $

#include <mapnik/raster_colorizer.hpp>
#include <limits>

namespace mapnik
{

//! \brief Strings for the colorizer_mode enumeration
static const char *colorizer_mode_strings[] = {
    "inherit",
    "linear",
    "discrete",
    "exact",
    ""
};

IMPLEMENT_ENUM( colorizer_mode, colorizer_mode_strings )

colorizer_stop::colorizer_stop(float value, colorizer_mode mode,
                               color const& _color,
                               std::string const& label)
: value_(value)
    , mode_(mode)
    , color_(_color)
    , label_(label)
{

}

colorizer_stop::colorizer_stop(colorizer_stop const& stop)
    : value_(stop.value_)
    , mode_(stop.mode_)
    , color_(stop.color_)
    , label_(stop.label_)
{
}

colorizer_stop::~colorizer_stop()
{

}

bool colorizer_stop::operator==(colorizer_stop const& other) const
{
    return  (value_ == other.value_) &&
        (color_ == other.color_) &&
        (mode_ == other.mode_) &&
        (label_ == other.label_);
}

std::string colorizer_stop::to_string() const
{
    std::stringstream ss;
    ss << color_.to_string() << " " << value_ << " " << mode_.as_string();
    return ss.str();
}

raster_colorizer::raster_colorizer(colorizer_mode mode, color const& _color)
    : default_mode_(mode)
    , default_color_(_color)
    , epsilon_(std::numeric_limits<float>::epsilon())
{

}

raster_colorizer::~raster_colorizer()
{
}

bool raster_colorizer::add_stop(colorizer_stop const& stop)
{
    //make sure stops are added in order of value
    if(stops_.size())
    {
        if(stop.get_value() <= stops_.back().get_value())
        {
            return false;
        }
    }

    stops_.push_back(stop);

    return true;
}

void raster_colorizer::colorize(raster_ptr const& raster, std::map<std::string,value> const& Props) const
{
    unsigned *imageData = raster->data_.getData();

    int len = raster->data_.width() * raster->data_.height();

    bool hasNoData = false;
    float noDataValue = 0;

    std::map<std::string,value>::const_iterator fi = Props.find("NODATA");
    if (fi != Props.end())
    {
        hasNoData = true;
        noDataValue = static_cast<float>(fi->second.to_double());
    }

    for (int i=0; i<len; ++i)
    {
        // the GDAL plugin reads single bands as floats
        float value = *reinterpret_cast<float *> (&imageData[i]);
        if (hasNoData && noDataValue == value)
            imageData[i] = color(0,0,0,0).rgba();
        else
            imageData[i] = get_color(value).rgba();
    }
}

inline unsigned interpolate(unsigned start, unsigned end, float fraction)
{
    return static_cast<unsigned>(fraction * ((float)end - (float)start) + start);
}

color raster_colorizer::get_color(float value) const
{
    int stopCount = stops_.size();

    //use default color if no stops
    if(stopCount == 0)
    {
        return default_color_;
    }

    //1 - Find the stop that the value is in
    int stopIdx = -1;
    bool foundStopIdx = false;

    for(int i=0; i<stopCount; ++i)
    {
        if(value < stops_[i].get_value())
        {
            stopIdx = i-1;
            foundStopIdx = true;
            break;
        }
    }

    if(!foundStopIdx)
    {
        stopIdx = stopCount-1;
    }

    //2 - Find the next stop
    int nextStopIdx = stopIdx + 1;
    if(nextStopIdx >= stopCount)
    {
        //there is no next stop
        nextStopIdx = stopCount - 1;
    }

    //3 - Work out the mode
    colorizer_mode stopMode;
    if( stopIdx == -1 )
    {
        //before the first stop
        stopMode = default_mode_;
    }
    else
    {
        stopMode = stops_[stopIdx].get_mode();
        if(stopMode == COLORIZER_INHERIT)
        {
            stopMode = default_mode_;
        }
    }

    //4 - Calculate the colour
    color stopColor;
    color nextStopColor;
    float stopValue = 0;
    float nextStopValue = 0;
    color outputColor = get_default_color();
    if(stopIdx == -1)
    {
        stopColor = default_color_;
        nextStopColor = stops_[nextStopIdx].get_color();
        stopValue = value;
        nextStopValue = stops_[nextStopIdx].get_value();
    }
    else
    {
        stopColor = stops_[stopIdx].get_color();
        nextStopColor = stops_[nextStopIdx].get_color();
        stopValue = stops_[stopIdx].get_value();
        nextStopValue = stops_[nextStopIdx].get_value();
    }

    switch(stopMode)
    {
    case COLORIZER_LINEAR:
    {
        //deal with this separately so we don't have to worry about div0
        if(nextStopValue == stopValue)
        {
            outputColor = stopColor;
        }
        else
        {
            float fraction = (value - stopValue) / (nextStopValue - stopValue);

            unsigned r = interpolate(stopColor.red(), nextStopColor.red(),fraction);
            unsigned g = interpolate(stopColor.green(), nextStopColor.green(),fraction);
            unsigned b = interpolate(stopColor.blue(), nextStopColor.blue(),fraction);
            unsigned a = interpolate(stopColor.alpha(), nextStopColor.alpha(),fraction);

            outputColor.set_red(r);
            outputColor.set_green(g);
            outputColor.set_blue(b);
            outputColor.set_alpha(a);
        }

    }
    break;
    case COLORIZER_DISCRETE:
        outputColor = stopColor;
        break;
    case COLORIZER_EXACT:
    default:
        //approximately equal (within epsilon)
        if(fabs(value - stopValue) < epsilon_)
        {
            outputColor = stopColor;
        }
        else
        {
            outputColor = default_color_;
        }
        break;
    }

    /*
      std::clog << "get_color: " << value << "\n";
      std::clog << "\tstopIdx: " << stopIdx << "\n";
      std::clog << "\tnextStopIdx: " << nextStopIdx << "\n";
      std::clog << "\tstopValue: " << stopValue << "\n";
      std::clog << "\tnextStopValue: " << nextStopValue << "\n";
      std::clog << "\tstopColor: " << stopColor.to_string() << "\n";
      std::clog << "\tnextStopColor: " << nextStopColor.to_string() << "\n";
      std::clog << "\tstopMode: " << stopMode.as_string() << "\n";
      std::clog << "\toutputColor: " << outputColor.to_string() << "\n";
    */

    return outputColor;
}

}

<commit_msg>update to work with new feature_impl<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2011 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

//$Id:  $

#include <mapnik/raster_colorizer.hpp>
#include <limits>

namespace mapnik
{

//! \brief Strings for the colorizer_mode enumeration
static const char *colorizer_mode_strings[] = {
    "inherit",
    "linear",
    "discrete",
    "exact",
    ""
};

IMPLEMENT_ENUM( colorizer_mode, colorizer_mode_strings )

colorizer_stop::colorizer_stop(float value, colorizer_mode mode,
                               color const& _color,
                               std::string const& label)
: value_(value)
    , mode_(mode)
    , color_(_color)
    , label_(label)
{

}

colorizer_stop::colorizer_stop(colorizer_stop const& stop)
    : value_(stop.value_)
    , mode_(stop.mode_)
    , color_(stop.color_)
    , label_(stop.label_)
{
}

colorizer_stop::~colorizer_stop()
{

}

bool colorizer_stop::operator==(colorizer_stop const& other) const
{
    return  (value_ == other.value_) &&
        (color_ == other.color_) &&
        (mode_ == other.mode_) &&
        (label_ == other.label_);
}

std::string colorizer_stop::to_string() const
{
    std::stringstream ss;
    ss << color_.to_string() << " " << value_ << " " << mode_.as_string();
    return ss.str();
}

raster_colorizer::raster_colorizer(colorizer_mode mode, color const& _color)
    : default_mode_(mode)
    , default_color_(_color)
    , epsilon_(std::numeric_limits<float>::epsilon())
{

}

raster_colorizer::~raster_colorizer()
{
}

bool raster_colorizer::add_stop(colorizer_stop const& stop)
{
    //make sure stops are added in order of value
    if(stops_.size())
    {
        if(stop.get_value() <= stops_.back().get_value())
        {
            return false;
        }
    }

    stops_.push_back(stop);

    return true;
}

void raster_colorizer::colorize(raster_ptr const& raster, Feature const& f) const
{
    unsigned *imageData = raster->data_.getData();

    int len = raster->data_.width() * raster->data_.height();

    bool hasNoData = false;
    float noDataValue = 0;

    //std::map<std::string,value>::const_iterator fi = Props.find("NODATA");
    if (f.has_key("NODATA"))
    {
        hasNoData = true;
        noDataValue = static_cast<float>(f.get("NODATA").to_double());
    }

    for (int i=0; i<len; ++i)
    {
        // the GDAL plugin reads single bands as floats
        float value = *reinterpret_cast<float *> (&imageData[i]);
        if (hasNoData && noDataValue == value)
            imageData[i] = color(0,0,0,0).rgba();
        else
            imageData[i] = get_color(value).rgba();
    }
}

inline unsigned interpolate(unsigned start, unsigned end, float fraction)
{
    return static_cast<unsigned>(fraction * ((float)end - (float)start) + start);
}

color raster_colorizer::get_color(float value) const
{
    int stopCount = stops_.size();

    //use default color if no stops
    if(stopCount == 0)
    {
        return default_color_;
    }

    //1 - Find the stop that the value is in
    int stopIdx = -1;
    bool foundStopIdx = false;

    for(int i=0; i<stopCount; ++i)
    {
        if(value < stops_[i].get_value())
        {
            stopIdx = i-1;
            foundStopIdx = true;
            break;
        }
    }

    if(!foundStopIdx)
    {
        stopIdx = stopCount-1;
    }

    //2 - Find the next stop
    int nextStopIdx = stopIdx + 1;
    if(nextStopIdx >= stopCount)
    {
        //there is no next stop
        nextStopIdx = stopCount - 1;
    }

    //3 - Work out the mode
    colorizer_mode stopMode;
    if( stopIdx == -1 )
    {
        //before the first stop
        stopMode = default_mode_;
    }
    else
    {
        stopMode = stops_[stopIdx].get_mode();
        if(stopMode == COLORIZER_INHERIT)
        {
            stopMode = default_mode_;
        }
    }

    //4 - Calculate the colour
    color stopColor;
    color nextStopColor;
    float stopValue = 0;
    float nextStopValue = 0;
    color outputColor = get_default_color();
    if(stopIdx == -1)
    {
        stopColor = default_color_;
        nextStopColor = stops_[nextStopIdx].get_color();
        stopValue = value;
        nextStopValue = stops_[nextStopIdx].get_value();
    }
    else
    {
        stopColor = stops_[stopIdx].get_color();
        nextStopColor = stops_[nextStopIdx].get_color();
        stopValue = stops_[stopIdx].get_value();
        nextStopValue = stops_[nextStopIdx].get_value();
    }

    switch(stopMode)
    {
    case COLORIZER_LINEAR:
    {
        //deal with this separately so we don't have to worry about div0
        if(nextStopValue == stopValue)
        {
            outputColor = stopColor;
        }
        else
        {
            float fraction = (value - stopValue) / (nextStopValue - stopValue);

            unsigned r = interpolate(stopColor.red(), nextStopColor.red(),fraction);
            unsigned g = interpolate(stopColor.green(), nextStopColor.green(),fraction);
            unsigned b = interpolate(stopColor.blue(), nextStopColor.blue(),fraction);
            unsigned a = interpolate(stopColor.alpha(), nextStopColor.alpha(),fraction);

            outputColor.set_red(r);
            outputColor.set_green(g);
            outputColor.set_blue(b);
            outputColor.set_alpha(a);
        }

    }
    break;
    case COLORIZER_DISCRETE:
        outputColor = stopColor;
        break;
    case COLORIZER_EXACT:
    default:
        //approximately equal (within epsilon)
        if(fabs(value - stopValue) < epsilon_)
        {
            outputColor = stopColor;
        }
        else
        {
            outputColor = default_color_;
        }
        break;
    }

    /*
      std::clog << "get_color: " << value << "\n";
      std::clog << "\tstopIdx: " << stopIdx << "\n";
      std::clog << "\tnextStopIdx: " << nextStopIdx << "\n";
      std::clog << "\tstopValue: " << stopValue << "\n";
      std::clog << "\tnextStopValue: " << nextStopValue << "\n";
      std::clog << "\tstopColor: " << stopColor.to_string() << "\n";
      std::clog << "\tnextStopColor: " << nextStopColor.to_string() << "\n";
      std::clog << "\tstopMode: " << stopMode.as_string() << "\n";
      std::clog << "\toutputColor: " << outputColor.to_string() << "\n";
    */

    return outputColor;
}

}

<|endoftext|>


<commit_before>// Copyright (c) 2013, Matthew Harvey. All rights reserved.

#include "transaction_ctrl.hpp"
#include "account.hpp"
#include "account_ctrl.hpp"
#include "account_reader.hpp"
#include "account_type.hpp"
#include "b_string.hpp"
#include "date.hpp"
#include "date_ctrl.hpp"
#include "decimal_text_ctrl.hpp"
#include "decimal_validator.hpp"
#include "draft_journal.hpp"
#include "entry.hpp"
#include "entry_ctrl.hpp"
#include "finformat.hpp"
#include "frame.hpp"
#include "frequency_ctrl.hpp"
#include "ordinary_journal.hpp"
#include "proto_journal.hpp"
#include "locale.hpp"
#include "phatbooks_database_connection.hpp"
#include "repeater.hpp"
#include "top_panel.hpp"
#include "transaction_type_ctrl.hpp"
#include "transaction_type.hpp"
#include <boost/optional.hpp>
#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/scoped_ptr.hpp>
#include <jewel/debug_log.hpp>
#include <jewel/decimal.hpp>
#include <jewel/on_windows.hpp>
#include <jewel/optional.hpp>
#include <wx/arrstr.h>
#include <wx/button.h>
#include <wx/combobox.h>
#include <wx/panel.h>
#include <wx/event.h>
#include <wx/msgdlg.h>
#include <wx/gbsizer.h>
#include <wx/stattext.h>
#include <wx/string.h>
#include <wx/textctrl.h>
#include <algorithm>
#include <cassert>
#include <iostream>
#include <vector>

using boost::optional;
using boost::scoped_ptr;
using jewel::Decimal;
using jewel::value;
using std::endl;
using std::vector;

namespace gregorian = boost::gregorian;

namespace phatbooks
{
namespace gui
{

BEGIN_EVENT_TABLE(TransactionCtrl, wxPanel)
	EVT_BUTTON
	(	wxID_OK,
		TransactionCtrl::on_ok_button_click
	)
	EVT_BUTTON
	(	wxID_CANCEL,
		TransactionCtrl::on_cancel_button_click
	)
END_EVENT_TABLE()

// WARNING There are bugs in wxWidgets' wxDatePickerCtrl under wxGTK.
// Firstly, tab traversal gets stuck on that control.
// Secondly, if we type a different date and then press "Enter" for OK,
// the date that actually gets picked up as the transaction date always
// seems to be TODAY's date, not the date actually entered. This appears to
// be an unresolved bug in wxWidgets.
// Note adding wxTAB_TRAVERSAL to style does not seem to fix the problem.
// We have used a simple custom class, DateCtrl here instead, to avoid
// these problems. Might later add a button to pop up a wxCalendarCtrl
// if the user wants one.

TransactionCtrl::TransactionCtrl
(	TopPanel* p_parent,
	vector<Account> const& p_balance_sheet_accounts,
	vector<Account> const& p_pl_accounts,
	PhatbooksDatabaseConnection& p_database_connection
):
	wxPanel
	(	p_parent,
		wxID_ANY,
		wxDefaultPosition,
		wxDefaultSize
	),
	m_top_sizer(0),
	m_transaction_type_ctrl(0),
	m_source_entry_ctrl(0),
	m_destination_entry_ctrl(0),
	m_primary_amount_ctrl(0),
	m_frequency_ctrl(0),
	m_date_ctrl(0),
	m_cancel_button(0),
	m_ok_button(0),
	m_database_connection(p_database_connection)
{
	assert (!p_balance_sheet_accounts.empty() || !p_pl_accounts.empty());
	assert (p_balance_sheet_accounts.size() + p_pl_accounts.size() >= 2);

	// Figure out the natural TransactionType given the Accounts we have
	// been passed. We will use this to initialize the TransactionTypeCtrl.
	Account account_x(p_database_connection);
	Account account_y(p_database_connection);
	if (p_balance_sheet_accounts.empty())
	{
		assert (p_pl_accounts.size() >= 2);
		account_x = p_pl_accounts[0];
		account_y = p_pl_accounts[1];
	}
	else if (p_pl_accounts.empty())
	{
		assert (p_balance_sheet_accounts.size() >= 2);
		account_x = p_balance_sheet_accounts[0];
		account_y = p_balance_sheet_accounts[1];
	}
	else
	{
		assert (!p_balance_sheet_accounts.empty());
		assert (!p_pl_accounts.empty());
		account_x = p_balance_sheet_accounts[0];
		account_y = p_pl_accounts[0];
	}
	if (account_y.account_type() == account_type::revenue)
	{
		using std::swap;
		swap(account_x, account_y);
	}
	assert (account_x.has_id());
	assert (account_y.has_id());
	transaction_type::TransactionType const initial_transaction_type =
		natural_transaction_type(account_x, account_y);

	size_t row = 0;

	// Top sizer
	m_top_sizer = new wxGridBagSizer();
	SetSizer(m_top_sizer);

	m_transaction_type_ctrl = new TransactionTypeCtrl
	(	this,
		s_transaction_type_ctrl_id,
		wxSize(160, wxDefaultSize.y),
		m_database_connection
	);
	m_transaction_type_ctrl->set_transaction_type(initial_transaction_type);
	wxSize const text_box_size = m_transaction_type_ctrl->GetSize();
	m_top_sizer->Add(m_transaction_type_ctrl, wxGBPosition(row, 0));

	m_primary_amount_ctrl = new DecimalTextCtrl
	(	this,
		s_primary_amount_ctrl_id,
		text_box_size,
		m_database_connection.default_commodity().precision(),
		false
	);
	m_top_sizer->Add
	(	m_primary_amount_ctrl,
		wxGBPosition(row, 3),
		wxDefaultSpan,
		wxALIGN_RIGHT
	);

	// We need the names of available Accounts, for the given
	// TransactionType, from which the user will choose
	// Accounts, for each side of the transaction.

	assert_transaction_type_validity(initial_transaction_type);

	// Rows for entering Entry details
	typedef vector<Account>::size_type Size;
	vector<Account> accounts;
	accounts.push_back(account_x);
	accounts.push_back(account_y);

	row += 2;

	// WARNING Temp hack
	assert (accounts.size() >= 2);

	vector<Account> source_accounts;
	source_accounts.push_back(accounts[0]);
	vector<Account> destination_accounts;
	destination_accounts.push_back(accounts[1]);

	Decimal::places_type const precision =
		m_database_connection.default_commodity().precision();
	m_source_entry_ctrl = new EntryCtrl
	(	this,
		source_accounts,
		m_database_connection,
		initial_transaction_type,
		text_box_size,
		true,
		Decimal(0, precision)
	);
	m_destination_entry_ctrl = new EntryCtrl
	(	this,
		destination_accounts,
		m_database_connection,
		initial_transaction_type,
		text_box_size,
		false,
		Decimal(0, precision)
	);
	m_top_sizer->Add
	(	m_source_entry_ctrl,
		wxGBPosition(row, 0),
		wxGBSpan(1, 4),
		wxEXPAND
	);

	row += 2;

	m_top_sizer->Add
	(	m_destination_entry_ctrl,
		wxGBPosition(row, 0),
		wxGBSpan(1, 4),
		wxEXPAND
	);

	row += 2;

	// Date and Frequency controls
	m_frequency_ctrl = new FrequencyCtrl
	(	this,
		wxID_ANY,
		wxSize(text_box_size.x * 3, text_box_size.y)
	);
	m_top_sizer->Add(m_frequency_ctrl, wxGBPosition(row, 0), wxGBSpan(1, 3));
	m_date_ctrl = new DateCtrl
	(	this,
		wxID_ANY,
		wxSize(text_box_size.x, text_box_size.y)
	);
	m_top_sizer->Add(m_date_ctrl, wxGBPosition(row, 3));

	row += 2;

	// Cancel/Clear button
	m_cancel_button = new wxButton
	(	this,
		wxID_CANCEL,
		wxString("&Clear"),
		wxDefaultPosition,
		wxSize(text_box_size.x, text_box_size.y)
	);
	m_top_sizer->Add(m_cancel_button, wxGBPosition(row, 0));

	// Save/OK button
	m_ok_button = new wxButton
	(	this,
		wxID_OK,
		wxString("&Save"),
		wxDefaultPosition,
		wxSize(text_box_size.x, text_box_size.y)
	);

	m_top_sizer->Add(m_ok_button, wxGBPosition(row, 3));
	m_ok_button->SetDefault();  // Enter key will now trigger "OK" button

	// "Admin"
	// SetSizer(m_top_sizer);
	m_top_sizer->Fit(this);
	m_top_sizer->SetSizeHints(this);
	Fit();
	Layout();
}

void
TransactionCtrl::refresh_for_transaction_type
(	transaction_type::TransactionType p_transaction_type
)
{
	m_source_entry_ctrl->
		refresh_for_transaction_type(p_transaction_type);
	m_destination_entry_ctrl->
		refresh_for_transaction_type(p_transaction_type);
	return;
}

void
TransactionCtrl::notify_decimal_ctrl_focus_kill()
{
	reset_entry_ctrl_amounts();
	return;
}

Decimal
TransactionCtrl::primary_amount() const
{
	return m_primary_amount_ctrl->amount();
}

void
TransactionCtrl::on_ok_button_click(wxCommandEvent& event)
{
	(void)event;  // Silence compiler re. unused parameter.
	Decimal const primary_amt = primary_amount();
	m_source_entry_ctrl->set_primary_amount(primary_amt);
	m_destination_entry_ctrl->set_primary_amount(primary_amt);
	if (Validate() && TransferDataFromWindow())
	{
		if (is_balanced())
		{
			post_journal();
			TopPanel* const panel = dynamic_cast<TopPanel*>(GetParent());
			assert (panel);
			panel->update();
		}
		else
		{
			wxMessageBox("Transaction does not balance.");
		}
	}
	return;
}

void
TransactionCtrl::on_cancel_button_click(wxCommandEvent& event)
{
	(void)event;  // Silence compiler re. unused parameter.
	TopPanel* const panel = dynamic_cast<TopPanel*>(GetParent());
	assert (panel);
	panel->update();
}

void
TransactionCtrl::reset_entry_ctrl_amounts()
{
	Decimal const pa = primary_amount();
	m_source_entry_ctrl->set_primary_amount(pa);
	m_destination_entry_ctrl->set_primary_amount(pa);
	return;
}

void
TransactionCtrl::post_journal()
{
	// TODO HIGH PRIORITY Fix this
	// to work now that we have EntryCtrl instead of storing
	// Entry info directly in the TransactionCtrl.
	ProtoJournal journal;
	// TODO What if the dereferencing of optional fails?
	transaction_type::TransactionType const ttype =
		value(m_transaction_type_ctrl->transaction_type());
	journal.set_whether_actual(transaction_type_is_actual(ttype));

	size_t const num_entry_controls = 2;
	EntryCtrl const* const entry_controls[num_entry_controls] =
	{	m_source_entry_ctrl,
		m_destination_entry_ctrl
	};
	for (size_t i = 0; i != num_entry_controls; ++i)
	{
		vector<Entry> entries = entry_controls[i]->make_entries();
		for (vector<Entry>::size_type j = 0; j != entries.size(); ++j)
		{
			Entry entry = entries[j];
			journal.push_entry(entry);
		}
	}
	if
	(	m_source_entry_ctrl->is_all_zero() &&
		m_destination_entry_ctrl->is_all_zero()
	)
	{
		// Then maybe OK was pressed before the primary amount was
		// propagated through to the entries.
		// TODO This is messy, and feels fragile.
		if (primary_amount() != Decimal(0, 0))
		{
			// Then it CAN'T have been propagated. Let's propagate it
			// and try again.
			reset_entry_ctrl_amounts();
			post_journal();
			return;
		}
	}
	journal.set_comment("");

	optional<Frequency> const maybe_frequency = m_frequency_ctrl->frequency();
	if (maybe_frequency)
	{
		DraftJournal dj(m_database_connection);
		dj.mimic(journal);
		Repeater repeater(m_database_connection);
		repeater.set_frequency(value(maybe_frequency));
		repeater.set_next_date(m_date_ctrl->date());
		// TODO HIGH PRIORITY This can stuff up the next date is
		// invalid for the selected Frequency. Make sure this works
		// OK.
		dj.push_repeater(repeater);
		// TODO HIGH PRIORITY Get name for DraftJournal? Do we need one?
		// WARNING The name needs to be unique per database schema.
		dj.set_name(wx_to_bstring(wxEmptyString));
		assert (dj.is_balanced());
		dj.save();
		JEWEL_DEBUG_LOG << "Posted Journal:\n\n" << dj << endl;
	}
	else
	{
		assert (!maybe_frequency);
		OrdinaryJournal oj(m_database_connection);
		oj.mimic(journal);
		oj.set_date(m_date_ctrl->date());
		assert (oj.is_balanced());
		oj.save();
		JEWEL_DEBUG_LOG << "Posted journal:\n\n" << oj << endl;
	}
	return;
}

bool
TransactionCtrl::is_balanced() const
{
	// WARNING For now this is trivial, as we have only the primary_amount
	// informing one each of only two sides of the transaction. But it
	// probably won't always be trivial.
	return true;
	/*
	Decimal balance(0, 0);
	vector<DecimalTextCtrl*>::size_type i = 0;
	vector<DecimalTextCtrl*>::size_type const sz = m_amount_boxes.size();
	for ( ; i != sz; ++i)
	{
		balance += wx_to_decimal(m_amount_boxes[i]->GetValue(), locale());
	}
	return balance == Decimal(0, 0);
	*/
}

}  // namespace gui
}  // namespace phatbooks
<commit_msg>Updated a couple of TODOs in TransactionCtrl.<commit_after>// Copyright (c) 2013, Matthew Harvey. All rights reserved.

#include "transaction_ctrl.hpp"
#include "account.hpp"
#include "account_ctrl.hpp"
#include "account_reader.hpp"
#include "account_type.hpp"
#include "b_string.hpp"
#include "date.hpp"
#include "date_ctrl.hpp"
#include "decimal_text_ctrl.hpp"
#include "decimal_validator.hpp"
#include "draft_journal.hpp"
#include "entry.hpp"
#include "entry_ctrl.hpp"
#include "finformat.hpp"
#include "frame.hpp"
#include "frequency_ctrl.hpp"
#include "ordinary_journal.hpp"
#include "proto_journal.hpp"
#include "locale.hpp"
#include "phatbooks_database_connection.hpp"
#include "repeater.hpp"
#include "top_panel.hpp"
#include "transaction_type_ctrl.hpp"
#include "transaction_type.hpp"
#include <boost/optional.hpp>
#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/scoped_ptr.hpp>
#include <jewel/debug_log.hpp>
#include <jewel/decimal.hpp>
#include <jewel/on_windows.hpp>
#include <jewel/optional.hpp>
#include <wx/arrstr.h>
#include <wx/button.h>
#include <wx/combobox.h>
#include <wx/panel.h>
#include <wx/event.h>
#include <wx/msgdlg.h>
#include <wx/gbsizer.h>
#include <wx/stattext.h>
#include <wx/string.h>
#include <wx/textctrl.h>
#include <algorithm>
#include <cassert>
#include <iostream>
#include <vector>

using boost::optional;
using boost::scoped_ptr;
using jewel::Decimal;
using jewel::value;
using std::endl;
using std::vector;

namespace gregorian = boost::gregorian;

namespace phatbooks
{
namespace gui
{

BEGIN_EVENT_TABLE(TransactionCtrl, wxPanel)
	EVT_BUTTON
	(	wxID_OK,
		TransactionCtrl::on_ok_button_click
	)
	EVT_BUTTON
	(	wxID_CANCEL,
		TransactionCtrl::on_cancel_button_click
	)
END_EVENT_TABLE()

// WARNING There are bugs in wxWidgets' wxDatePickerCtrl under wxGTK.
// Firstly, tab traversal gets stuck on that control.
// Secondly, if we type a different date and then press "Enter" for OK,
// the date that actually gets picked up as the transaction date always
// seems to be TODAY's date, not the date actually entered. This appears to
// be an unresolved bug in wxWidgets.
// Note adding wxTAB_TRAVERSAL to style does not seem to fix the problem.
// We have used a simple custom class, DateCtrl here instead, to avoid
// these problems. Might later add a button to pop up a wxCalendarCtrl
// if the user wants one.

TransactionCtrl::TransactionCtrl
(	TopPanel* p_parent,
	vector<Account> const& p_balance_sheet_accounts,
	vector<Account> const& p_pl_accounts,
	PhatbooksDatabaseConnection& p_database_connection
):
	wxPanel
	(	p_parent,
		wxID_ANY,
		wxDefaultPosition,
		wxDefaultSize
	),
	m_top_sizer(0),
	m_transaction_type_ctrl(0),
	m_source_entry_ctrl(0),
	m_destination_entry_ctrl(0),
	m_primary_amount_ctrl(0),
	m_frequency_ctrl(0),
	m_date_ctrl(0),
	m_cancel_button(0),
	m_ok_button(0),
	m_database_connection(p_database_connection)
{
	assert (!p_balance_sheet_accounts.empty() || !p_pl_accounts.empty());
	assert (p_balance_sheet_accounts.size() + p_pl_accounts.size() >= 2);

	// Figure out the natural TransactionType given the Accounts we have
	// been passed. We will use this to initialize the TransactionTypeCtrl.
	Account account_x(p_database_connection);
	Account account_y(p_database_connection);
	if (p_balance_sheet_accounts.empty())
	{
		assert (p_pl_accounts.size() >= 2);
		account_x = p_pl_accounts[0];
		account_y = p_pl_accounts[1];
	}
	else if (p_pl_accounts.empty())
	{
		assert (p_balance_sheet_accounts.size() >= 2);
		account_x = p_balance_sheet_accounts[0];
		account_y = p_balance_sheet_accounts[1];
	}
	else
	{
		assert (!p_balance_sheet_accounts.empty());
		assert (!p_pl_accounts.empty());
		account_x = p_balance_sheet_accounts[0];
		account_y = p_pl_accounts[0];
	}
	if (account_y.account_type() == account_type::revenue)
	{
		using std::swap;
		swap(account_x, account_y);
	}
	assert (account_x.has_id());
	assert (account_y.has_id());
	transaction_type::TransactionType const initial_transaction_type =
		natural_transaction_type(account_x, account_y);

	size_t row = 0;

	// Top sizer
	m_top_sizer = new wxGridBagSizer();
	SetSizer(m_top_sizer);

	m_transaction_type_ctrl = new TransactionTypeCtrl
	(	this,
		s_transaction_type_ctrl_id,
		wxSize(160, wxDefaultSize.y),
		m_database_connection
	);
	m_transaction_type_ctrl->set_transaction_type(initial_transaction_type);
	wxSize const text_box_size = m_transaction_type_ctrl->GetSize();
	m_top_sizer->Add(m_transaction_type_ctrl, wxGBPosition(row, 0));

	m_primary_amount_ctrl = new DecimalTextCtrl
	(	this,
		s_primary_amount_ctrl_id,
		text_box_size,
		m_database_connection.default_commodity().precision(),
		false
	);
	m_top_sizer->Add
	(	m_primary_amount_ctrl,
		wxGBPosition(row, 3),
		wxDefaultSpan,
		wxALIGN_RIGHT
	);

	// We need the names of available Accounts, for the given
	// TransactionType, from which the user will choose
	// Accounts, for each side of the transaction.

	assert_transaction_type_validity(initial_transaction_type);

	// Rows for entering Entry details
	typedef vector<Account>::size_type Size;
	vector<Account> accounts;
	accounts.push_back(account_x);
	accounts.push_back(account_y);

	row += 2;

	// WARNING Temp hack
	assert (accounts.size() >= 2);

	vector<Account> source_accounts;
	source_accounts.push_back(accounts[0]);
	vector<Account> destination_accounts;
	destination_accounts.push_back(accounts[1]);

	Decimal::places_type const precision =
		m_database_connection.default_commodity().precision();
	m_source_entry_ctrl = new EntryCtrl
	(	this,
		source_accounts,
		m_database_connection,
		initial_transaction_type,
		text_box_size,
		true,
		Decimal(0, precision)
	);
	m_destination_entry_ctrl = new EntryCtrl
	(	this,
		destination_accounts,
		m_database_connection,
		initial_transaction_type,
		text_box_size,
		false,
		Decimal(0, precision)
	);
	m_top_sizer->Add
	(	m_source_entry_ctrl,
		wxGBPosition(row, 0),
		wxGBSpan(1, 4),
		wxEXPAND
	);

	row += 2;

	m_top_sizer->Add
	(	m_destination_entry_ctrl,
		wxGBPosition(row, 0),
		wxGBSpan(1, 4),
		wxEXPAND
	);

	row += 2;

	// Date and Frequency controls
	m_frequency_ctrl = new FrequencyCtrl
	(	this,
		wxID_ANY,
		wxSize(text_box_size.x * 3, text_box_size.y)
	);
	m_top_sizer->Add(m_frequency_ctrl, wxGBPosition(row, 0), wxGBSpan(1, 3));
	m_date_ctrl = new DateCtrl
	(	this,
		wxID_ANY,
		wxSize(text_box_size.x, text_box_size.y)
	);
	m_top_sizer->Add(m_date_ctrl, wxGBPosition(row, 3));

	row += 2;

	// Cancel/Clear button
	m_cancel_button = new wxButton
	(	this,
		wxID_CANCEL,
		wxString("&Clear"),
		wxDefaultPosition,
		wxSize(text_box_size.x, text_box_size.y)
	);
	m_top_sizer->Add(m_cancel_button, wxGBPosition(row, 0));

	// Save/OK button
	m_ok_button = new wxButton
	(	this,
		wxID_OK,
		wxString("&Save"),
		wxDefaultPosition,
		wxSize(text_box_size.x, text_box_size.y)
	);

	m_top_sizer->Add(m_ok_button, wxGBPosition(row, 3));
	m_ok_button->SetDefault();  // Enter key will now trigger "OK" button

	// "Admin"
	// SetSizer(m_top_sizer);
	m_top_sizer->Fit(this);
	m_top_sizer->SetSizeHints(this);
	Fit();
	Layout();
}

void
TransactionCtrl::refresh_for_transaction_type
(	transaction_type::TransactionType p_transaction_type
)
{
	m_source_entry_ctrl->
		refresh_for_transaction_type(p_transaction_type);
	m_destination_entry_ctrl->
		refresh_for_transaction_type(p_transaction_type);
	return;
}

void
TransactionCtrl::notify_decimal_ctrl_focus_kill()
{
	reset_entry_ctrl_amounts();
	return;
}

Decimal
TransactionCtrl::primary_amount() const
{
	return m_primary_amount_ctrl->amount();
}

void
TransactionCtrl::on_ok_button_click(wxCommandEvent& event)
{
	(void)event;  // Silence compiler re. unused parameter.
	Decimal const primary_amt = primary_amount();
	m_source_entry_ctrl->set_primary_amount(primary_amt);
	m_destination_entry_ctrl->set_primary_amount(primary_amt);
	if (Validate() && TransferDataFromWindow())
	{
		if (is_balanced())
		{
			post_journal();
			TopPanel* const panel = dynamic_cast<TopPanel*>(GetParent());
			assert (panel);
			panel->update();
		}
		else
		{
			wxMessageBox("Transaction does not balance.");
		}
	}
	return;
}

void
TransactionCtrl::on_cancel_button_click(wxCommandEvent& event)
{
	(void)event;  // Silence compiler re. unused parameter.
	TopPanel* const panel = dynamic_cast<TopPanel*>(GetParent());
	assert (panel);
	panel->update();
}

void
TransactionCtrl::reset_entry_ctrl_amounts()
{
	Decimal const pa = primary_amount();
	m_source_entry_ctrl->set_primary_amount(pa);
	m_destination_entry_ctrl->set_primary_amount(pa);
	return;
}

void
TransactionCtrl::post_journal()
{
	ProtoJournal journal;
	assert (transaction_type_ctrl->transaction_type());
	transaction_type::TransactionType const ttype =
		value(m_transaction_type_ctrl->transaction_type());
	journal.set_whether_actual(transaction_type_is_actual(ttype));

	size_t const num_entry_controls = 2;
	EntryCtrl const* const entry_controls[num_entry_controls] =
	{	m_source_entry_ctrl,
		m_destination_entry_ctrl
	};
	for (size_t i = 0; i != num_entry_controls; ++i)
	{
		vector<Entry> entries = entry_controls[i]->make_entries();
		for (vector<Entry>::size_type j = 0; j != entries.size(); ++j)
		{
			Entry entry = entries[j];
			journal.push_entry(entry);
		}
	}
	if
	(	m_source_entry_ctrl->is_all_zero() &&
		m_destination_entry_ctrl->is_all_zero()
	)
	{
		// Then maybe OK was pressed before the primary amount was
		// propagated through to the entries.
		// TODO This is messy, and feels fragile.
		if (primary_amount() != Decimal(0, 0))
		{
			// Then it CAN'T have been propagated. Let's propagate it
			// and try again.
			reset_entry_ctrl_amounts();
			post_journal();
			return;
		}
	}
	journal.set_comment("");

	optional<Frequency> const maybe_frequency = m_frequency_ctrl->frequency();
	if (maybe_frequency)
	{
		DraftJournal dj(m_database_connection);
		dj.mimic(journal);
		Repeater repeater(m_database_connection);
		repeater.set_frequency(value(maybe_frequency));
		repeater.set_next_date(m_date_ctrl->date());
		// TODO HIGH PRIORITY This can stuff up the next date is
		// invalid for the selected Frequency. Make sure this works
		// OK.
		dj.push_repeater(repeater);
		// TODO HIGH PRIORITY Get name for DraftJournal? Do we need one?
		// WARNING The name needs to be unique per database schema.
		dj.set_name(wx_to_bstring(wxEmptyString));
		assert (dj.is_balanced());
		dj.save();
		JEWEL_DEBUG_LOG << "Posted Journal:\n\n" << dj << endl;
	}
	else
	{
		assert (!maybe_frequency);
		OrdinaryJournal oj(m_database_connection);
		oj.mimic(journal);
		oj.set_date(m_date_ctrl->date());
		assert (oj.is_balanced());
		oj.save();
		JEWEL_DEBUG_LOG << "Posted journal:\n\n" << oj << endl;
	}
	return;
}

bool
TransactionCtrl::is_balanced() const
{
	// WARNING For now this is trivial, as we have only the primary_amount
	// informing one each of only two sides of the transaction. But it
	// probably won't always be trivial.
	return true;
	/*
	Decimal balance(0, 0);
	vector<DecimalTextCtrl*>::size_type i = 0;
	vector<DecimalTextCtrl*>::size_type const sz = m_amount_boxes.size();
	for ( ; i != sz; ++i)
	{
		balance += wx_to_decimal(m_amount_boxes[i]->GetValue(), locale());
	}
	return balance == Decimal(0, 0);
	*/
}

}  // namespace gui
}  // namespace phatbooks
<|endoftext|>


<commit_before>// Copyright (c) 2015-present, Qihoo, Inc.  All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.

#include "pink/include/server_thread.h"

#include <arpa/inet.h>
#include <sys/time.h>
#include <fcntl.h>

#include "slash/include/xdebug.h"
#include "pink/src/pink_epoll.h"
#include "pink/src/server_socket.h"

namespace pink {

using slash::Status;

class DefaultServerHandle : public ServerHandle {
 public:
  virtual void CronHandle() const override {}
  virtual void FdTimeoutHandle(
      int fd, const std::string& ip_port) const override {
    UNUSED(fd);
    UNUSED(ip_port);
  }
  virtual void FdClosedHandle(
      int fd, const std::string& ip_port) const override {
    UNUSED(fd);
    UNUSED(ip_port);
  }
  virtual bool AccessHandle(std::string& ip) const override {
    UNUSED(ip);
    return true;
  }
  virtual bool AccessHandle(int fd, std::string& ip) const override {
    UNUSED(fd);
    UNUSED(ip);
    return true;
  }
  virtual int CreateWorkerSpecificData(void** data) const override {
    UNUSED(data);
    return 0;
  }
  virtual int DeleteWorkerSpecificData(void* data) const override {
    UNUSED(data);
    return 0;
  }
};

static const ServerHandle* SanitizeHandle(const ServerHandle* raw_handle) {
  if (raw_handle == nullptr) {
    return new DefaultServerHandle();
  }
  return raw_handle;
}

ServerThread::ServerThread(int port,
                           int cron_interval, const ServerHandle* handle)
    : pink_epoll_(NULL),
      cron_interval_(cron_interval),
      handle_(SanitizeHandle(handle)),
      own_handle_(handle_ != handle),
#ifdef __ENABLE_SSL
      security_(false),
#endif
      port_(port) {
  ips_.insert("0.0.0.0");
}

ServerThread::ServerThread(const std::string& bind_ip, int port,
                           int cron_interval, const ServerHandle* handle)
    : cron_interval_(cron_interval),
      handle_(SanitizeHandle(handle)),
      own_handle_(handle_ != handle),
#ifdef __ENABLE_SSL
      security_(false),
#endif
      port_(port) {
  ips_.insert(bind_ip);
}

ServerThread::ServerThread(const std::set<std::string>& bind_ips, int port,
                           int cron_interval, const ServerHandle* handle)
    : cron_interval_(cron_interval),
      handle_(SanitizeHandle(handle)),
      own_handle_(handle_ != handle),
#ifdef __ENABLE_SSL
      security_(false),
#endif
      port_(port) {
  ips_ = bind_ips;
}

ServerThread::~ServerThread() {
#ifdef __ENABLE_SSL
  if (security_) {
    SSL_CTX_free(ssl_ctx_);
    EVP_cleanup();
  }
#endif
  delete(pink_epoll_);
  for (std::vector<ServerSocket*>::iterator iter = server_sockets_.begin();
       iter != server_sockets_.end();
       ++iter) {
    delete *iter;
  }
  if (own_handle_) {
    delete handle_;
  }
}

int ServerThread::StartThread() {
  int ret = 0;
  ret = InitHandle();
  if (ret != kSuccess)
    return ret;
  return Thread::StartThread();
}

int ServerThread::InitHandle() {
  int ret = 0;
  ServerSocket* socket_p;
  pink_epoll_ = new PinkEpoll();
  if (ips_.find("0.0.0.0") != ips_.end()) {
    ips_.clear();
    ips_.insert("0.0.0.0");
  }
  for (std::set<std::string>::iterator iter = ips_.begin();
       iter != ips_.end();
       ++iter) {
    socket_p = new ServerSocket(port_);
    ret = socket_p->Listen(*iter);
    if (ret != kSuccess) {
      return ret;
    }

    // init pool
    pink_epoll_->PinkAddEvent(
        socket_p->sockfd(), EPOLLIN | EPOLLERR | EPOLLHUP);
    server_sockets_.push_back(socket_p);
    server_fds_.insert(socket_p->sockfd());
  }
  return kSuccess;
}

void ServerThread::DoCronTask() {
}

void *ServerThread::ThreadMain() {
  int nfds;
  PinkFiredEvent *pfe;
  Status s;
  struct sockaddr_in cliaddr;
  socklen_t clilen = sizeof(struct sockaddr);
  int fd, connfd;

  struct timeval when;
  gettimeofday(&when, nullptr);
  struct timeval now = when;

  when.tv_sec += (cron_interval_ / 1000);
  when.tv_usec += ((cron_interval_ % 1000) * 1000);
  int timeout = cron_interval_;
  if (timeout <= 0) {
    timeout = PINK_CRON_INTERVAL;
  }

  std::string ip_port;
  char port_buf[32];
  char ip_addr[INET_ADDRSTRLEN] = "";

  while (!should_stop()) {
    if (cron_interval_ > 0) {
      gettimeofday(&now, nullptr);
      if (when.tv_sec > now.tv_sec ||
          (when.tv_sec == now.tv_sec && when.tv_usec > now.tv_usec)) {
        timeout = (when.tv_sec - now.tv_sec) * 1000 +
          (when.tv_usec - now.tv_usec) / 1000;
      } else {
        // Do own cron task as well as user's
        DoCronTask();
        handle_->CronHandle();

        when.tv_sec = now.tv_sec + (cron_interval_ / 1000);
        when.tv_usec = now.tv_usec + ((cron_interval_ % 1000) * 1000);
        timeout = cron_interval_;
      }
    }

    nfds = pink_epoll_->PinkPoll(timeout);
    for (int i = 0; i < nfds; i++) {
      pfe = (pink_epoll_->firedevent()) + i;
      fd = pfe->fd;
      /*
       * Handle server event
       */
      if (server_fds_.find(fd) != server_fds_.end()) {
        if (pfe->mask & EPOLLIN) {
          connfd = accept(fd, (struct sockaddr *) &cliaddr, &clilen);
          if (connfd == -1) {
            log_warn("accept error, errno numberis %d, error reason %s",
                     errno, strerror(errno));
            continue;
          }
          fcntl(connfd, F_SETFD, fcntl(connfd, F_GETFD) | FD_CLOEXEC);

          // Just ip
          ip_port =
            inet_ntop(AF_INET, &cliaddr.sin_addr, ip_addr, sizeof(ip_addr));

          if (!handle_->AccessHandle(ip_port) ||
              !handle_->AccessHandle(connfd, ip_port)) {
            close(connfd);
            continue;
          }

          ip_port.append(":");
          snprintf(port_buf, sizeof(port_buf), "%d", ntohs(cliaddr.sin_port));
          ip_port.append(port_buf);

          /*
           * Handle new connection,
           * implemented in derived class
           */
          HandleNewConn(connfd, ip_port);

        } else if (pfe->mask & (EPOLLHUP | EPOLLERR)) {
          /*
           * this branch means there is error on the listen fd
           */
          close(pfe->fd);
          continue;
        }
      } else {
        /*
         * Handle connection's event
         * implemented in derived class
         */
        HandleConnEvent(pfe);
      }
    }
  }
  return nullptr;
}

#ifdef __ENABLE_SSL
static std::vector<std::unique_ptr<slash::Mutex>> ssl_mutex_;

static void SSLLockingCallback(int mode, int type, const char* file, int line) {
  if (mode & CRYPTO_LOCK) {
    ssl_mutex_[type]->Lock();
  } else {
    ssl_mutex_[type]->Unlock();
  }
}

static unsigned long SSLIdCallback() {
  return (unsigned long)pthread_self();
}

int ServerThread::EnableSecurity(const std::string& cert_file,
                                 const std::string& key_file) {
  if (cert_file.empty() || key_file.empty()) {
    log_warn("cert_file and key_file can not be empty!");
  }
  // Init Security Env
  // 1. Create multithread mutex used by openssl
  ssl_mutex_.resize(CRYPTO_num_locks());
  for (auto& sm : ssl_mutex_) {
    sm.reset(new slash::Mutex());
  }
  CRYPTO_set_locking_callback(SSLLockingCallback);
  CRYPTO_set_id_callback(SSLIdCallback);

  // 2. Use default configuration
  OPENSSL_config(NULL);

  // 3. Init library, load all algorithms
  SSL_library_init();
  SSL_load_error_strings();
  OpenSSL_add_all_algorithms();

  // 4. Create ssl context
  ssl_ctx_ = SSL_CTX_new(SSLv23_server_method());
  if (!ssl_ctx_) {
    log_warn("Unable to create SSL context");
    return -1;
  }

  // 5. Set cert file and key file, then check key file
  if (SSL_CTX_use_certificate_file(
          ssl_ctx_, cert_file.c_str(), SSL_FILETYPE_PEM) != 1) {
    log_warn("SSL_CTX_use_certificate_file(%s) failed", cert_file.c_str());
    return -1;
  }

  if (SSL_CTX_use_PrivateKey_file(
          ssl_ctx_, key_file.c_str(), SSL_FILETYPE_PEM) != 1) {
    log_warn("SSL_CTX_use_PrivateKey_file(%s)", key_file.c_str());
    return -1;
  }

  if (SSL_CTX_check_private_key(ssl_ctx_) != 1) {
    log_warn("SSL_CTX_check_private_key(%s)", key_file.c_str());
    return -1;
  }

  // https://wiki.openssl.org/index.php/Manual:SSL_CTX_set_read_ahead(3)
  // read data as more as possible
  SSL_CTX_set_read_ahead(ssl_ctx_, true);

  // Force using TLS 1.2
  SSL_CTX_set_options(ssl_ctx_, SSL_OP_NO_SSLv2);
  SSL_CTX_set_options(ssl_ctx_, SSL_OP_NO_SSLv3);
  SSL_CTX_set_options(ssl_ctx_, SSL_OP_NO_TLSv1);

  // Enable ECDH
  // https://en.wikipedia.org/wiki/Elliptic_curve_Diffie%E2%80%93Hellman
  // https://wiki.openssl.org/index.php/Diffie_Hellman
  // https://wiki.openssl.org/index.php/Diffie-Hellman_parameters
  EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
  if (!ecdh) {
    log_warn("EC_KEY_new_by_curve_name(%d)", NID_X9_62_prime256v1);
    return -1;
  }

  SSL_CTX_set_options(ssl_ctx_, SSL_OP_SINGLE_ECDH_USE);
  SSL_CTX_set_tmp_ecdh(ssl_ctx_, ecdh);
  EC_KEY_free(ecdh);

  security_ = true;
  return 0;
}
#endif

}  // namespace pink
<commit_msg>Update server_thread.cc<commit_after>// Copyright (c) 2015-present, Qihoo, Inc.  All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.

#include "pink/include/server_thread.h"

#include <arpa/inet.h>
#include <sys/time.h>
#include <fcntl.h>

#include "slash/include/xdebug.h"
#include "pink/src/pink_epoll.h"
#include "pink/src/server_socket.h"

namespace pink {

using slash::Status;

class DefaultServerHandle : public ServerHandle {
 public:
  virtual void CronHandle() const override {}
  virtual void FdTimeoutHandle(
      int fd, const std::string& ip_port) const override {
    UNUSED(fd);
    UNUSED(ip_port);
  }
  virtual void FdClosedHandle(
      int fd, const std::string& ip_port) const override {
    UNUSED(fd);
    UNUSED(ip_port);
  }
  virtual bool AccessHandle(std::string& ip) const override {
    UNUSED(ip);
    return true;
  }
  virtual bool AccessHandle(int fd, std::string& ip) const override {
    UNUSED(fd);
    UNUSED(ip);
    return true;
  }
  virtual int CreateWorkerSpecificData(void** data) const override {
    UNUSED(data);
    return 0;
  }
  virtual int DeleteWorkerSpecificData(void* data) const override {
    UNUSED(data);
    return 0;
  }
};

static const ServerHandle* SanitizeHandle(const ServerHandle* raw_handle) {
  if (raw_handle == nullptr) {
    return new DefaultServerHandle();
  }
  return raw_handle;
}

ServerThread::ServerThread(int port,
                           int cron_interval, const ServerHandle* handle)
    : pink_epoll_(NULL),
      cron_interval_(cron_interval),
      handle_(SanitizeHandle(handle)),
      own_handle_(handle_ != handle),
#ifdef __ENABLE_SSL
      security_(false),
#endif
      port_(port) {
  ips_.insert("0.0.0.0");
}

ServerThread::ServerThread(const std::string& bind_ip, int port,
                           int cron_interval, const ServerHandle* handle)
    : cron_interval_(cron_interval),
      handle_(SanitizeHandle(handle)),
      own_handle_(handle_ != handle),
#ifdef __ENABLE_SSL
      security_(false),
#endif
      port_(port) {
  ips_.insert(bind_ip);
}

ServerThread::ServerThread(const std::set<std::string>& bind_ips, int port,
                           int cron_interval, const ServerHandle* handle)
    : cron_interval_(cron_interval),
      handle_(SanitizeHandle(handle)),
      own_handle_(handle_ != handle),
#ifdef __ENABLE_SSL
      security_(false),
#endif
      port_(port) {
  ips_ = bind_ips;
}

ServerThread::~ServerThread() {
#ifdef __ENABLE_SSL
  if (security_) {
    SSL_CTX_free(ssl_ctx_);
    EVP_cleanup();
  }
#endif
  delete(pink_epoll_);
  for (std::vector<ServerSocket*>::iterator iter = server_sockets_.begin();
       iter != server_sockets_.end();
       ++iter) {
    delete *iter;
  }
  if (own_handle_) {
    delete handle_;
  }
}

int ServerThread::StartThread() {
  int ret = 0;
  ret = InitHandle();
  if (ret != kSuccess)
    return ret;
  return Thread::StartThread();
}

int ServerThread::InitHandle() {
  int ret = 0;
  ServerSocket* socket_p;
  pink_epoll_ = new PinkEpoll();
  if (ips_.find("0.0.0.0") != ips_.end()) {
    ips_.clear();
    ips_.insert("0.0.0.0");
  }
  for (std::set<std::string>::iterator iter = ips_.begin();
       iter != ips_.end();
       ++iter) {
    socket_p = new ServerSocket(port_);
    ret = socket_p->Listen(*iter);
    if (ret != kSuccess) {
      return ret;
    }

    // init pool
    pink_epoll_->PinkAddEvent(
        socket_p->sockfd(), EPOLLIN | EPOLLERR | EPOLLHUP);
    server_sockets_.push_back(socket_p);
    server_fds_.insert(socket_p->sockfd());
  }
  return kSuccess;
}

void ServerThread::DoCronTask() {
}

void *ServerThread::ThreadMain() {
  int nfds;
  PinkFiredEvent *pfe;
  Status s;
  struct sockaddr_in cliaddr;
  socklen_t clilen = sizeof(struct sockaddr);
  int fd, connfd;

  struct timeval when;
  gettimeofday(&when, nullptr);
  struct timeval now = when;

  when.tv_sec += (cron_interval_ / 1000);
  when.tv_usec += ((cron_interval_ % 1000) * 1000);
  int timeout = cron_interval_;
  if (timeout <= 0) {
    timeout = PINK_CRON_INTERVAL;
  }

  std::string ip_port;
  char port_buf[32];
  char ip_addr[INET_ADDRSTRLEN] = "";

  while (!should_stop()) {
    if (cron_interval_ > 0) {
      gettimeofday(&now, nullptr);
      if (when.tv_sec > now.tv_sec ||
          (when.tv_sec == now.tv_sec && when.tv_usec > now.tv_usec)) {
        timeout = (when.tv_sec - now.tv_sec) * 1000 +
          (when.tv_usec - now.tv_usec) / 1000;
      } else {
        // Do own cron task as well as user's
        DoCronTask();
        handle_->CronHandle();

        when.tv_sec = now.tv_sec + (cron_interval_ / 1000);
        when.tv_usec = now.tv_usec + ((cron_interval_ % 1000) * 1000);
        timeout = cron_interval_;
      }
    }

    nfds = pink_epoll_->PinkPoll(timeout);
    for (int i = 0; i < nfds; i++) {
      pfe = (pink_epoll_->firedevent()) + i;
      fd = pfe->fd;
      /*
       * Handle server event
       */
      if (server_fds_.find(fd) != server_fds_.end()) {
        if (pfe->mask & EPOLLIN) {
          connfd = accept(fd, (struct sockaddr *) &cliaddr, &clilen);
          if (connfd == -1) {
            log_warn("accept error, errno numberis %d, error reason %s",
                     errno, strerror(errno));
            continue;
          }
          fcntl(connfd, F_SETFD, fcntl(connfd, F_GETFD) | FD_CLOEXEC);

          // Just ip
          ip_port =
            inet_ntop(AF_INET, &cliaddr.sin_addr, ip_addr, sizeof(ip_addr));

          if (!handle_->AccessHandle(ip_port) ||
              !handle_->AccessHandle(connfd, ip_port)) {
            close(connfd);
            continue;
          }

          ip_port.append(":");
          snprintf(port_buf, sizeof(port_buf), "%d", ntohs(cliaddr.sin_port));
          ip_port.append(port_buf);

          /*
           * Handle new connection,
           * implemented in derived class
           */
          HandleNewConn(connfd, ip_port);

        } else if (pfe->mask & (EPOLLHUP | EPOLLERR)) {
          /*
           * this branch means there is error on the listen fd
           */
          close(pfe->fd);
          continue;
        }
      } else {
        /*
         * Handle connection's event
         * implemented in derived class
         */
        HandleConnEvent(pfe);
      }
    }
  }
  return nullptr;
}

#ifdef __ENABLE_SSL
static std::vector<std::unique_ptr<slash::Mutex>> ssl_mutex_;

static void SSLLockingCallback(int mode, int type, const char* file, int line) {
  if (mode & CRYPTO_LOCK) {
    ssl_mutex_[type]->Lock();
  } else {
    ssl_mutex_[type]->Unlock();
  }
}

static unsigned long SSLIdCallback() {
  return (unsigned long)pthread_self();
}

int ServerThread::EnableSecurity(const std::string& cert_file,
                                 const std::string& key_file) {
  if (cert_file.empty() || key_file.empty()) {
    log_warn("cert_file and key_file can not be empty!");
  }
  // Init Security Env
  // 1. Create multithread mutex used by openssl
  ssl_mutex_.resize(CRYPTO_num_locks());
  for (auto& sm : ssl_mutex_) {
    sm.reset(new slash::Mutex());
  }
  CRYPTO_set_locking_callback(SSLLockingCallback);
  CRYPTO_set_id_callback(SSLIdCallback);

  // 2. Use default configuration
  OPENSSL_config(NULL);

  // 3. Init library, load all algorithms
  SSL_library_init();
  SSL_load_error_strings();
  OpenSSL_add_all_algorithms();

  // 4. Create ssl context
  ssl_ctx_ = SSL_CTX_new(SSLv23_server_method());
  if (!ssl_ctx_) {
    log_warn("Unable to create SSL context");
    return -1;
  }

  // 5. Set cert file and key file, then check key file
  if (SSL_CTX_use_certificate_file(
          ssl_ctx_, cert_file.c_str(), SSL_FILETYPE_PEM) != 1) {
    log_warn("SSL_CTX_use_certificate_file(%s) failed", cert_file.c_str());
    return -1;
  }

  if (SSL_CTX_use_PrivateKey_file(
          ssl_ctx_, key_file.c_str(), SSL_FILETYPE_PEM) != 1) {
    log_warn("SSL_CTX_use_PrivateKey_file(%s)", key_file.c_str());
    return -1;
  }

  if (SSL_CTX_check_private_key(ssl_ctx_) != 1) {
    log_warn("SSL_CTX_check_private_key(%s)", key_file.c_str());
    return -1;
  }

  // https://wiki.openssl.org/index.php/Manual:SSL_CTX_set_read_ahead(3)
  // read data as more as possible
  SSL_CTX_set_read_ahead(ssl_ctx_, true);

  // Force using TLS 1.2
  SSL_CTX_set_options(ssl_ctx_, SSL_OP_NO_SSLv2);
  SSL_CTX_set_options(ssl_ctx_, SSL_OP_NO_SSLv3);
  SSL_CTX_set_options(ssl_ctx_, SSL_OP_NO_TLSv1);

  // Enable ECDH
  // https://en.wikipedia.org/wiki/Elliptic_curve_Diffie%E2%80%93Hellman
  // https://wiki.openssl.org/index.php/Diffie_Hellman
  // https://wiki.openssl.org/index.php/Diffie-Hellman_parameters
  EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
  if (!ecdh) {
    log_warn("EC_KEY_new_by_curve_name(%d)", NID_X9_62_prime256v1);
    return -1;
  }

  SSL_CTX_set_options(ssl_ctx_, SSL_OP_SINGLE_ECDH_USE);
  SSL_CTX_set_tmp_ecdh(ssl_ctx_, ecdh);
  EC_KEY_free(ecdh);

  security_ = true;
  return 0;
}

}  // namespace pink
<|endoftext|>