bool
ccnxCodecSchemaV1PacketDecoder_Decode(CCNxCodecTlvDecoder *packetDecoder, CCNxTlvDictionary *packetDictionary)
{
    bool decodeSuccess = false;

    _CCNxCodecSchemaV1Data data;

    // we temporarily store this reference, but we do not destroy it.  This
    // is just to pass the reference down the decode chain, it is not
    // stored beyond the immediate scope.  Therefore, no reference acquired.
    data.packetDictionary = packetDictionary;
    data.decoder = packetDecoder;

    if (ccnxCodecSchemaV1FixedHeaderDecoder_Decode(data.decoder, data.packetDictionary)) {
        if (_decodeOptionalHeaders(&data)) {
            // Record the position we'd start the signature verification at
            size_t signatureStartPosition = ccnxCodecTlvDecoder_Position(data.decoder);


            // Mark the beginning of the ContentObject hash region.
            CCNxWireFormatFacadeV1_Implementation.setContentObjectHashRegionStart(data.packetDictionary, signatureStartPosition);

            if (_decodeMessage(&data)) {
                // If there's anything else left, it must be the validation alg and payload
                if (!ccnxCodecTlvDecoder_IsEmpty(data.decoder)) {
                    if (_decodeValidationAlg(&data)) {
                        // at this point, we've advanced to the end of the validation algorithm,
                        // that's where we would end signature verification
                        size_t signatureStopPosition = ccnxCodecTlvDecoder_Position(data.decoder);

                        CCNxWireFormatFacadeV1_Implementation.setProtectedRegionStart(data.packetDictionary, signatureStartPosition);
                        CCNxWireFormatFacadeV1_Implementation.setProtectedRegionLength(data.packetDictionary, signatureStopPosition - signatureStartPosition);

                        if (_decodeValidationPayload(&data)) {
                            decodeSuccess = true;
                        }
                    }
                } else {
                    // nothing after the message, so that's a successful decode
                    decodeSuccess = true;
                }

                // Mark the length of the ContentObject hash region (to the end of the packet).
                size_t contentObjectHashRegionLength = ccnxCodecTlvDecoder_Position(data.decoder) - signatureStartPosition;
                CCNxWireFormatFacadeV1_Implementation.setContentObjectHashRegionLength(data.packetDictionary, contentObjectHashRegionLength);
            }
        }
    }

    return decodeSuccess;
}
LONGBOW_TEST_CASE(Global, ccnxTlvCodecName_Decode_WrongType)
{
    uint8_t decodeBytes[] = { 0x10, 0x20, 0x00, 0x0E, 0x00, CCNxNameLabelType_NAME, 0x00, 0x0A, 'b', 'r', 'a', 'n', 'd', 'y', 'w', 'i', 'n', 'e' };
    PARCBuffer *decodeBuffer = parcBuffer_Wrap(decodeBytes, sizeof(decodeBytes), 0, sizeof(decodeBytes));
    CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_Create(decodeBuffer);
    CCNxName *test = ccnxCodecSchemaV1NameCodec_Decode(decoder, 0xFFFF);

    assertNull(test, "Name should have returned NULL because the name type does not match");
    assertTrue(ccnxCodecTlvDecoder_Position(decoder) == 0, "Position should not have moved, expected 0, got %zu", ccnxCodecTlvDecoder_Position(decoder));

    ccnxCodecTlvDecoder_Destroy(&decoder);
    parcBuffer_Release(&decodeBuffer);
}
bool
ccnxCodecSchemaV1FixedHeaderDecoder_Decode(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary)
{
    if (ccnxCodecTlvDecoder_EnsureRemaining(decoder, _fixedHeaderBytes)) {
        PARCBuffer *buffer = ccnxCodecTlvDecoder_GetValue(decoder, _fixedHeaderBytes);
        bool success = ccnxTlvDictionary_PutBuffer(packetDictionary, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_FixedHeader, buffer);

        // validation
        parcBuffer_SetPosition(buffer, _fixedHeader_VersionOffset);
        uint8_t version = parcBuffer_GetUint8(buffer);

        parcBuffer_SetPosition(buffer, _fixedHeader_PacketLengthOffset);
        uint16_t packetLength = parcBuffer_GetUint16(buffer);

        parcBuffer_SetPosition(buffer, _fixedHeader_HopLimitOffset);
        uint8_t hopLimit = parcBuffer_GetUint8(buffer);

        parcBuffer_SetPosition(buffer, _fixedHeader_HeaderLengthOffset);
        uint8_t headerLength = parcBuffer_GetUint8(buffer);

        if (version != 1) {
            CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_VERSION, __func__, __LINE__, _fixedHeader_VersionOffset);
            ccnxCodecTlvDecoder_SetError(decoder, error);
            ccnxCodecError_Release(&error);
            success = false;
        } else if (packetLength < _fixedHeaderBytes) {
            CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_PACKETLENGTH_TOO_SHORT, __func__, __LINE__, _fixedHeader_PacketTypeOffset);
            ccnxCodecTlvDecoder_SetError(decoder, error);
            ccnxCodecError_Release(&error);
            success = false;
        } else if (headerLength < _fixedHeaderBytes) {
            CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_HEADERLENGTH_TOO_SHORT, __func__, __LINE__, _fixedHeader_HeaderLengthOffset);
            ccnxCodecTlvDecoder_SetError(decoder, error);
            ccnxCodecError_Release(&error);
            success = false;
        } else if (packetLength < headerLength) {
            CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_PACKETLENGTHSHORTER, __func__, __LINE__, _fixedHeader_PacketTypeOffset);
            ccnxCodecTlvDecoder_SetError(decoder, error);
            ccnxCodecError_Release(&error);
            success = false;
        }

        // decoder now points to just past the fixed header
        parcBuffer_Release(&buffer);

        // Set the hoplimit in the dictionary.
        ccnxTlvDictionary_PutInteger(packetDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_HOPLIMIT, hopLimit);

        return success;
    } else {
        CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_DECODE, __func__, __LINE__, ccnxCodecTlvDecoder_Position(decoder));
        ccnxCodecTlvDecoder_SetError(decoder, error);
        ccnxCodecError_Release(&error);
        return false;
    }
}
static bool
_decodeValidationAlg(_CCNxCodecSchemaV1Data *data)
{
    bool success = false;

    if (ccnxCodecTlvDecoder_EnsureRemaining(data->decoder, 4)) {
        // what kind of message are we looking at?
        // Note that this is based on the TLV container, not the fixed head PacketType
        uint16_t tlv_type = ccnxCodecTlvDecoder_GetType(data->decoder);
        uint16_t tlv_length = ccnxCodecTlvDecoder_GetLength(data->decoder);

        if (tlv_type == CCNxCodecSchemaV1Types_MessageType_ValidationAlg &&
            ccnxCodecTlvDecoder_EnsureRemaining(data->decoder, tlv_length)) {
            CCNxCodecTlvDecoder *decoder = ccnxCodecTlvDecoder_GetContainer(data->decoder, tlv_length);

            success = ccnxCodecSchemaV1ValidationDecoder_DecodeAlg(decoder, data->packetDictionary);

            ccnxCodecTlvDecoder_Destroy(&decoder);
        } else {
            // raise and error
            if (!ccnxCodecTlvDecoder_EnsureRemaining(data->decoder, tlv_length)) {
                // tlv_length goes beyond the decoder
                CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_TOO_LONG, __func__, __LINE__, ccnxCodecTlvDecoder_Position(data->decoder));
                ccnxCodecTlvDecoder_SetError(data->decoder, error);
                ccnxCodecError_Release(&error);
            } else {
                // not CCNxCodecSchemaV1Types_MessageType_ValidationAlg
                CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_DECODE, __func__, __LINE__, ccnxCodecTlvDecoder_Position(data->decoder));
                ccnxCodecTlvDecoder_SetError(data->decoder, error);
                ccnxCodecError_Release(&error);
            }
        }
    }

    return success;
}
/**
 * Decodes the CCNx message inside a TLV packet
 *
 * Creates an inner decoder that slices the decode buffer then passes that and our
 * message dictionary to the appropriate inner decoder.
 *
 * @param [in] data The packet decoder state
 *
 * @return true successful decode
 * @return false A decoding error
 *
 * Example:
 * @code
 * <#example#>
 * @endcode
 */
static bool
_decodeMessage(_CCNxCodecSchemaV1Data *data)
{
    bool success = false;

    if (ccnxCodecTlvDecoder_EnsureRemaining(data->decoder, 4)) {
        // what kind of message are we looking at?
        // Note that this is based on the TLV container, not the fixed head PacketType
        uint16_t tlv_type = ccnxCodecTlvDecoder_GetType(data->decoder);
        uint16_t tlv_length = ccnxCodecTlvDecoder_GetLength(data->decoder);

        // ensure its a proper tlv type
        switch (tlv_type) {
            case CCNxCodecSchemaV1Types_MessageType_Interest: // fallthrough
            case CCNxCodecSchemaV1Types_MessageType_ContentObject: // fallthrough
            case CCNxCodecSchemaV1Types_MessageType_Control: // fallthrough
                break;

            default:
                return false;
        }

        // cross check with the fixed header value
        // ccnxCodecSchemaV1FixedHeaderDecoder_Decode ensures that PacketLength is not less than HeaderLength
        size_t messageLength = ccnxCodecSchemaV1FixedHeaderDecoder_GetPacketLength(data->packetDictionary) - ccnxCodecSchemaV1FixedHeaderDecoder_GetHeaderLength(data->packetDictionary);

        if (tlv_length <= messageLength && ccnxCodecTlvDecoder_EnsureRemaining(data->decoder, tlv_length)) {
            // This decode is for the "value" of the message, it does not include the wrapper
            CCNxCodecTlvDecoder *messageDecoder = ccnxCodecTlvDecoder_GetContainer(data->decoder, tlv_length);

            if (tlv_type == CCNxCodecSchemaV1Types_MessageType_Control) {
                // the CPI messages are not a proper "message" in that there's no inner TLV, its just data
                success = _decodeCPI(messageDecoder, data->packetDictionary);
            } else {
                success = ccnxCodecSchemaV1MessageDecoder_Decode(messageDecoder, data->packetDictionary);
            }

            ccnxCodecTlvDecoder_Destroy(&messageDecoder);
        } else {
            // raise an error
            CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_TOO_LONG, __func__, __LINE__, ccnxCodecTlvDecoder_Position(data->decoder));
            ccnxCodecTlvDecoder_SetError(data->decoder, error);
            ccnxCodecError_Release(&error);
        }
    }

    return success;
}
static bool
_decodeType(CCNxCodecTlvDecoder *decoder, CCNxTlvDictionary *packetDictionary, uint16_t type, uint16_t length)
{
    bool success = false;
    switch (type) {
        case CCNxCodecSchemaV1Types_OptionalHeaders_InterestFragment:
            success = ccnxCodecTlvUtilities_PutAsBuffer(decoder, packetDictionary, type, length, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_INTFRAG);
            break;

        case CCNxCodecSchemaV1Types_OptionalHeaders_ContentObjectFragment:
            success = ccnxCodecTlvUtilities_PutAsBuffer(decoder, packetDictionary, type, length, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_OBJFRAG);
            break;

        case CCNxCodecSchemaV1Types_OptionalHeaders_InterestLifetime:
            // its a time, so use an Integer
            success = ccnxCodecTlvUtilities_PutAsInteger(decoder, packetDictionary, type, length, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_InterestLifetime);
            break;

        case CCNxCodecSchemaV1Types_OptionalHeaders_RecommendedCacheTime:
            // its a time, so use an Integer
            success = ccnxCodecTlvUtilities_PutAsInteger(decoder, packetDictionary, type, length, CCNxCodecSchemaV1TlvDictionary_HeadersFastArray_RecommendedCacheTime);
            break;

        default: {
            // if we do not know the TLV type, put it in this container's unknown list
            success = ccnxCodecTlvUtilities_PutAsListBuffer(decoder, packetDictionary, type, length, CCNxCodecSchemaV1TlvDictionary_Lists_HEADERS);
        }
        break;
    }
    if (!success) {
        CCNxCodecError *error = ccnxCodecError_Create(TLV_ERR_DECODE, __func__, __LINE__, ccnxCodecTlvDecoder_Position(decoder));
        ccnxCodecTlvDecoder_SetError(decoder, error);
        ccnxCodecError_Release(&error);
    }
    return success;
}