ssize_t
_appendMetadata(CCNxCodecTlvEncoder *encoder, CCNxManifestHashGroup *group)
{
    ssize_t length = 0;

    // Pre-populate this field -- we'll come back and fill in the length after we're done
    size_t startPosition = ccnxCodecTlvEncoder_Position(encoder);
    ccnxCodecTlvEncoder_AppendContainer(encoder, CCNxCodecSchemaV1Types_CCNxManifestHashGroup_Metadata, length);

    // Now append all metadata that exists in the hash group.
    const CCNxName *locator = ccnxManifestHashGroup_GetLocator(group);
    if (locator != NULL) {
        char *nameString = ccnxName_ToString(locator);
        PARCBuffer *nameBuffer = parcBuffer_AllocateCString(nameString);
        length += ccnxCodecTlvEncoder_AppendBuffer(encoder, CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_Locator, nameBuffer);
        parcBuffer_Release(&nameBuffer);
        parcMemory_Deallocate(&nameString);
    }

    size_t dataSize = ccnxManifestHashGroup_GetDataSize(group);
    if (dataSize > 0) {
        length += ccnxCodecTlvEncoder_AppendUint64(encoder, CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_DataSize, dataSize);
    }

    size_t blockSize = ccnxManifestHashGroup_GetBlockSize(group);
    if (blockSize > 0) {
        length += ccnxCodecTlvEncoder_AppendUint64(encoder, CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_BlockSize, blockSize);
    }

    size_t entrySize = ccnxManifestHashGroup_GetEntrySize(group);
    if (entrySize > 0) {
        length += ccnxCodecTlvEncoder_AppendUint64(encoder, CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_EntrySize, entrySize);
    }

    size_t treeSize = ccnxManifestHashGroup_GetTreeHeight(group);
    if (treeSize > 0) {
        length += ccnxCodecTlvEncoder_AppendUint64(encoder, CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_TreeHeight, treeSize);
    }

    const PARCBuffer *dataDigest = ccnxManifestHashGroup_GetOverallDataDigest(group);
    if (dataDigest != NULL) {
        length += ccnxCodecTlvEncoder_AppendBuffer(encoder, CCNxCodecSchemaV1Types_CCNxManifestHashGroupMetadata_OverallDataSha256, (PARCBuffer *) dataDigest);
    }

    // Rewind back to the container opening and fill in the length
    size_t endPosition = ccnxCodecTlvEncoder_Position(encoder);
    ccnxCodecTlvEncoder_PutUint16(encoder, startPosition, CCNxCodecSchemaV1Types_CCNxManifestHashGroup_Metadata);
    ccnxCodecTlvEncoder_PutUint16(encoder, startPosition + 2, length);
    ccnxCodecTlvEncoder_SetPosition(encoder, endPosition);

    return endPosition - startPosition;
}
ssize_t
ccnxCodecTlvUtilities_NestedEncode(CCNxCodecTlvEncoder *outerEncoder, CCNxTlvDictionary *packetDictionary, uint32_t nestedType,
                           ssize_t (*nestedEncoderFunction)(CCNxCodecTlvEncoder *protoInfoEncoder, CCNxTlvDictionary *packetDictionary))
{
    size_t startPosition = ccnxCodecTlvEncoder_Position(outerEncoder);

    ccnxCodecTlvEncoder_AppendContainer(outerEncoder, nestedType, 0);
    ssize_t nestedLength = nestedEncoderFunction(outerEncoder, packetDictionary);
    if (nestedLength > 0) {
        ccnxCodecTlvEncoder_SetContainerLength(outerEncoder, startPosition, nestedLength);
    } else {
        // rewind the container
        ccnxCodecTlvEncoder_SetPosition(outerEncoder, startPosition);
        return nestedLength;
    }

    size_t endPosition = ccnxCodecTlvEncoder_Position(outerEncoder);
    return endPosition - startPosition;
}
ssize_t
ccnxCodecSchemaV1ManifestEncoder_Encode(CCNxCodecTlvEncoder *encoder, CCNxTlvDictionary *packetDictionary)
{
    ssize_t length = 0;

    ssize_t numHashGroups = ccnxTlvDictionary_ListSize(packetDictionary, CCNxCodecSchemaV1TlvDictionary_Lists_HASH_GROUP_LIST);
    for (size_t i = 0; i < numHashGroups; i++) {

        // Skip past the TL of the hash group to append the pointers inside
        ssize_t groupLength = 0;
        ccnxCodecTlvEncoder_AppendContainer(encoder, CCNxCodecSchemaV1Types_CCNxMessage_HashGroup, groupLength);

        CCNxManifestInterface *interface = ccnxManifestInterface_GetInterface(packetDictionary);
        CCNxManifestHashGroup *group = interface->getHashGroup(packetDictionary, i);

        // Encode any metadata, if present.
        if (ccnxManifestHashGroup_HasMetadata(group)) {
            groupLength += _appendMetadata(encoder, group);
        }

        // Append the HashGroup pointers
        size_t numPointers = ccnxManifestHashGroup_GetNumberOfPointers(group);
        for (size_t p = 0; p < numPointers; p++) {
            CCNxManifestHashGroupPointer *ptr = ccnxManifestHashGroup_GetPointerAtIndex(group, p);
            ssize_t ptrLength = _appendPointer(encoder, ptr);
            if (ptrLength < 0) {
                return ptrLength;
            }
            groupLength += ptrLength;
        }

        // Now that we know the overall length, rewind back to the start and append the TL
        // part of the container.
        size_t endPosition = ccnxCodecTlvEncoder_Position(encoder);
        ssize_t offset = endPosition - groupLength - 4;
        ccnxCodecTlvEncoder_PutUint16(encoder, offset, CCNxCodecSchemaV1Types_CCNxMessage_HashGroup);
        ccnxCodecTlvEncoder_PutUint16(encoder, offset + 2, groupLength);
        ccnxCodecTlvEncoder_SetPosition(encoder, endPosition);

        length += groupLength + 4;

        ccnxManifestHashGroup_Release(&group);
    }

    return length;
}
static size_t
_appendPointer(CCNxCodecTlvEncoder *encoder, CCNxManifestHashGroupPointer *ptr)
{
    const PARCBuffer *digest = ccnxManifestHashGroupPointer_GetDigest(ptr);
    CCNxManifestHashGroupPointerType type = ccnxManifestHashGroupPointer_GetType(ptr);

    ssize_t length = -1;
    switch (type) {
    case CCNxManifestHashGroupPointerType_Data:
        length = ccnxCodecTlvEncoder_AppendBuffer(encoder, CCNxCodecSchemaV1Types_CCNxManifestHashGroup_DataPointer, (PARCBuffer *) digest);
        break;
    case CCNxManifestHashGroupPointerType_Manifest:
        length = ccnxCodecTlvEncoder_AppendBuffer(encoder, CCNxCodecSchemaV1Types_CCNxManifestHashGroup_ManifestPointer, (PARCBuffer *) digest);
        break;
    default:
        assertTrue(false, "Invalid pointer type %d", type);
    }

    if (length < 0) {
        CCNxCodecError *error = ccnxCodecError_Create(TLV_MISSING_MANDATORY, __func__, __LINE__, ccnxCodecTlvEncoder_Position(encoder));
        ccnxCodecTlvEncoder_SetError(encoder, error);
        ccnxCodecError_Release(&error);
    }

    return length;
}