text:
/***************************************************************************
 * NASA Glenn Research Center, Cleveland, OH
 * Released under the NASA Open Source Agreement (NOSA)
 * May  2021
 *
 ****************************************************************************
 */
#include "codec/bpv7.h"
#include "codec/Bpv7Crc.h"
#include "CborUint.h"
#include <boost/format.hpp>
#include <boost/make_unique.hpp>

/////////////////////////////////////////
// ABSTRACT SECURITY (EXTENSION) BLOCK
/////////////////////////////////////////

Bpv7AbstractSecurityBlock::Bpv7AbstractSecurityBlock() : Bpv7CanonicalBlock() { //a default constructor: X() //don't initialize anything for efficiency, use SetZero if required
    //m_blockTypeCode = ???;
}
Bpv7AbstractSecurityBlock::~Bpv7AbstractSecurityBlock() { } //a destructor: ~X()
Bpv7AbstractSecurityBlock::Bpv7AbstractSecurityBlock(Bpv7AbstractSecurityBlock&& o) :
    Bpv7CanonicalBlock(std::move(o)),
    m_securityTargets(std::move(o.m_securityTargets)),
    m_securityContextId(o.m_securityContextId),
    m_securityContextFlags(o.m_securityContextFlags),
    m_securitySource(std::move(o.m_securitySource)),
    m_securityContextParametersOptional(std::move(o.m_securityContextParametersOptional)),
    m_securityResults(std::move(o.m_securityResults)) { } //a move constructor: X(X&&)
Bpv7AbstractSecurityBlock& Bpv7AbstractSecurityBlock::operator=(Bpv7AbstractSecurityBlock && o) { //a move assignment: operator=(X&&)
    m_securityTargets = std::move(o.m_securityTargets);
    m_securityContextId = o.m_securityContextId;
    m_securityContextFlags = o.m_securityContextFlags;
    m_securitySource = std::move(o.m_securitySource);
    m_securityContextParametersOptional = std::move(o.m_securityContextParametersOptional);
    m_securityResults = std::move(o.m_securityResults);
    return static_cast<Bpv7AbstractSecurityBlock&>(Bpv7CanonicalBlock::operator=(std::move(o)));
}
bool Bpv7AbstractSecurityBlock::operator==(const Bpv7AbstractSecurityBlock & o) const {
    const bool initialTest = (m_securityTargets == o.m_securityTargets)
        && (m_securityContextId == o.m_securityContextId)
        && (m_securityContextFlags == o.m_securityContextFlags)
        && (m_securitySource == o.m_securitySource)
        //&& ((IsCipherSuiteParametersPresent()) ? (m_cipherSuiteParametersOptional == o.m_cipherSuiteParametersOptional) : true)
        //&& (m_securityResults == o.m_securityResults)
        && Bpv7CanonicalBlock::operator==(o);
    if (!initialTest) {
        return false;
    }
    if (IsSecurityContextParametersPresent()) {
        if (!Bpv7AbstractSecurityBlock::IsEqual(m_securityContextParametersOptional, o.m_securityContextParametersOptional)) {
            return false;
        }
    }
    return Bpv7AbstractSecurityBlock::IsEqual(m_securityResults, o.m_securityResults);
}
bool Bpv7AbstractSecurityBlock::operator!=(const Bpv7AbstractSecurityBlock & o) const {
    return !(*this == o);
}
void Bpv7AbstractSecurityBlock::SetZero() {
    Bpv7CanonicalBlock::SetZero();
    m_securityTargets.clear();
    m_securityContextId = 0;
    m_securityContextFlags = 0;
    m_securitySource.SetZero();
    m_securityContextParametersOptional.clear();
    m_securityResults.clear();
    m_blockTypeCode = BPV7_BLOCK_TYPE_CODE::PRIMARY_IMPLICIT_ZERO; //??
}

//Security Context Flags:
//This field identifies which optional fields are present in the
//security block.  This field SHALL be represented as a CBOR
//unsigned integer whose contents shall be interpreted as a bit
//field.  Each bit in this bit field indicates the presence (bit
//set to 1) or absence (bit set to 0) of optional data in the
//security block.  The association of bits to security block data
//is defined as follows.
//
//Bit 0  (the least-significant bit, 0x01): Security Context
//    Parameters Present Flag.
//
//Bit >0 Reserved
//
//Implementations MUST set reserved bits to 0 when writing this
//field and MUST ignore the values of reserved bits when reading
//this field.  For unreserved bits, a value of 1 indicates that
//the associated security block field MUST be included in the
//security block.  A value of 0 indicates that the associated
//security block field MUST NOT be in the security block.
bool Bpv7AbstractSecurityBlock::IsSecurityContextParametersPresent() const {
    return ((m_securityContextFlags & 0x1) != 0);
}
void Bpv7AbstractSecurityBlock::SetSecurityContextParametersPresent() {
    m_securityContextFlags |= 0x1;
}
void Bpv7AbstractSecurityBlock::ClearSecurityContextParametersPresent() {
    m_securityContextFlags &= ~(static_cast<uint8_t>(0x1));
}

uint64_t Bpv7AbstractSecurityBlock::SerializeBpv7(uint8_t * serialization) {
    //m_blockTypeCode = ???
    m_dataPtr = NULL;
    m_dataLength = GetCanonicalBlockTypeSpecificDataSerializationSize();
    const uint64_t serializationSizeCanonical = Bpv7CanonicalBlock::SerializeBpv7(serialization);
    uint64_t bufferSize = m_dataLength;
    uint8_t * blockSpecificDataSerialization = m_dataPtr;
    uint64_t thisSerializationSize;

    //The fields of the ASB SHALL be as follows, listed in the order in
    //which they must appear.  The encoding of these fields MUST be in
    //accordance with the canonical forms provided in Section 4.

    //Security Targets:
    //This field identifies the block(s) targeted by the security
    //operation(s) represented by this security block.  Each target
    //block is represented by its unique Block Number.  This field
    //SHALL be represented by a CBOR array of data items.  Each
    //target within this CBOR array SHALL be represented by a CBOR
    //unsigned integer.  This array MUST have at least 1 entry and
    //each entry MUST represent the Block Number of a block that
    //exists in the bundle.  There MUST NOT be duplicate entries in
    //this array.  The order of elements in this list has no semantic
    //meaning outside of the context of this block.  Within the
    //block, the ordering of targets must match the ordering of
    //results associated with these targets.
    thisSerializationSize = CborArbitrarySizeUint64ArraySerialize(blockSpecificDataSerialization, m_securityTargets, bufferSize);
    blockSpecificDataSerialization += thisSerializationSize;
    bufferSize -= thisSerializationSize;

    //Security Context Id:
    //This field identifies the security context used to implement
    //the security service represented by this block and applied to
    //each security target.  This field SHALL be represented by a
    //CBOR unsigned integer.  The values for this Id should come from
    //the registry defined in Section 11.3
    thisSerializationSize = CborEncodeU64(blockSpecificDataSerialization, m_securityContextId, bufferSize);
    blockSpecificDataSerialization += thisSerializationSize;
    bufferSize -= thisSerializationSize;

    //Security Context Flags:
    //This field identifies which optional fields are present in the
    //security block.  This field SHALL be represented as a CBOR
    //unsigned integer whose contents shall be interpreted as a bit
    //field.  Each bit in this bit field indicates the presence (bit
    //set to 1) or absence (bit set to 0) of optional data in the
    //security block.  The association of bits to security block data
    //is defined as follows.
    //
    //Bit 0  (the least-significant bit, 0x01): Security Context
    //    Parameters Present Flag.
    //
    //Bit >0 Reserved
    //
    //Implementations MUST set reserved bits to 0 when writing this
    //field and MUST ignore the values of reserved bits when reading
    //this field.  For unreserved bits, a value of 1 indicates that
    //the associated security block field MUST be included in the
    //security block.  A value of 0 indicates that the associated
    //security block field MUST NOT be in the security block.
    thisSerializationSize = CborEncodeU64(blockSpecificDataSerialization, m_securityContextFlags, bufferSize);
    blockSpecificDataSerialization += thisSerializationSize;
    bufferSize -= thisSerializationSize;

    //Security Source:
    //This field identifies the Endpoint that inserted the security
    //block in the bundle.  This field SHALL be represented by a CBOR
    //array in accordance with [I-D.ietf-dtn-bpbis] rules for
    //representing Endpoint Identifiers (EIDs).
    thisSerializationSize = m_securitySource.SerializeBpv7(blockSpecificDataSerialization, bufferSize);
    blockSpecificDataSerialization += thisSerializationSize;
    bufferSize -= thisSerializationSize;

    //Security Context Parameters (Optional):
    //This field captures one or more security context parameters
    //that should be used when processing the security service
    //described by this security block.  This field SHALL be
    //represented by a CBOR array.  Each entry in this array is a
    //single security context parameter.  A single parameter SHALL
    //also be represented as a CBOR array comprising a 2-tuple of the
    //id and value of the parameter, as follows.
    //
    //*  Parameter Id.  This field identifies which parameter is
    //   being specified.  This field SHALL be represented as a CBOR
    //   unsigned integer.  Parameter Ids are selected as described
    //   in Section 3.10.
    //
    //*  Parameter Value.  This field captures the value associated
    //   with this parameter.  This field SHALL be represented by the
    //   applicable CBOR representation of the parameter, in
    //   accordance with Section 3.10.
    if (IsSecurityContextParametersPresent()) {
        thisSerializationSize = SerializeIdValuePairsVecBpv7(blockSpecificDataSerialization, m_securityContextParametersOptional, bufferSize);
        blockSpecificDataSerialization += thisSerializationSize;
        bufferSize -= thisSerializationSize;
    }

    //Security Results:
    //This field captures the results of applying a security service
    //to the security targets of the security block.  This field
    //SHALL be represented as a CBOR array of target results.  Each
    //entry in this array represents the set of security results for
    //a specific security target.  The target results MUST be ordered
    //identically to the Security Targets field of the security
    //block.  This means that the first set of target results in this
    //array corresponds to the first entry in the Security Targets
    //field of the security block, and so on.  There MUST be one
    //entry in this array for each entry in the Security Targets
    //field of the security block.
    //
    //The set of security results for a target is also represented as
    //a CBOR array of individual results.  An individual result is
    //represented as a 2-tuple of a result id and a result value,
    //defined as follows.
    //
    //*  Result Id.  This field identifies which security result is
    //   being specified.  Some security results capture the primary
    //   output of a cipher suite.  Other security results contain
    //   additional annotative information from cipher suite
    //   processing.  This field SHALL be represented as a CBOR
    //   unsigned integer.  Security result Ids will be as specified
    //   in Section 3.10.
    //
    //*  Result Value.  This field captures the value associated with
    //   the result.  This field SHALL be represented by the
    //   applicable CBOR representation of the result value, in
    //   accordance with Section 3.10.
    thisSerializationSize = SerializeIdValuePairsVecBpv7(blockSpecificDataSerialization, m_securityResults, bufferSize);
    blockSpecificDataSerialization += thisSerializationSize;

    RecomputeCrcAfterDataModification(serialization, serializationSizeCanonical);
    return serializationSizeCanonical;
}
uint64_t Bpv7AbstractSecurityBlock::GetCanonicalBlockTypeSpecificDataSerializationSize() const {
    uint64_t serializationSize = CborArbitrarySizeUint64ArraySerializationSize(m_securityTargets);
    serializationSize += CborGetEncodingSizeU64(m_securityContextId);
    serializationSize += CborGetEncodingSizeU64(m_securityContextFlags);
    serializationSize += m_securitySource.GetSerializationSizeBpv7();
    if (IsSecurityContextParametersPresent()) {
        serializationSize += IdValuePairsVecBpv7SerializationSize(m_securityContextParametersOptional);
    }
    serializationSize += IdValuePairsVecBpv7SerializationSize(m_securityResults);
    return serializationSize;
}

bool Bpv7AbstractSecurityBlock::Virtual_DeserializeExtensionBlockDataBpv7() {
    static constexpr uint64_t maxElements = 1000; //todo
    uint8_t numBytesTakenToDecode;
    if (m_dataPtr == NULL) {
        return false;
    }

    uint8_t * serialization = m_dataPtr;
    uint64_t bufferSize = m_dataLength;
    uint8_t cborUintSizeDecoded;
    uint64_t tmpNumBytesTakenToDecode64;
    if (!CborArbitrarySizeUint64ArrayDeserialize(serialization, tmpNumBytesTakenToDecode64, bufferSize, m_securityTargets, maxElements)) {
        return false; //failure
    }
    serialization += tmpNumBytesTakenToDecode64;
    bufferSize -= tmpNumBytesTakenToDecode64;

    m_securityContextId = CborDecodeU64(serialization, &cborUintSizeDecoded, bufferSize);
    if (cborUintSizeDecoded == 0) {
        return false; //failure
    }
    serialization += cborUintSizeDecoded;
    bufferSize -= cborUintSizeDecoded;

    const uint64_t tmpCipherSuiteFlags64 = CborDecodeU64(serialization, &cborUintSizeDecoded, bufferSize);
    if (cborUintSizeDecoded == 0) {
        return false; //failure
    }
    if(tmpCipherSuiteFlags64 > 0x1f) {
        return false; //failure
    }
    m_securityContextFlags = static_cast<uint8_t>(tmpCipherSuiteFlags64);
    serialization += cborUintSizeDecoded;
    bufferSize -= cborUintSizeDecoded;

    if(!m_securitySource.DeserializeBpv7(serialization, &numBytesTakenToDecode, m_dataLength)) {
        return false; //failure
    }
    serialization += numBytesTakenToDecode;
    bufferSize -= numBytesTakenToDecode;

    if (IsSecurityContextParametersPresent()) {
        if(!DeserializeIdValuePairsVecBpv7(serialization, tmpNumBytesTakenToDecode64, bufferSize, m_securityContextParametersOptional,
            static_cast<BPSEC_SECURITY_CONTEXT_IDENTIFIERS>(m_securityContextId), true, maxElements))
        {
            return false; //failure
        }
        serialization += tmpNumBytesTakenToDecode64;
        bufferSize -= tmpNumBytesTakenToDecode64;
    }

    if (!DeserializeIdValuePairsVecBpv7(serialization, tmpNumBytesTakenToDecode64, bufferSize, m_securityResults,
        static_cast<BPSEC_SECURITY_CONTEXT_IDENTIFIERS>(m_securityContextId), false, maxElements))
    {
        return false; //failure
    }
    serialization += tmpNumBytesTakenToDecode64;
    bufferSize -= tmpNumBytesTakenToDecode64;

    return (bufferSize == 0);
}

//static helpers
//This field SHALL be represented by a CBOR array. Each entry in this array is also a CBOR array
//comprising a 2-tuple of the id and value, as follows.
//    Id. This field SHALL be represented as a CBOR unsigned integer.
//    Value. This field SHALL be represented by the applicable CBOR representation (treate as already raw cbor serialization here)
uint64_t Bpv7AbstractSecurityBlock::SerializeIdValuePairsVecBpv7(uint8_t * serialization, const id_value_pairs_vec_t & idValuePairsVec, uint64_t bufferSize) {
    uint8_t * const serializationBase = serialization;
    uint64_t thisSerializationSize;
    uint8_t * const arrayHeaderStartPtr = serialization;
    thisSerializationSize = CborEncodeU64(serialization, idValuePairsVec.size(), bufferSize);
    serialization += thisSerializationSize;
    bufferSize -= thisSerializationSize;
    *arrayHeaderStartPtr |= (4U << 5); //change from major type 0 (unsigned integer) to major type 4 (array)

    for (std::size_t i = 0; i < idValuePairsVec.size(); ++i) {
        const id_value_pair_t & idValuePair = idValuePairsVec[i];
        if (bufferSize == 0) {
            return 0;
        }
        *serialization++ = (4U << 5) | 2; //major type 4, additional information 2 (add cbor array of size 2)
        --bufferSize;

        thisSerializationSize = CborEncodeU64(serialization, idValuePair.first, bufferSize); //id
        serialization += thisSerializationSize;
        bufferSize -= thisSerializationSize;

        thisSerializationSize = idValuePair.second->SerializeBpv7(serialization, bufferSize); //value
        serialization += thisSerializationSize;
        bufferSize -= thisSerializationSize;
    }
    return serialization - serializationBase;
}
uint64_t Bpv7AbstractSecurityBlock::IdValuePairsVecBpv7SerializationSize(const id_value_pairs_vec_t & idValuePairsVec) {
    uint64_t serializationSize = CborGetEncodingSizeU64(idValuePairsVec.size());
    serializationSize += idValuePairsVec.size(); //for major type 4, additional information 2 (cbor array of size 2) headers within the following loop
    for (std::size_t i = 0; i < idValuePairsVec.size(); ++i) {
        const id_value_pair_t & idValuePair = idValuePairsVec[i];
        serializationSize += CborGetEncodingSizeU64(idValuePair.first);
        serializationSize += idValuePair.second->GetSerializationSize(); //value
    }
    return serializationSize;
}
bool Bpv7AbstractSecurityBlock::DeserializeIdValuePairsVecBpv7(uint8_t * serialization, uint64_t & numBytesTakenToDecode, uint64_t bufferSize,
    id_value_pairs_vec_t & idValuePairsVec, const BPSEC_SECURITY_CONTEXT_IDENTIFIERS securityContext, const bool isForSecurityParameters, const uint64_t maxElements) {
    const uint8_t * const serializationBase = serialization;

    if (bufferSize == 0) {
        return false;
    }
    const uint8_t initialCborByte = *serialization; //buffer size verified above
    if (initialCborByte == ((4U << 5) | 31U)) { //major type 4, additional information 31 (Indefinite-Length Array)
        //An implementation of the Bundle Protocol MAY accept a sequence of
        //bytes that does not conform to the Bundle Protocol specification
        //(e.g., one that represents data elements in fixed-length arrays
        //rather than indefinite-length arrays) and transform it into
        //conformant BP structure before processing it.
        ++serialization;
        --bufferSize;
        idValuePairsVec.resize(0);
        idValuePairsVec.reserve(maxElements);
        for (std::size_t i = 0; i < maxElements; ++i) {
            idValuePairsVec.emplace_back();
            id_value_pair_t & idValuePair = idValuePairsVec.back();
            uint64_t tmpNumBytesTakenToDecode64;
            if (!DeserializeIdValuePairBpv7(serialization, tmpNumBytesTakenToDecode64, bufferSize, idValuePair, securityContext, isForSecurityParameters)) {
                return false; //failure
            }
            serialization += tmpNumBytesTakenToDecode64;
            bufferSize -= tmpNumBytesTakenToDecode64;
        }
        if (bufferSize == 0) {
            return false;
        }
        const uint8_t breakStopCode = *serialization++;
        if (breakStopCode != 0xff) {
            return false;
        }
    }
    else {
        uint8_t * const arrayHeaderStartPtr = serialization; //buffer size verified above
        const uint8_t cborMajorTypeArray = (*arrayHeaderStartPtr) >> 5;
        if (cborMajorTypeArray != 4) {
            return false; //failure
        }
        *arrayHeaderStartPtr &= 0x1f; //temporarily zero out major type to 0 to make it unsigned integer
        uint8_t cborUintSizeDecoded;
        const uint64_t numElements = CborDecodeU64(arrayHeaderStartPtr, &cborUintSizeDecoded, bufferSize);
        *arrayHeaderStartPtr |= (4U << 5); // restore to major type to 4 (change from major type 0 (unsigned integer) to major type 4 (array))
        if (cborUintSizeDecoded == 0) {
            return false; //failure
        }
        if (numElements > maxElements) {
            return false; //failure
        }
        serialization += cborUintSizeDecoded;
        bufferSize -= cborUintSizeDecoded;

        idValuePairsVec.resize(numElements);
        for (std::size_t i = 0; i < numElements; ++i) {
            id_value_pair_t & idValuePair = idValuePairsVec[i];
            uint64_t tmpNumBytesTakenToDecode64;
            if (!DeserializeIdValuePairBpv7(serialization, tmpNumBytesTakenToDecode64, bufferSize, idValuePair, securityContext, isForSecurityParameters)) {
                return false; //failure
            }
            serialization += tmpNumBytesTakenToDecode64;
            bufferSize -= tmpNumBytesTakenToDecode64;
        }
    }

    numBytesTakenToDecode = (serialization - serializationBase);
    return true;
}

bool Bpv7AbstractSecurityBlock::DeserializeIdValuePairBpv7(uint8_t * serialization, uint64_t & numBytesTakenToDecode, uint64_t bufferSize,
    id_value_pair_t & idValuePair, const BPSEC_SECURITY_CONTEXT_IDENTIFIERS securityContext, const bool isForSecurityParameters) {
    uint8_t cborUintSize;
    const uint8_t * const serializationBase = serialization;

    if (bufferSize == 0) {
        return false;
    }
    --bufferSize;
    const uint8_t initialCborByte = *serialization++;
    if ((initialCborByte != ((4U << 5) | 2U)) && //major type 4, additional information 2 (array of length 2)
        (initialCborByte != ((4U << 5) | 31U))) { //major type 4, additional information 31 (Indefinite-Length Array)
        return false;
    }

    idValuePair.first = CborDecodeU64(serialization, &cborUintSize, bufferSize);
    if (cborUintSize == 0) {
        return false; //failure
    }
    serialization += cborUintSize;
    bufferSize -= cborUintSize;

    uint64_t tmpNumBytesTakenToDecode64;
    if (isForSecurityParameters) {
        switch (securityContext) {
            case BPSEC_SECURITY_CONTEXT_IDENTIFIERS::BIB_HMAC_SHA2:
                switch (static_cast<BPSEC_BIB_HMAX_SHA2_SECURITY_PARAMETERS>(idValuePair.first)) {
                    case BPSEC_BIB_HMAX_SHA2_SECURITY_PARAMETERS::SHA_VARIANT:
                    case BPSEC_BIB_HMAX_SHA2_SECURITY_PARAMETERS::INTEGRITY_SCOPE_FLAGS:
                        idValuePair.second = boost::make_unique<Bpv7AbstractSecurityBlockValueUint>();
                        break;
                    case BPSEC_BIB_HMAX_SHA2_SECURITY_PARAMETERS::WRAPPED_KEY:
                        idValuePair.second = boost::make_unique<Bpv7AbstractSecurityBlockValueByteString>();
                        break;
                    default:
                        return false;
                }
                break;
            case BPSEC_SECURITY_CONTEXT_IDENTIFIERS::BCB_AES_GCM:
                switch (static_cast<BPSEC_BCB_AES_GCM_AAD_SECURITY_PARAMETERS>(idValuePair.first)) {
                    case BPSEC_BCB_AES_GCM_AAD_SECURITY_PARAMETERS::AES_VARIANT:
                    case BPSEC_BCB_AES_GCM_AAD_SECURITY_PARAMETERS::AAD_SCOPE_FLAGS:
                        idValuePair.second = boost::make_unique<Bpv7AbstractSecurityBlockValueUint>();
                        break;
                    case BPSEC_BCB_AES_GCM_AAD_SECURITY_PARAMETERS::INITIALIZATION_VECTOR:
                    case BPSEC_BCB_AES_GCM_AAD_SECURITY_PARAMETERS::WRAPPED_KEY:
                        idValuePair.second = boost::make_unique<Bpv7AbstractSecurityBlockValueByteString>();
                        break;
                    default:
                        return false;
                }
                break;
            default:
                return false;
        }
    }
    else {
        switch (securityContext) {
            case BPSEC_SECURITY_CONTEXT_IDENTIFIERS::BIB_HMAC_SHA2:
                switch (static_cast<BPSEC_BIB_HMAX_SHA2_SECURITY_RESULTS>(idValuePair.first)) {
                    case BPSEC_BIB_HMAX_SHA2_SECURITY_RESULTS::EXPECTED_HMAC:
                        idValuePair.second = boost::make_unique<Bpv7AbstractSecurityBlockValueByteString>();
                        break;
                    default:
                        return false;
                }
                break;
            case BPSEC_SECURITY_CONTEXT_IDENTIFIERS::BCB_AES_GCM:
                switch (static_cast<BPSEC_BCB_AES_GCM_AAD_SECURITY_RESULTS>(idValuePair.first)) {
                    case BPSEC_BCB_AES_GCM_AAD_SECURITY_RESULTS::AUTHENTICATION_TAG:
                        idValuePair.second = boost::make_unique<Bpv7AbstractSecurityBlockValueByteString>();
                        break;
                    default:
                        return false;
                }
                break;
            default:
                return false;
        }
    }
    if(!idValuePair.second->DeserializeBpv7(serialization, tmpNumBytesTakenToDecode64, bufferSize)) {
        return false; //failure
    }
    serialization += tmpNumBytesTakenToDecode64;

    //An implementation of the Bundle Protocol MAY accept a sequence of
    //bytes that does not conform to the Bundle Protocol specification
    //(e.g., one that represents data elements in fixed-length arrays
    //rather than indefinite-length arrays) and transform it into
    //conformant BP structure before processing it.
    if (initialCborByte == ((4U << 5) | 31U)) { //major type 4, additional information 31 (Indefinite-Length Array)
        bufferSize -= tmpNumBytesTakenToDecode64; //from value above
        if (bufferSize == 0) {
            return false;
        }
        const uint8_t breakStopCode = *serialization++;
        if (breakStopCode != 0xff) {
            return false;
        }
    }

    numBytesTakenToDecode = static_cast<uint8_t>(serialization - serializationBase);
    return true;
}
bool Bpv7AbstractSecurityBlock::IsEqual(const id_value_pairs_vec_t & pVec1, const id_value_pairs_vec_t & pVec2) {
    if (pVec1.size() != pVec2.size()) {
        return false;
    }
    for (std::size_t i = 0; i < pVec1.size(); ++i) {
        const id_value_pair_t & p1 = pVec1[i];
        const id_value_pair_t & p2 = pVec2[i];
        if (p1.first != p2.first) {
            return false;
        }
        if ((p1.second) && (p2.second)) { //both not null
            if (!p1.second->IsEqual(p2.second.get())) {
                return false;
            }
        }
        else if ((!p1.second) && (!p2.second)) { //both null
            continue;
        }
        else {
            return false;
        }
    }
    return true;
}
void Bpv7AbstractSecurityBlock::SetSecurityContextId(BPSEC_SECURITY_CONTEXT_IDENTIFIERS id) {
    m_securityContextId = static_cast<security_context_id_t>(id);
}
std::vector<uint8_t> * Bpv7AbstractSecurityBlock::Protected_AppendAndGetSecurityResultByteStringPtr(uint64_t resultType) {
    //add it
    m_securityResults.emplace_back();
    m_securityResults.back().first = resultType;
    std::unique_ptr<Bpv7AbstractSecurityBlockValueByteString> v = boost::make_unique<Bpv7AbstractSecurityBlockValueByteString>();
    std::vector<uint8_t> * retVal = &(v->m_byteString);
    m_securityResults.back().second = std::move(v);
    return retVal;
}
std::vector<std::vector<uint8_t>*> Bpv7AbstractSecurityBlock::Protected_GetAllSecurityResultsByteStringPtrs(uint64_t resultType) {
    std::vector<std::vector<uint8_t>*> ptrs;
    for (std::size_t i = 0; i < m_securityResults.size(); ++i) {
        security_result_t & res = m_securityResults[i];
        if (res.first == resultType) {
            if (Bpv7AbstractSecurityBlockValueByteString * valueByteString = dynamic_cast<Bpv7AbstractSecurityBlockValueByteString*>(res.second.get())) {
                ptrs.push_back(&(valueByteString->m_byteString));
            }
        }
    }
    return ptrs;
}

/////////////////////////////////////////
// BLOCK INTEGRITY BLOCK
/////////////////////////////////////////

Bpv7BlockIntegrityBlock::Bpv7BlockIntegrityBlock() : Bpv7AbstractSecurityBlock() { //a default constructor: X() //don't initialize anything for efficiency, use SetZero if required
    m_blockTypeCode = BPV7_BLOCK_TYPE_CODE::INTEGRITY;
    SetSecurityContextId(BPSEC_SECURITY_CONTEXT_IDENTIFIERS::BIB_HMAC_SHA2);
}
Bpv7BlockIntegrityBlock::~Bpv7BlockIntegrityBlock() { } //a destructor: ~X()
Bpv7BlockIntegrityBlock::Bpv7BlockIntegrityBlock(Bpv7BlockIntegrityBlock&& o) :
    Bpv7AbstractSecurityBlock(std::move(o)) { } //a move constructor: X(X&&)
Bpv7BlockIntegrityBlock& Bpv7BlockIntegrityBlock::operator=(Bpv7BlockIntegrityBlock && o) { //a move assignment: operator=(X&&)
    return static_cast<Bpv7BlockIntegrityBlock&>(Bpv7AbstractSecurityBlock::operator=(std::move(o)));
}
bool Bpv7BlockIntegrityBlock::operator==(const Bpv7BlockIntegrityBlock & o) const {
    return Bpv7AbstractSecurityBlock::operator==(o);
}
bool Bpv7BlockIntegrityBlock::operator!=(const Bpv7BlockIntegrityBlock & o) const {
    return !(*this == o);
}
void Bpv7BlockIntegrityBlock::SetZero() {
    Bpv7AbstractSecurityBlock::SetZero();
    m_blockTypeCode = BPV7_BLOCK_TYPE_CODE::INTEGRITY;
}
bool Bpv7BlockIntegrityBlock::AddOrUpdateSecurityParameterShaVariant(COSE_ALGORITHMS alg) {
    for (std::size_t i = 0; i < m_securityContextParametersOptional.size(); ++i) {
        security_context_parameter_t & param = m_securityContextParametersOptional[i];
        if (static_cast<BPSEC_BIB_HMAX_SHA2_SECURITY_PARAMETERS>(param.first) == BPSEC_BIB_HMAX_SHA2_SECURITY_PARAMETERS::SHA_VARIANT) {
            if (Bpv7AbstractSecurityBlockValueUint * valueUint = dynamic_cast<Bpv7AbstractSecurityBlockValueUint*>(param.second.get())) {
                valueUint->m_uintValue = static_cast<uint64_t>(alg);
                return true;
            }
            else {
                return false;
            }
        }
    }
    //doesn't exist, add it
    m_securityContextParametersOptional.emplace_back();
    m_securityContextParametersOptional.back().first = static_cast<uint64_t>(BPSEC_BIB_HMAX_SHA2_SECURITY_PARAMETERS::SHA_VARIANT);
    std::unique_ptr<Bpv7AbstractSecurityBlockValueUint> v = boost::make_unique<Bpv7AbstractSecurityBlockValueUint>();
    v->m_uintValue = static_cast<uint64_t>(alg);
    m_securityContextParametersOptional.back().second = std::move(v);
    return true;
}
COSE_ALGORITHMS Bpv7BlockIntegrityBlock::GetSecurityParameterShaVariant(bool & success) const {
    for (std::size_t i = 0; i < m_securityContextParametersOptional.size(); ++i) {
        const security_context_parameter_t & param = m_securityContextParametersOptional[i];
        if (static_cast<BPSEC_BIB_HMAX_SHA2_SECURITY_PARAMETERS>(param.first) == BPSEC_BIB_HMAX_SHA2_SECURITY_PARAMETERS::SHA_VARIANT) {
            if (Bpv7AbstractSecurityBlockValueUint * valueUint = dynamic_cast<Bpv7AbstractSecurityBlockValueUint*>(param.second.get())) {
                success = true;
                return static_cast<COSE_ALGORITHMS>(valueUint->m_uintValue);
            }
            else {
                success = false;
                return static_cast<COSE_ALGORITHMS>(0);
            }
        }
    }
    success = false;
    return static_cast<COSE_ALGORITHMS>(0);
}
bool Bpv7BlockIntegrityBlock::AddSecurityParameterIntegrityScope(BPSEC_BIB_HMAX_SHA2_INTEGRITY_SCOPE_MASKS integrityScope) {
    for (std::size_t i = 0; i < m_securityContextParametersOptional.size(); ++i) {
        security_context_parameter_t & param = m_securityContextParametersOptional[i];
        if (static_cast<BPSEC_BIB_HMAX_SHA2_SECURITY_PARAMETERS>(param.first) == BPSEC_BIB_HMAX_SHA2_SECURITY_PARAMETERS::INTEGRITY_SCOPE_FLAGS) {
            if (Bpv7AbstractSecurityBlockValueUint * valueUint = dynamic_cast<Bpv7AbstractSecurityBlockValueUint*>(param.second.get())) {
                valueUint->m_uintValue |= static_cast<uint64_t>(integrityScope);
                return true;
            }
            else {
                return false;
            }
        }
    }
    //doesn't exist, add it
    m_securityContextParametersOptional.emplace_back();
    m_securityContextParametersOptional.back().first = static_cast<uint64_t>(BPSEC_BIB_HMAX_SHA2_SECURITY_PARAMETERS::INTEGRITY_SCOPE_FLAGS);
    std::unique_ptr<Bpv7AbstractSecurityBlockValueUint> v = boost::make_unique<Bpv7AbstractSecurityBlockValueUint>();
    v->m_uintValue = static_cast<uint64_t>(integrityScope);
    m_securityContextParametersOptional.back().second = std::move(v);
    return true;
}
bool Bpv7BlockIntegrityBlock::IsSecurityParameterIntegrityScopePresentAndSet(BPSEC_BIB_HMAX_SHA2_INTEGRITY_SCOPE_MASKS integrityScope) const {
    for (std::size_t i = 0; i < m_securityContextParametersOptional.size(); ++i) {
        const security_context_parameter_t & param = m_securityContextParametersOptional[i];
        if (static_cast<BPSEC_BIB_HMAX_SHA2_SECURITY_PARAMETERS>(param.first) == BPSEC_BIB_HMAX_SHA2_SECURITY_PARAMETERS::INTEGRITY_SCOPE_FLAGS) {
            if (const Bpv7AbstractSecurityBlockValueUint * valueUint = dynamic_cast<const Bpv7AbstractSecurityBlockValueUint*>(param.second.get())) {
                return (valueUint->m_uintValue & static_cast<uint64_t>(integrityScope)) == static_cast<uint64_t>(integrityScope);
            }
            else {
                return false;
            }
        }
    }
    return false;
}
std::vector<uint8_t> * Bpv7BlockIntegrityBlock::AddAndGetWrappedKeyPtr() {
    for (std::size_t i = 0; i < m_securityContextParametersOptional.size(); ++i) {
        security_context_parameter_t & param = m_securityContextParametersOptional[i];
        if (static_cast<BPSEC_BIB_HMAX_SHA2_SECURITY_PARAMETERS>(param.first) == BPSEC_BIB_HMAX_SHA2_SECURITY_PARAMETERS::WRAPPED_KEY) {
            if (Bpv7AbstractSecurityBlockValueByteString * valueByteString = dynamic_cast<Bpv7AbstractSecurityBlockValueByteString*>(param.second.get())) {
                return &(valueByteString->m_byteString);
            }
            else {
                return NULL;
            }
        }
    }
    //doesn't exist, add it
    m_securityContextParametersOptional.emplace_back();
    m_securityContextParametersOptional.back().first = static_cast<uint64_t>(BPSEC_BIB_HMAX_SHA2_SECURITY_PARAMETERS::WRAPPED_KEY);
    std::unique_ptr<Bpv7AbstractSecurityBlockValueByteString> v = boost::make_unique<Bpv7AbstractSecurityBlockValueByteString>();
    std::vector<uint8_t> * retVal = &(v->m_byteString);
    m_securityContextParametersOptional.back().second = std::move(v);
    return retVal;
}
std::vector<uint8_t> * Bpv7BlockIntegrityBlock::AppendAndGetExpectedHmacPtr() {
    return Protected_AppendAndGetSecurityResultByteStringPtr(static_cast<uint64_t>(BPSEC_BIB_HMAX_SHA2_SECURITY_RESULTS::EXPECTED_HMAC));
}
std::vector<std::vector<uint8_t>*> Bpv7BlockIntegrityBlock::GetAllExpectedHmacPtrs() {
    return Protected_GetAllSecurityResultsByteStringPtrs(static_cast<uint64_t>(BPSEC_BIB_HMAX_SHA2_SECURITY_RESULTS::EXPECTED_HMAC));
}

/////////////////////////////////////////
// BLOCK CONFIDENTIALITY BLOCK
/////////////////////////////////////////

Bpv7BlockConfidentialityBlock::Bpv7BlockConfidentialityBlock() : Bpv7AbstractSecurityBlock() { //a default constructor: X() //don't initialize anything for efficiency, use SetZero if required
    m_blockTypeCode = BPV7_BLOCK_TYPE_CODE::CONFIDENTIALITY;
    SetSecurityContextId(BPSEC_SECURITY_CONTEXT_IDENTIFIERS::BCB_AES_GCM);
}
Bpv7BlockConfidentialityBlock::~Bpv7BlockConfidentialityBlock() { } //a destructor: ~X()
Bpv7BlockConfidentialityBlock::Bpv7BlockConfidentialityBlock(Bpv7BlockConfidentialityBlock&& o) :
    Bpv7AbstractSecurityBlock(std::move(o)) { } //a move constructor: X(X&&)
Bpv7BlockConfidentialityBlock& Bpv7BlockConfidentialityBlock::operator=(Bpv7BlockConfidentialityBlock && o) { //a move assignment: operator=(X&&)
    return static_cast<Bpv7BlockConfidentialityBlock&>(Bpv7AbstractSecurityBlock::operator=(std::move(o)));
}
bool Bpv7BlockConfidentialityBlock::operator==(const Bpv7BlockConfidentialityBlock & o) const {
    return Bpv7AbstractSecurityBlock::operator==(o);
}
bool Bpv7BlockConfidentialityBlock::operator!=(const Bpv7BlockConfidentialityBlock & o) const {
    return !(*this == o);
}
void Bpv7BlockConfidentialityBlock::SetZero() {
    Bpv7AbstractSecurityBlock::SetZero();
    m_blockTypeCode = BPV7_BLOCK_TYPE_CODE::CONFIDENTIALITY;
}

bool Bpv7BlockConfidentialityBlock::AddOrUpdateSecurityParameterAesVariant(COSE_ALGORITHMS alg) {
    for (std::size_t i = 0; i < m_securityContextParametersOptional.size(); ++i) {
        security_context_parameter_t & param = m_securityContextParametersOptional[i];
        if (static_cast<BPSEC_BCB_AES_GCM_AAD_SECURITY_PARAMETERS>(param.first) == BPSEC_BCB_AES_GCM_AAD_SECURITY_PARAMETERS::AES_VARIANT) {
            if (Bpv7AbstractSecurityBlockValueUint * valueUint = dynamic_cast<Bpv7AbstractSecurityBlockValueUint*>(param.second.get())) {
                valueUint->m_uintValue = static_cast<uint64_t>(alg);
                return true;
            }
            else {
                return false;
            }
        }
    }
    //doesn't exist, add it
    m_securityContextParametersOptional.emplace_back();
    m_securityContextParametersOptional.back().first = static_cast<uint64_t>(BPSEC_BCB_AES_GCM_AAD_SECURITY_PARAMETERS::AES_VARIANT);
    std::unique_ptr<Bpv7AbstractSecurityBlockValueUint> v = boost::make_unique<Bpv7AbstractSecurityBlockValueUint>();
    v->m_uintValue = static_cast<uint64_t>(alg);
    m_securityContextParametersOptional.back().second = std::move(v);
    return true;
}
COSE_ALGORITHMS Bpv7BlockConfidentialityBlock::GetSecurityParameterAesVariant(bool & success) const {
    for (std::size_t i = 0; i < m_securityContextParametersOptional.size(); ++i) {
        const security_context_parameter_t & param = m_securityContextParametersOptional[i];
        if (static_cast<BPSEC_BCB_AES_GCM_AAD_SECURITY_PARAMETERS>(param.first) == BPSEC_BCB_AES_GCM_AAD_SECURITY_PARAMETERS::AES_VARIANT) {
            if (Bpv7AbstractSecurityBlockValueUint * valueUint = dynamic_cast<Bpv7AbstractSecurityBlockValueUint*>(param.second.get())) {
                success = true;
                return static_cast<COSE_ALGORITHMS>(valueUint->m_uintValue);
            }
            else {
                success = false;
                return static_cast<COSE_ALGORITHMS>(0);
            }
        }
    }
    success = false;
    return static_cast<COSE_ALGORITHMS>(0);
}
bool Bpv7BlockConfidentialityBlock::AddSecurityParameterScope(BPSEC_BCB_AES_GCM_AAD_SCOPE_MASKS scope) {
    for (std::size_t i = 0; i < m_securityContextParametersOptional.size(); ++i) {
        security_context_parameter_t & param = m_securityContextParametersOptional[i];
        if (static_cast<BPSEC_BCB_AES_GCM_AAD_SECURITY_PARAMETERS>(param.first) == BPSEC_BCB_AES_GCM_AAD_SECURITY_PARAMETERS::AAD_SCOPE_FLAGS) {
            if (Bpv7AbstractSecurityBlockValueUint * valueUint = dynamic_cast<Bpv7AbstractSecurityBlockValueUint*>(param.second.get())) {
                valueUint->m_uintValue |= static_cast<uint64_t>(scope);
                return true;
            }
            else {
                return false;
            }
        }
    }
    //doesn't exist, add it
    m_securityContextParametersOptional.emplace_back();
    m_securityContextParametersOptional.back().first = static_cast<uint64_t>(BPSEC_BCB_AES_GCM_AAD_SECURITY_PARAMETERS::AAD_SCOPE_FLAGS);
    std::unique_ptr<Bpv7AbstractSecurityBlockValueUint> v = boost::make_unique<Bpv7AbstractSecurityBlockValueUint>();
    v->m_uintValue = static_cast<uint64_t>(scope);
    m_securityContextParametersOptional.back().second = std::move(v);
    return true;
}
bool Bpv7BlockConfidentialityBlock::IsSecurityParameterScopePresentAndSet(BPSEC_BCB_AES_GCM_AAD_SCOPE_MASKS scope) const {
    for (std::size_t i = 0; i < m_securityContextParametersOptional.size(); ++i) {
        const security_context_parameter_t & param = m_securityContextParametersOptional[i];
        if (static_cast<BPSEC_BCB_AES_GCM_AAD_SECURITY_PARAMETERS>(param.first) == BPSEC_BCB_AES_GCM_AAD_SECURITY_PARAMETERS::AAD_SCOPE_FLAGS) {
            if (const Bpv7AbstractSecurityBlockValueUint * valueUint = dynamic_cast<const Bpv7AbstractSecurityBlockValueUint*>(param.second.get())) {
                return (valueUint->m_uintValue & static_cast<uint64_t>(scope)) == static_cast<uint64_t>(scope);
            }
            else {
                return false;
            }
        }
    }
    return false;
}
std::vector<uint8_t> * Bpv7BlockConfidentialityBlock::AddAndGetAesWrappedKeyPtr() {
    return Private_AddAndGetByteStringParamPtr(BPSEC_BCB_AES_GCM_AAD_SECURITY_PARAMETERS::WRAPPED_KEY);
}
std::vector<uint8_t> * Bpv7BlockConfidentialityBlock::Private_AddAndGetByteStringParamPtr(BPSEC_BCB_AES_GCM_AAD_SECURITY_PARAMETERS parameter) {
    for (std::size_t i = 0; i < m_securityContextParametersOptional.size(); ++i) {
        security_context_parameter_t & param = m_securityContextParametersOptional[i];
        if (static_cast<BPSEC_BCB_AES_GCM_AAD_SECURITY_PARAMETERS>(param.first) == parameter) {
            if (Bpv7AbstractSecurityBlockValueByteString * valueByteString = dynamic_cast<Bpv7AbstractSecurityBlockValueByteString*>(param.second.get())) {
                return &(valueByteString->m_byteString);
            }
            else {
                return NULL;
            }
        }
    }
    //doesn't exist, add it
    m_securityContextParametersOptional.emplace_back();
    m_securityContextParametersOptional.back().first = static_cast<uint64_t>(parameter);
    std::unique_ptr<Bpv7AbstractSecurityBlockValueByteString> v = boost::make_unique<Bpv7AbstractSecurityBlockValueByteString>();
    std::vector<uint8_t> * retVal = &(v->m_byteString);
    m_securityContextParametersOptional.back().second = std::move(v);
    return retVal;
}
std::vector<uint8_t> * Bpv7BlockConfidentialityBlock::AddAndGetInitializationVectorPtr() {
    return Private_AddAndGetByteStringParamPtr(BPSEC_BCB_AES_GCM_AAD_SECURITY_PARAMETERS::INITIALIZATION_VECTOR);
}
std::vector<uint8_t> * Bpv7BlockConfidentialityBlock::AppendAndGetPayloadAuthenticationTagPtr() {
    return Protected_AppendAndGetSecurityResultByteStringPtr(static_cast<uint64_t>(BPSEC_BCB_AES_GCM_AAD_SECURITY_RESULTS::AUTHENTICATION_TAG));
}
std::vector<std::vector<uint8_t>*> Bpv7BlockConfidentialityBlock::GetAllPayloadAuthenticationTagPtrs() {
    return Protected_GetAllSecurityResultsByteStringPtrs(static_cast<uint64_t>(BPSEC_BCB_AES_GCM_AAD_SECURITY_RESULTS::AUTHENTICATION_TAG));
}

/////////////////////////////////////////
// VALUES FOR ABSTRACT SECURITY BLOCK
/////////////////////////////////////////
Bpv7AbstractSecurityBlockValueBase::~Bpv7AbstractSecurityBlockValueBase() {}

Bpv7AbstractSecurityBlockValueUint::~Bpv7AbstractSecurityBlockValueUint() {}
uint64_t Bpv7AbstractSecurityBlockValueUint::SerializeBpv7(uint8_t * serialization, uint64_t bufferSize) {
    return CborEncodeU64(serialization, m_uintValue, bufferSize);
}
uint64_t Bpv7AbstractSecurityBlockValueUint::GetSerializationSize() const {
    return CborGetEncodingSizeU64(m_uintValue);
}
bool Bpv7AbstractSecurityBlockValueUint::DeserializeBpv7(uint8_t * serialization, uint64_t & numBytesTakenToDecode, uint64_t bufferSize) {
    uint8_t cborUintSize;
    m_uintValue = CborDecodeU64(serialization, &cborUintSize, bufferSize);
    numBytesTakenToDecode = cborUintSize;
    return (numBytesTakenToDecode != 0);
}
bool Bpv7AbstractSecurityBlockValueUint::IsEqual(const Bpv7AbstractSecurityBlockValueBase * otherPtr) const {
    if (const Bpv7AbstractSecurityBlockValueUint * asUintPtr = dynamic_cast<const Bpv7AbstractSecurityBlockValueUint*>(otherPtr)) {
        return (asUintPtr->m_uintValue == m_uintValue);
    }
    else {
        return false;
    }
}

Bpv7AbstractSecurityBlockValueByteString::~Bpv7AbstractSecurityBlockValueByteString() {}
uint64_t Bpv7AbstractSecurityBlockValueByteString::SerializeBpv7(uint8_t * serialization, uint64_t bufferSize) {
    uint8_t * const byteStringHeaderStartPtr = serialization; //uint8_t * const serializationBase = serialization;
    uint64_t thisSerializationSize;
    thisSerializationSize = CborEncodeU64(serialization, m_byteString.size(), bufferSize);
    serialization += thisSerializationSize;
    bufferSize -= thisSerializationSize;
    *byteStringHeaderStartPtr |= (2U << 5); //change from major type 0 (unsigned integer) to major type 2 (byte string)

    if (bufferSize < m_byteString.size()) {
        return 0;
    }
    memcpy(serialization, m_byteString.data(), m_byteString.size());
    serialization += m_byteString.size(); //todo safety check on data length
    return serialization - byteStringHeaderStartPtr;// - serializationBase;
}
uint64_t Bpv7AbstractSecurityBlockValueByteString::GetSerializationSize() const {
    return CborGetEncodingSizeU64(m_byteString.size()) + m_byteString.size();
}
bool Bpv7AbstractSecurityBlockValueByteString::DeserializeBpv7(uint8_t * serialization, uint64_t & numBytesTakenToDecode, uint64_t bufferSize) {
    if (bufferSize == 0) { //for byteStringHeader
        return false;
    }
    uint8_t * const byteStringHeaderStartPtr = serialization; //buffer size verified above
    const uint8_t cborMajorTypeByteString = (*byteStringHeaderStartPtr) >> 5;
    if (cborMajorTypeByteString != 2) {
        return false; //failure
    }
    *byteStringHeaderStartPtr &= 0x1f; //temporarily zero out major type to 0 to make it unsigned integer
    uint8_t cborSizeDecoded;
    const uint64_t dataLength = CborDecodeU64(byteStringHeaderStartPtr, &cborSizeDecoded, bufferSize);
    *byteStringHeaderStartPtr |= (2U << 5); // restore to major type to 2 (change from major type 0 (unsigned integer) to major type 2 (byte string))
    if (cborSizeDecoded == 0) {
        return false; //failure
    }
    serialization += cborSizeDecoded;
    bufferSize -= cborSizeDecoded;

    if (dataLength > bufferSize) {
        return false;
    }
    m_byteString.assign(serialization, serialization + dataLength);
    serialization += dataLength;

    numBytesTakenToDecode = serialization - byteStringHeaderStartPtr;
    return true;
}
bool Bpv7AbstractSecurityBlockValueByteString::IsEqual(const Bpv7AbstractSecurityBlockValueBase * otherPtr) const {
    if (const Bpv7AbstractSecurityBlockValueByteString * asByteStringPtr = dynamic_cast<const Bpv7AbstractSecurityBlockValueByteString*>(otherPtr)) {
        return (asByteStringPtr->m_byteString == m_byteString);
    }
    else {
        return false;
    }
}


text:
#pragma once

#include <polymesh/pm.hh>
#include <typed-geometry/tg.hh>

#include <Eigen/Dense>

namespace LayoutEmbedding {

struct IGLMesh
{
    Eigen::MatrixXd V;
    Eigen::MatrixXi F;
};

IGLMesh to_igl_mesh(const pm::vertex_attribute<tg::pos3>& _pos);

void to_polymesh(const IGLMesh& _igl, pm::Mesh& _m, pm::vertex_attribute<tg::dpos3>& _pos);
void to_polymesh_param(const Eigen::MatrixXd& _W, const pm::Mesh& _m, pm::vertex_attribute<tg::dpos2>& _pos);

}

ext: hh

max_stars_repo_name: jsb/LayoutEmbedding

max_issues_repo_name: jsb/LayoutEmbedding

max_forks_repo_name: jsb/LayoutEmbedding


text:
// Copyright 2009 by BBN Technologies Corp.
// All Rights Reserved.

#include "Generic/common/leak_detection.h" // This must be the first #include

#include "Generic/common/limits.h"
#include "Generic/common/ParamReader.h"
#include "Generic/common/HeapChecker.h"
#include "Generic/common/UnrecoverableException.h"
#include "Generic/common/UTF8InputStream.h"
#include "Generic/common/UTF8OutputStream.h"
#include "Generic/common/UTF8Token.h"
#include "Generic/common/LocatedString.h"
#include "Generic/tokens/Tokenizer.h"
#include "Generic/theories/Sentence.h"
#include "Generic/theories/Document.h"
#include "Generic/theories/Region.h"
#include "Generic/sentences/SentenceBreaker.h"
#include "Generic/driver/SessionProgram.h"
#include "Generic/common/ConsoleSessionLogger.h"
#include <boost/scoped_ptr.hpp>

#if defined(WIN32) || defined(WIN64)
#include <direct.h>
#endif

using namespace std;

const int max_token_sequences = 1;

/* For the Cluster project, the limits.h was modified to the following values:
** The maximum number of sentences per document:
** #define MAX_DOCUMENT_SENTENCES 200000
** The maximum number of wchar_t chars per sentence:
** #define MAX_SENTENCE_CHARS 5000
** The maximum number of tokens per sentence:
** #define MAX_SENTENCE_TOKENS 5000
** The maximum number of wchar_t chars per token:
** #define MAX_TOKEN_SIZE 500
*/

int main(int argc, char **argv) {
	if ((argc != 4) && (argc != 5))  {
		cerr << "USAGE: StandaloneTokenizer.exe <param_file> <input_file> <output_file> <append_to_output_bit>\n";
		return -1;
	}
	try {
		const char *param_file = argv[1];
		const char *input_file = argv[2];
		const char *output_file = argv[3];
		bool append = false;
		if (argc == 5)
			append = (atoi(argv[4]) == 1);

		ParamReader::readParamFile(argv[1]);

		std::vector<std::wstring>  context_level_names;
		ConsoleSessionLogger logger(context_level_names, L"[StandaloneTokenizer]");
		SessionLogger::setGlobalLogger(&logger);

		SentenceBreaker * sentenceBreaker = SentenceBreaker::build();
		Sentence **sentences = _new Sentence*[MAX_DOCUMENT_SENTENCES];

		Tokenizer * _tokenizer = Tokenizer::build();
		TokenSequence ** _tokenSequenceBuf = _new TokenSequence*[max_token_sequences];

		// Read the input file
		SessionLogger::info("StandaloneTokenizer") << "Reading " << input_file << "...\n";
		boost::scoped_ptr<UTF8InputStream> uis_scoped_ptr(UTF8InputStream::build(input_file));
		UTF8InputStream& uis(*uis_scoped_ptr);
		if(uis.fail()){
			char msg[1000];
			strcpy(msg, "Couldn't open input document: ");
			strcat(msg, input_file);
			throw UnexpectedInputException(
			"StandaloneTokenizer::Main()",
			msg);
		}
		LocatedString *text = _new LocatedString(uis, true);
		Document * doc = _new Document(Symbol(L"foodoc"));
		Region * region = _new Region(doc, Symbol(L"fooregion"), 0, text);

		// Open the output file
		UTF8OutputStream out(output_file);

		SessionLogger::info("StandaloneTokenizer") << "Breaking into sentences...\n";
		sentenceBreaker->resetForNewDocument(doc);
		int n_sentences = sentenceBreaker->getSentences(sentences, MAX_DOCUMENT_SENTENCES, &region, 1);

		SessionLogger::info("StandaloneTokenizer") << "Tokenizing...\n";
		for (int j = 0; j < n_sentences; j++) {
			_tokenizer->resetForNewSentence(doc, sentences[j]->getSentNumber());
			int _n_token_sequences = _tokenizer->getTokenTheories(
				_tokenSequenceBuf, max_token_sequences,
				sentences[j]->getString());
			for (int k = 0; k < _tokenSequenceBuf[0]->getNTokens(); k++) {
				if (k != 0)
					out << L" ";
				out << _tokenSequenceBuf[0]->getToken(k)->getSymbol().to_string();
			}
			out << L"\n";
			delete _tokenSequenceBuf[0];
			delete sentences[j];
		}

		delete region;
		delete doc;
		delete text;

		uis.close();
		out.close();

		delete _tokenizer;
		delete _tokenSequenceBuf;

		delete sentences;
		delete sentenceBreaker;
	}
	catch (UnrecoverableException &e) {
		cerr << "\n" << e.getMessage() << "\n";
		HeapChecker::checkHeap("main(); About to exit due to error");

		return -1;
	}
#ifdef ENABLE_LEAK_DETECTION
		ParamReader::finalize();

		_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
		_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDOUT);
		_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
		_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT);
		_CrtDumpMemoryLeaks();
#endif

#if defined(_DEBUG) || defined(_UNOPTIMIZED)
		printf("Press enter to exit....\n");
		getchar();
#endif

	HeapChecker::checkHeap("main(); About to exit after successful run");
	return 0;
}

ext: cpp

max_stars_repo_name: BBN-E/serif

max_issues_repo_name: BBN-E/serif

max_forks_repo_name: BBN-E/serif


text:
/* Copyright © 2019 Apple Inc. All rights reserved.
 *
 * Use of this source code is governed by a BSD-3-clause license that can
 * be found in the LICENSE.txt file or at https://opensource.org/licenses/BSD-3-Clause
 */

#include <algorithm>
#include <cmath>
#include <limits>
#include <vector>
#include <random>

#include <boost/gil/gil_all.hpp>

#include <Eigen/Core>
#include <Eigen/Dense>

namespace turi {
namespace one_shot_object_detection {
namespace data_augmentation {

class Line {
public:
  /* Equation of Line from two points */
  Line(Eigen::Vector3f P1, Eigen::Vector3f P2);
  bool side_of_line(size_t x, size_t y);

private:
  float m_a, m_b, m_c; // ax + by + c = 0
};

/* Returns true if (x,y) is inside a quadrilateral
 * defined by corners, where corners are in cyclic
 * order from top right to bottom left.
 */
bool is_in_quadrilateral(size_t x, size_t y,
  const std::vector<Eigen::Vector3f> &corners);

/* mask is an image with all pixels set to black and
 * mask_complement is an image with all pixels set to white.
 * This function colors the pixels inside the convex quadrilateral defined by
 * corners with white for the mask and black for the mask_complement.
 */
void color_quadrilateral(const boost::gil::rgba8_image_t::view_t &transformed_view,
                         const std::vector<Eigen::Vector3f> &corners);

} // data_augmentation
} // one_shot_object_detection
} // turi

ext: hpp

max_stars_repo_name: jolinlaw/turicreate

max_issues_repo_name: jolinlaw/turicreate

max_forks_repo_name: jolinlaw/turicreate


text:
/*==============================================================================
    Copyright (c) 2005-2010 Joel de Guzman
    Copyright (c) 2011 Thomas Heller

    Distributed under the Boost Software License, Version 1.0. (See accompanying
    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
==============================================================================*/
#ifndef BOOST_PHOENIX_CORE_CALL_HPP
#define BOOST_PHOENIX_CORE_CALL_HPP

#include <boost/phoenix/core/environment.hpp>
#include <boost/phoenix/core/limits.hpp>
#include <boost/proto/proto_fwd.hpp>
#include <boost/proto/traits.hpp>
#include <boost/proto/transform/impl.hpp>

#ifndef BOOST_PHOENIX_NO_VARIADIC_CALL
#include <boost/phoenix/core/detail/index_sequence.hpp>
#endif

namespace boost
{
namespace phoenix
{
namespace detail
{
template <typename Fun, typename Expr, typename State, typename Data,
          long Arity = proto::arity_of<Expr>::value>
struct call_impl;

template <typename Fun, typename Expr, typename State, typename Data>
struct call_impl<Fun, Expr, State, Data, 0>
    : proto::transform_impl<Expr, State, Data>
{
    typedef typename boost::phoenix::result_of::context<State, Data>::type
        context_type;

    typedef
        typename boost::result_of<Fun(Expr, context_type)>::type result_type;

    result_type operator()(typename call_impl::expr_param e,
                           typename call_impl::state_param s,
                           typename call_impl::data_param d) const
    {
        return Fun()(e, boost::phoenix::context(s, d));
    }
};

#ifdef BOOST_PHOENIX_NO_VARIADIC_CALL
#include <boost/phoenix/core/detail/cpp03/call.hpp>
#else
template <typename Fun, typename Expr, typename State, typename Data,
          typename Indices>
struct call_impl_;

template <typename Fun, typename Expr, typename State, typename Data,
          std::size_t... Indices>
struct call_impl_<Fun, Expr, State, Data, index_sequence<Indices...>>
    : proto::transform_impl<Expr, State, Data>
{
    typedef typename boost::phoenix::result_of::context<State, Data>::type
        context_type;
    template <std::size_t Index>
    struct result_of_expr
    {
        typedef typename proto::result_of::child_c<Expr, Index>::type type;
    };
    typedef
        typename boost::result_of<Fun(typename result_of_expr<Indices>::type...,
                                      context_type)>::type result_type;
    result_type operator()(typename call_impl_::expr_param e,
                           typename call_impl_::state_param s,
                           typename call_impl_::data_param d) const
    {
        return Fun()(proto::child_c<Indices>(e)...,
                     boost::phoenix::context(s, d));
    }
};

template <typename Fun, typename Expr, typename State, typename Data,
          long Arity>
struct call_impl : call_impl_<Fun, Expr, State, Data,
                              typename make_index_sequence<Arity>::type>
{
};
#endif
} // namespace detail

template <typename Fun, typename Dummy = void>
struct call : proto::transform<call<Fun>>
{
    template <typename Expr, typename State, typename Data>
    struct impl : detail::call_impl<Fun, Expr, State, Data>
    {
    };
};
} // namespace phoenix

namespace proto
{
template <typename Fun, typename Dummy>
struct is_callable<phoenix::call<Fun, Dummy>> : mpl::true_
{
};
} // namespace proto
} // namespace boost

#endif

max_stars_repo_name: sotaoverride/backup

max_issues_repo_name: sotaoverride/backup

max_forks_repo_name: sotaoverride/backup


text:
/*=============================================================================
    Copyright (c) 1999-2003 Jaakko Jarvi
    Copyright (c) 2001-2011 Joel de Guzman

    Distributed under the Boost Software License, Version 1.0. (See accompanying
    file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
==============================================================================*/
#include <boost/detail/lightweight_test.hpp>
#include <boost/fusion/sequence/intrinsic/at.hpp>
#include <boost/fusion/mpl.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/mpl/insert_range.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/begin.hpp>
#include <boost/mpl/equal.hpp>
#include <boost/static_assert.hpp>

#if !defined(FUSION_AT)
#define FUSION_AT at_c
#endif

#if !defined(FUSION_MAKE)
#define FUSION_MAKE BOOST_PP_CAT(make_, FUSION_SEQUENCE)
#endif

#if !defined(FUSION_TIE)
#define FUSION_TIE BOOST_PP_CAT(FUSION_SEQUENCE, _tie)
#endif

namespace test_detail
{
    // classes with different kinds of conversions
    class AA {};
    class BB : public AA {};
    struct CC { CC() {} CC(const BB&) {} };
    struct DD { operator CC() const { return CC(); }; };
}

boost::fusion::FUSION_SEQUENCE<double, double, double, double>
foo(int i)
{
   return boost::fusion::FUSION_MAKE(i, i+1, i+2, i+3);
}

void test_mpl()
{
    using namespace boost::fusion;

    typedef FUSION_SEQUENCE<int, char> seq;

    typedef
        boost::mpl::insert_range<
            boost::mpl::vector<>
          , boost::mpl::end< boost::mpl::vector<> >::type
          , seq
        >::type
    sequence;

    typedef boost::mpl::equal<sequence, boost::mpl::vector<int, char> > equal;
    BOOST_STATIC_ASSERT(equal::value);
}

void
test()
{
    using namespace boost::fusion;
    using namespace test_detail;

    FUSION_SEQUENCE<int, char> t1(4, 'a');
    FUSION_SEQUENCE<int, char> t2(5, 'b');
    t2 = t1;
    BOOST_TEST(FUSION_AT<0>(t1) == FUSION_AT<0>(t2));
    BOOST_TEST(FUSION_AT<1>(t1) == FUSION_AT<1>(t2));

    FUSION_SEQUENCE<long, std::string> t3(2, "a");
    t3 = t1;
    BOOST_TEST((double)FUSION_AT<0>(t1) == FUSION_AT<0>(t3));
    BOOST_TEST(FUSION_AT<1>(t1) == FUSION_AT<1>(t3)[0]);

    // testing copy and assignment with implicit conversions
    // between elements testing tie

    FUSION_SEQUENCE<char, BB*, BB, DD> t;
    FUSION_SEQUENCE<int, AA*, CC, CC> a(t);
    a = t;

    int i; char c; double d;
    FUSION_TIE(i, c, d) = FUSION_MAKE(1, 'a', 5.5);

    BOOST_TEST(i==1);
    BOOST_TEST(c=='a');
    BOOST_TEST(d>5.4 && d<5.6);

    // returning a tuple with conversion
    foo(2);

    test_mpl();
}


text:
#ifdef WIN32
	#include <intrin.h>
	namespace {
		const char* c_DriveLetter = "C:";
	}
#else
	namespace {
		const char* c_DriveLetter = "/";
	}
#endif
#include "test.hpp"
#include "../path.hpp"
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include "../random/string.hpp"
namespace spn {
	namespace test {
		class PathTest : public RandomTestInitializer {};
		TEST_F(PathTest, Serialize) {
			// ランダムなパス生成
			auto rd = getRand();
			PathBlock pb = random::GenRPath(rd.getUniformF<int>(), {1,20}, {1,16}, c_DriveLetter);
			CheckSerializedDataBin(pb);
		}
		// 拡張子の抽出テスト
		TEST_F(PathTest, Extension) {
			// ランダムなパス生成
			auto rd = getRand();
			PathBlock pb = random::GenRPath(rd.getUniformF<int>(), {1,20}, {1,16}, c_DriveLetter);
			// 拡張子が無ければ空文字列が返る
			ASSERT_TRUE(pb.getExtension().empty());

			const std::string ext("the_extension"),
							ext2("another_extension");
			auto tmp = pb.getLast_utf8();
			tmp += ".";
			tmp += ext;
			pb.popBack();
			pb.pushBack(tmp);
			ASSERT_EQ(ext, pb.getExtension());

			pb.setExtension(ext2);
			ASSERT_EQ(ext2, pb.getExtension());
		}
		// ファイルパス末尾の数値に対するテスト
		TEST_F(PathTest, PathNumber) {
			// ランダムなパス生成
			auto rd = getRand();
			PathBlock pb = random::GenRPath(rd.getUniformF<int>(), {1,20}, {1,16}, c_DriveLetter);

			// 余計な拡張子を付加
			pb.setExtension("foo");

			// パスの末尾に数値が付加されていなければnoneが返る
			ASSERT_FALSE(pb.getPathNum());

			// パスの末尾に数値を付加
			pb.popBack();
			pb.pushBack("bar123.foo64");
			auto num = pb.getPathNum();
			ASSERT_TRUE(static_cast<bool>(num));
			ASSERT_EQ(123, *num);

			// 数値の改変テスト
			pb.setPathNum([](Int_OP n) -> Int_OP{
				if(n)
					return *n * 2;
				return spn::none;
			});
			num = pb.getPathNum();
			ASSERT_TRUE(static_cast<bool>(num));
			ASSERT_EQ(123*2, *num);

			// 数値の削除テスト
			pb.setPathNum([](auto)-> Int_OP{ return spn::none; });
			ASSERT_FALSE(pb.getPathNum());
		}
		// 拡張子末尾の数値に対するテスト
		TEST_F(PathTest, ExtNumber) {
			// ランダムなパス生成
			auto rd = getRand();
			PathBlock pb = random::GenRPath(rd.getUniformF<int>(), {1,20}, {1,16}, c_DriveLetter);

			// 拡張子が無ければnoneが返る
			ASSERT_FALSE(pb.getExtNum());

			// 拡張子の末尾に数値が無ければnoneが返る
			pb.setExtension("foo");
			ASSERT_FALSE(pb.getExtNum());

			// 指定した数値の取得テスト
			pb.setExtension("bar123");
			auto num = pb.getExtNum();
			ASSERT_TRUE(static_cast<bool>(num));
			ASSERT_EQ(123, *num);

			// 数値の改変テスト
			pb.setExtNum([](Int_OP n) -> Int_OP{
				if(n)
					return *n * 2;
				return spn::none;
			});
			num = pb.getExtNum();
			ASSERT_TRUE(static_cast<bool>(num));
			ASSERT_EQ(123*2, *num);

			// 数値の削除テスト
			pb.setExtNum([](auto)-> Int_OP{ return spn::none; });
			ASSERT_FALSE(pb.getExtNum());
		}
	}
}

max_stars_repo_name: degarashi/spinner

max_issues_repo_name: degarashi/spinner

max_forks_repo_name: degarashi/spinner


text:
#include <gunz/Channel.h>
#include <gunz/MUIDSanta.h>

#include <tinyxml/tinyxml.h>

#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>
#include <climits>
#include <cstddef>
#include <cstdio>
#include <cstring>
#include <string>

using namespace std;
using namespace boost;

namespace {
	struct ParseFailed {};
}

static char my_tolower(char c)
{
	return static_cast<char>(tolower(c));
}

static void on_error(const char* message)
{
	fprintf(stderr, "%s\n", message);
}

static gunz::Channel::Rule parse_rule(const char* text)
{
	std::string fixed(text);
	for_each(fixed.begin(), fixed.end(), my_tolower);

	if(fixed == "novice")
		return gunz::Channel::CR_NOVICE;
	else if(fixed == "newbie")
		return gunz::Channel::CR_NEWBIE;
	else if(fixed == "rookie")
		return gunz::Channel::CR_ROOKIE;
	else if(fixed == "mastery")
		return gunz::Channel::CR_MASTERY;
	else if(fixed == "elite")
		return gunz::Channel::CR_ELITE;
	else
	{
		on_error("Invalid channel rule. Skipping...");
		throw ParseFailed();
	}
}

// Converts a string to a byte. Throws ParseFailed on failure.
static uint8_t byte_cast(const char* str)
{
	uint16_t result;

	try {
		result = lexical_cast<uint16_t>(str);
	} catch(const bad_lexical_cast&) {
		throw ParseFailed();
	}

	if(result > UCHAR_MAX)
		throw ParseFailed();

	return static_cast<uint8_t>(result);
}

static uint8_t parse_maxPlayers(const char* text)
{
	try {
		return byte_cast(text);
	} catch(ParseFailed) {
		on_error("Attribute \"maxplayers\" must be in the range of 0-255. Skipping...");
		throw;
	}
}

static uint8_t parse_level(const char* text, const char* level)
{
	try {
		return byte_cast(text);
	} catch(ParseFailed) {
		on_error((format("Attribute \"%1%\" must be in the range of 0-255. Skipping...") % level).str().c_str());
		throw;
	}
}

// Retrives the channel list from an xml file's contents. An example of the
// grammar can be found in channel.xml
vector<gunz::Channel::Traits> parse_channel_list(const string& rawXML, gunz::MUIDSanta* santa)
{
	TiXmlDocument doc;
	doc.Parse(rawXML.c_str());

	vector<gunz::Channel::Traits> ret;

	TiXmlElement* root = doc.RootElement();

	const TiXmlNode* channelNode = NULL;
	while((channelNode = root->IterateChildren(channelNode)))
	{
		const TiXmlElement* channel = channelNode->ToElement();
		const char* name = channel->Attribute("name");
		const char* maxPlayers = channel->Attribute("maxplayers");
		const char* rule = channel->Attribute("rule");
		// Type is always CT_GENERAL at this point.
		const char* minLevel = channel->Attribute("minlevel");
		const char* maxLevel = channel->Attribute("maxlevel");

		if(name == NULL)
		{
			on_error("Channel missing attribute \"name\" found. Skipping...");
			continue;
		}

		if(maxPlayers == NULL)
		{
			on_error("Channel missing attribute \"maxplayers\" found. Skipping...");
			continue;
		}

		if(rule == NULL)
			rule = "novice";

		if(minLevel == NULL)
			minLevel = "1";
		if(maxLevel == NULL)
			maxLevel = "255";	// HACK: This assumes the max level is 255. If
			                    // that EVER changes, this line must be updated.

		try {
			uint8_t minimumLevel = parse_level(minLevel, "minlevel");
			uint8_t maximumLevel = parse_level(maxLevel, "maxlevel");

			if(minimumLevel > maximumLevel)
			{
				on_error("Minimum level is higher than maximum level. Skipping...");
				continue;
			}

			ret.push_back(gunz::Channel::Traits(
				santa->get(),
				name,
				parse_maxPlayers(maxPlayers),
				parse_rule(rule),
				gunz::Channel::CT_GENERAL,
				minimumLevel,
				maximumLevel
			));
		} catch(ParseFailed) {
			continue;
		}
	}

	return ret;
}

max_stars_repo_name: gogo-dev/GoGo

max_issues_repo_name: gogo-dev/GoGo

max_forks_repo_name: gogo-dev/GoGo


text:
// Copyright (C) 2013-2014 DNAnexus, Inc.
//
// This file is part of dx-toolkit (DNAnexus platform client libraries).
//
//   Licensed under the Apache License, Version 2.0 (the "License"); you may
//   not use this file except in compliance with the License. You may obtain a
//   copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
//   WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
//   License for the specific language governing permissions and limitations
//   under the License.

#include "dxgtable.h"
#include <boost/lexical_cast.hpp>
#include <boost/bind.hpp>

using namespace std;

namespace dx {
  void DXGTable::reset_buffer_() {
    row_buffer_.str("");
    row_buffer_ << "{\"data\": [";
  }

  void DXGTable::reset_data_processing_() {
    flush(); // flush will call reset_buffer_() as well
    stopLinearQuery();
    countThreadsWaitingOnConsume = 0;
    countThreadsNotWaitingOnConsume = 0;
  }

  void DXGTable::reset_config_variables_() {
    row_buffer_maxsize_ = DEFAULT_ROW_BUFFER_MAXSIZE;
    max_write_threads_ = DEFAULT_WRITE_THREADS;
    DXDataObject::setIDs("", "");
  }

  void DXGTable::copy_config_variables_(const DXGTable &to_copy) {
    row_buffer_maxsize_ = to_copy.row_buffer_maxsize_;
    max_write_threads_ = to_copy.max_write_threads_;
  }

  void DXGTable::reset_everything_() {
    reset_data_processing_();
    reset_config_variables_();
  }

  void DXGTable::setIDs(const string &dxid,
                        const string &proj) {
    reset_data_processing_(); // should never change ID during any data processing (addRows, getNextChunk)
    DXDataObject::setIDs(dxid, proj);
  }

  void DXGTable::setIDs(const char *dxid, const char *proj) {
    if (proj == NULL) {
      setIDs(string(dxid));
    } else {
      setIDs(string(dxid), string(proj));
    }
  }

  void DXGTable::setIDs(const JSON &dxlink) {
    reset_data_processing_(); // should never change ID during any data processing (addRows, getNextChunk)
    DXDataObject::setIDs(dxlink);
  }

  void DXGTable::create(const vector<JSON> &columns,
                        const vector<JSON> &indices,
                        const JSON &data_obj_fields) {
    JSON input_params = data_obj_fields;
    if (!data_obj_fields.has("project"))
      input_params["project"] = config::CURRENT_PROJECT();
    input_params["columns"] = columns;
    if (indices.size() > 0) {
      input_params["indices"] = indices;
    }

    const JSON resp = gtableNew(input_params);

    setIDs(resp["id"].get<string>(), input_params["project"].get<string>());
  }

  void DXGTable::create(const DXGTable &init_from,
                        const JSON &data_obj_fields) {
    JSON input_params = data_obj_fields;
    input_params["initializeFrom"] = JSON(JSON_OBJECT);
    input_params["initializeFrom"]["id"] = init_from.getID();
    input_params["initializeFrom"]["project"] = init_from.getProjectID();
    if (!data_obj_fields.has("project")) {
      input_params["project"] = config::CURRENT_PROJECT();
    }

    const JSON resp = gtableNew(input_params);

    setIDs(resp["id"].get<string>(), input_params["project"].get<string>());
  }

  void DXGTable::create(const DXGTable &init_from,
                        const vector<JSON> &columns,
                        const JSON &data_obj_fields) {
    JSON input_params = data_obj_fields;
    input_params["initializeFrom"] = JSON(JSON_OBJECT);
    input_params["initializeFrom"]["id"] = init_from.getID();
    input_params["initializeFrom"]["project"] = init_from.getProjectID();
    if (!data_obj_fields.has("project")) {
      input_params["project"] = config::CURRENT_PROJECT();
    }
    input_params["columns"] = columns;

    const JSON resp = gtableNew(input_params);

    setIDs(resp["id"].get<string>(), input_params["project"].get<string>());
  }

  void DXGTable::create(const DXGTable &init_from,
                        const vector<JSON> &columns,
                        const vector<JSON> &indices,
                        const JSON &data_obj_fields) {
    JSON input_params = data_obj_fields;
    input_params["initializeFrom"] = JSON(JSON_OBJECT);
    input_params["initializeFrom"]["id"] = init_from.getID();
    input_params["initializeFrom"]["project"] = init_from.getProjectID();
    if (!data_obj_fields.has("project")) {
      input_params["project"] = config::CURRENT_PROJECT();
    }
    if (columns.size() > 0) {
      input_params["columns"] = columns;
    }
    input_params["indices"] = indices;

    const JSON resp = gtableNew(input_params);

    setIDs(resp["id"].get<string>(), input_params["project"].get<string>());
  }

  JSON DXGTable::getRows(const JSON &query, const JSON &column_names,
                         const int64_t starting, const int64_t limit) const {
    JSON input_params(JSON_OBJECT);
    if (query.type() != JSON_NULL)
      input_params["query"] = query;
    if (column_names.type() == JSON_ARRAY)
      input_params["columns"] = column_names;
    if (starting >= 0)
      input_params["starting"] = starting;
    if (limit >= 0)
      input_params["limit"] = limit;

    return gtableGet(dxid_, input_params);
  }

  /////////////////////////////////////////////////////////////

  void DXGTable::startLinearQuery(const dx::JSON &column_names,
                        const int64_t start_row,
                        const int64_t num_rows,
                        const int64_t chunk_size,
                        const unsigned max_chunks,
                        const unsigned thread_count) const {
    stopLinearQuery(); // Stop any previously running linear query

    lq_columns_ = column_names;
    lq_query_start_ = (start_row == -1) ? 0 : start_row;
    lq_query_end_ = (num_rows == -1) ? describe()["length"].get<int64_t>() : lq_query_start_ + num_rows;
    lq_chunk_limit_ = chunk_size;
    lq_max_chunks_ = max_chunks;
    lq_next_result_ = lq_query_start_;
    lq_results_.clear();

    for (unsigned i = 0; i < thread_count; ++i)
      lq_readThreads_.push_back(boost::thread(boost::bind(&DXGTable::readChunk_, this)));
  }

  void DXGTable::readChunk_() const {
    int64_t start;
    while (true) {
      boost::mutex::scoped_lock qs_lock(lq_query_start_mutex_);
      if (lq_query_start_ >= lq_query_end_)
        break; // We are done fetching all chunks

      start = lq_query_start_;
      lq_query_start_ += lq_chunk_limit_;
      qs_lock.unlock();

      int64_t limit_for_req = std::min(lq_chunk_limit_, (lq_query_end_ - start));

      // Perform the actual query
      JSON ret = getRows(JSON(JSON_NULL), lq_columns_, start, limit_for_req);
      boost::mutex::scoped_lock r_lock(lq_results_mutex_);
      while (lq_next_result_ != start && lq_results_.size() >= lq_max_chunks_) {
        r_lock.unlock();
        boost::this_thread::sleep(boost::posix_time::milliseconds(1));
        r_lock.lock();
      }
      lq_results_[start] = ret["data"];
      r_lock.unlock();
      boost::this_thread::interruption_point();
    }
  }

  bool DXGTable::getNextChunk(JSON &chunk) const {
    if (lq_readThreads_.size() == 0) // Linear query was not called
      return false;

    boost::mutex::scoped_lock r_lock(lq_results_mutex_);
    if (lq_next_result_ >= lq_query_end_)
      return false;
    while (lq_results_.size() == 0 || (lq_results_.begin()->first != lq_next_result_)) {
      r_lock.unlock();
      usleep(100);
      r_lock.lock();
    }
    chunk = lq_results_.begin()->second;
    lq_results_.erase(lq_results_.begin());
    lq_next_result_ += chunk.size();
    r_lock.unlock();
    return true;
  }

  void DXGTable::stopLinearQuery() const {
    if (lq_readThreads_.size() == 0)
      return;
    for (unsigned i = 0; i < lq_readThreads_.size(); ++i) {
      lq_readThreads_[i].interrupt();
      lq_readThreads_[i].join();
    }
    lq_readThreads_.clear();
    lq_results_.clear();
  }

  ///////////////////////////////////////////////////////////////////////////

  void DXGTable::addRows(const JSON &data, int part_id) {
    JSON input_params(JSON_OBJECT);
    input_params["data"] = data;
    input_params["part"] = part_id;
    gtableAddRows(dxid_, input_params);
  }

  void DXGTable::finalizeRequestBuffer_() {
    /* This function "closes" the stringified JSON array we are keeping for
     * for buffering requests. It also creates the worker thread if not done previously
     */
    int64_t pos = row_buffer_.tellp();
    if (pos > 10) {
      row_buffer_.seekp(pos - 1); // Erase the trailing comma
      row_buffer_ << "], \"part\": " << getUnusedPartID() << "}";
    }
    // We need to create thread pool only once (i.e., if it doesn't exist already)
    if (writeThreads.size() == 0)
      createWriteThreads_();
  }

  void DXGTable::joinAllWriteThreads_() {
    /* This function ensures that all pending requests are executed and all
     * worker thread are closed after that
     * Brief notes about functioning:
     * --> addRowRequestsQueue.size() == 0, ensures that request queue is empty, i.e.,
     *     some worker has picked the last request (note we use term "pick", because
     *     the request might still be executing the request).
     * --> Once we know that request queue is empty, we issue interrupt() to all threads
     *     Note: interrupt() will only terminate threads, which are waiting on new request.
     *           So only threads which are blocked by .consume() operation will be terminated
     *           immediatly.
     * --> Now we use a condition based on two interleaved counters to wait until all the
     *     threads have finished the execution. (see writeChunk_() for understanding their usage)
     * --> Once we are sure that all threads have finished the requests, we join() them.
     *     Since interrupt() was already issued, thus join() terminates them instantly.
     *     Note: Most of them would have been already terminated (since after issuing
     *           interrupt(), they will be terminated when they start waiting on consume()).
     *           It's ok to join() terminated threads.
     * --> We clear the thread pool (vector), and reset the counters.
     */

    if (writeThreads.size() == 0) {
      // if no writeThreads are present, addRowRequestsQueue should be empty : sanity check
      assert(addRowRequestsQueue.size() == 0);
      return; // Nothing to do (no thread has been started)
    }

    // To avoid race condition
    // particularly the case when produce() has been called, but thread is still waiting on consume()
    // we don't want to incorrectly issue interrupt() that time
    while (addRowRequestsQueue.size() != 0) {
      usleep(100);
    }

    for (unsigned i = 0; i < writeThreads.size(); ++i)
      writeThreads[i].interrupt();

    boost::mutex::scoped_lock cl(countThreadsMutex);
    cl.unlock();
    while (true) {
      cl.lock();
      if ((countThreadsNotWaitingOnConsume == 0) &&
          (countThreadsWaitingOnConsume == (int) writeThreads.size())) {
        cl.unlock();
        break;
      }
      cl.unlock();
      usleep(100);
    }

    for (unsigned i = 0; i < writeThreads.size(); ++i)
      writeThreads[i].join();

    writeThreads.clear();
    // Reset the counts
    countThreadsWaitingOnConsume = 0;
    countThreadsNotWaitingOnConsume = 0;
  }

  // This function is what each of the worker thread executes
  void DXGTable::writeChunk_(string gtableId) {
    try {
      boost::mutex::scoped_lock cl(countThreadsMutex);
      cl.unlock();
      /* This function is executed throughtout the lifetime of an addRows worker thread
       * Brief note about various constructs used in the function:
       * --> addRowRequestsQueue.consume() will block if no pending requests to be
       *     excuted are available.
       * --> gtableAddRows() does the actual upload of rows.
       * --> We use two interleaved counters (countThread{NOT}WaitingOnConsume) to
       *     know when it is safe to terminate the threads (see joinAllWriteThreads_()).
       *     We want to terminate only when thread is waiting on .consume(), and not
       *     when gtableAddRows() is being executed.
       */
       // See C++11 working draft for details about atomics (used for counterS)
       // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf
      while (true) {
        cl.lock();
        countThreadsWaitingOnConsume++;
        cl.unlock();
        std::string req = addRowRequestsQueue.consume();
        cl.lock();
        countThreadsNotWaitingOnConsume++;
        countThreadsWaitingOnConsume--;
        cl.unlock();
        gtableAddRows(gtableId, req);
        cl.lock();
        countThreadsNotWaitingOnConsume--;
        cl.unlock();
      }
    }
    catch (const boost::thread_interrupted &ti)
    {
      return;
    }
  }

  /* This function creates new worker thread for addRows
   * Usually it wil be called once for a series of addRows() and then close() request
   * However, if we call flush() in between, then we destroy any existing threads, thus
   * threads will be recreated for any further addRows() request
   */
  void DXGTable::createWriteThreads_() {
    if (writeThreads.size() == 0) {
      // reset counters if no previous threads exist
      countThreadsNotWaitingOnConsume = 0;
      countThreadsWaitingOnConsume = 0;
      addRowRequestsQueue.setCapacity(max_write_threads_); // maximum number of addRows() requests to keep in memory
      for (int i = 0; i < max_write_threads_; ++i) {
        writeThreads.push_back(boost::thread(boost::bind(&DXGTable::writeChunk_, this, _1), dxid_));
      }
    }
  }

  void DXGTable::addRows(const JSON &data) {
    for (JSON::const_array_iterator iter = data.array_begin();
         iter != data.array_end();
         iter++) {
      row_buffer_ << (*iter).toString() << ",";

      if (row_buffer_.tellp() >= row_buffer_maxsize_) {
        finalizeRequestBuffer_();
        addRowRequestsQueue.produce(row_buffer_.str());
        reset_buffer_();
      }
    }
  }

  int DXGTable::getUnusedPartID() {
    const JSON resp = gtableNextPart(dxid_);
    return resp["part"].get<int>();
  }

  void DXGTable::flush() {
    int64_t pos = row_buffer_.tellp();
    if (pos > 10) {
      finalizeRequestBuffer_();
      addRowRequestsQueue.produce(row_buffer_.str());
    }
    joinAllWriteThreads_();
    reset_buffer_();
  }

  void DXGTable::close(const bool block) {
    flush();
    gtableClose(dxid_);

    if (block)
      waitOnState();
  }

  void DXGTable::waitOnClose() const {
    waitOnState();
  }

  DXGTable DXGTable::openDXGTable(const string &dxid,
                                  const string &proj) {
    return DXGTable(dxid, proj);
  }

  DXGTable DXGTable::newDXGTable(const vector<JSON> &columns,
                                 const vector<JSON> &indices,
                                 const JSON &data_obj_fields) {
    DXGTable gtable;
    gtable.create(columns, indices, data_obj_fields);
    return gtable;
  }

  DXGTable DXGTable::newDXGTable(const DXGTable &init_from,
                                 const JSON &data_obj_fields) {
    DXGTable gtable;
    gtable.create(init_from, data_obj_fields);
    return gtable;
  }

  DXGTable DXGTable::newDXGTable(const DXGTable &init_from,
                                 const vector<JSON> &columns,
                                 const JSON &data_obj_fields) {
    DXGTable gtable;
    gtable.create(init_from, columns, data_obj_fields);
    return gtable;
  }

  DXGTable DXGTable::newDXGTable(const DXGTable &init_from,
                                 const vector<JSON> &columns,
                                 const vector<JSON> &indices,
                                 const JSON &data_obj_fields) {
    DXGTable gtable;
    gtable.create(init_from, columns, indices, data_obj_fields);
    return gtable;
  }

  JSON DXGTable::columnDesc(const string &name,
                            const string &type) {
    JSON col_desc(JSON_OBJECT);
    col_desc["name"] = name;
    col_desc["type"] = type;
    return col_desc;
  }

  JSON DXGTable::genomicRangeIndex(const string &chr,
                                   const string &lo,
                                   const string &hi,
                                   const string &name) {
    JSON index_desc(JSON_OBJECT);
    index_desc["name"] = name;
    index_desc["type"] = "genomic";
    index_desc["chr"] = chr;
    index_desc["lo"] = lo;
    index_desc["hi"] = hi;
    return index_desc;
  }

  JSON DXGTable::lexicographicIndex(const JSON &columns,
                                    const string &name) {
    JSON index_desc(JSON_OBJECT);
    index_desc["name"] = name;
    index_desc["type"] = "lexicographic";
    index_desc["columns"] = columns;
    return index_desc;
  }

  JSON DXGTable::genomicRangeQuery(const string &chr,
                                   const int64_t lo,
                                   const int64_t hi,
                                   const string &mode,
                                   const string &index) {
    JSON query(JSON_OBJECT);
    query["index"] = index;
    query["parameters"] = JSON(JSON_OBJECT);
    query["parameters"]["mode"] = mode;
    query["parameters"]["coords"] = JSON(JSON_ARRAY);
    query["parameters"]["coords"].push_back(chr);
    query["parameters"]["coords"].push_back(lo);
    query["parameters"]["coords"].push_back(hi);
    return query;
  }

  JSON DXGTable::lexicographicQuery(const JSON &mongo_query,
                                    const string &index) {
    JSON query(JSON_OBJECT);
    query["index"] = index;
    query["parameters"] = mongo_query;
    return query;
  }

  DXGTable DXGTable::clone(const string &dest_proj_id,
                           const string &dest_folder) const {
    clone_(dest_proj_id, dest_folder);
    return DXGTable(dxid_, dest_proj_id);
  }
  const int DXGTable::NULL_VALUE = -2147483648;
}

ext: cc

max_stars_repo_name: psung/dx-toolkit

max_issues_repo_name: psung/dx-toolkit

max_forks_repo_name: psung/dx-toolkit