Parse and replace UUID and transid info in data stream.

def replaceIDs(data, receivedUUID, receivedGen, parentUUID, parentGen):
    """ Parse and replace UUID and transid info in data stream. """
    if len(data) < 20:
        return data

    logger.debug(
        "Setting received %s/%d and parent %s/%d",
        receivedUUID, receivedGen or 0, parentUUID, parentGen or 0.
        )
    data = bytearray(data)  # Make data writable

    buf = ioctl.Buffer(data)
    header = buf.read(btrfs_stream_header)

    if header.magic != BTRFS_SEND_STREAM_MAGIC:
        raise ParseException("Didn't find '%s'" % (BTRFS_SEND_STREAM_MAGIC))

    logger.debug("Version: %d", header.version)

    if header.version > BTRFS_SEND_STREAM_VERSION:
        logger.warn("Unknown stream version: %d", header.version)

    cmdHeaderView = buf.peekView(btrfs_cmd_header.size)
    cmdHeader = buf.read(btrfs_cmd_header)

    logger.debug("Command: %d", cmdHeader.cmd)

    # Read the attributes

    attrs = {}
    attrDataView = buf.peekView(cmdHeader.len)
    attrData = buf.readBuffer(cmdHeader.len)

    while attrData.len > 0:
        attrHeader = attrData.read(btrfs_tlv_header)
        attrs[attrHeader.tlv_type] = attrData.readBuffer(attrHeader.tlv_len)

    def calcCRC():
        header = cmdHeader._asdict()
        header['crc'] = 0

        # This works, but is slow
        # crc = crc32c.CRC_INIT ^ crc32c._MASK
        # crc = crc32c.crc_update(crc, btrfs_cmd_header.write(header))
        # crc = crc32c.crc_update(crc, attrDataView)
        # crc = crc32c.crc_finalize(crc)
        # crc = crc ^ crc32c._MASK

        # This works, and can be fast, when it used compiled extension
        crc = 0 ^ 0xffffffff
        crc = crc32c(btrfs_cmd_header.write(header).tostring(), crc)
        crc = crc32c(attrDataView.tobytes(), crc)
        crc &= 0xffffffff
        crc = crc ^ 0xffffffff

        # This does *not* work
        # crc = 0 ^ 0xffffffff
        # crc = binascii.crc32(btrfs_cmd_header.write(header), crc)
        # crc = binascii.crc32(attrDataView, crc)
        # crc &= 0xffffffff
        # crc ^= 0xffffffff

        return crc

    crc = calcCRC()
    if cmdHeader.crc != crc:
        logger.warn(
            "Stored crc (%d) doesn't match calculated crc (%d)",
            cmdHeader.crc, crc,
            )

    # Dispatch based on cmd and attributes

    s = attrs

    def correct(attr, format, name, old, new, encode=None):
        if new is not None and new != old:
            logger.debug("Correcting %s from %s to %s", name, str(old), str(new))
            if encode is not None:
                new = encode(new)
            TLV_PUT(attrs, attr, format, new)

    def correctCRC():
        crc = calcCRC()
        if cmdHeader.crc != crc:
            logger.debug("Correcting CRC from %d to %d", cmdHeader.crc, crc)
            header = cmdHeader._asdict()
            header['crc'] = crc
            cmdHeaderView[:] = btrfs_cmd_header.write(header).tostring()

    if cmdHeader.cmd == BTRFS_SEND_C_SUBVOL:
        path = TLV_GET_STRING(s, BTRFS_SEND_A_PATH, )
        uuid = TLV_GET_UUID(s, BTRFS_SEND_A_UUID, )
        ctransid = TLV_GET_U64(s, BTRFS_SEND_A_CTRANSID, )

        logger.debug('Subvol: %s/%d %s', uuid, ctransid, path)

        correct(
            BTRFS_SEND_A_UUID,
            's',
            'received UUID',
            uuid,
            receivedUUID,
            btrfs.uuid2bytes
        )
        correct(
            BTRFS_SEND_A_CTRANSID,
            t.u64,
            'received gen',
            ctransid,
            receivedGen
        )

    elif cmdHeader.cmd == BTRFS_SEND_C_SNAPSHOT:
        path = TLV_GET_STRING(s, BTRFS_SEND_A_PATH, )
        uuid = TLV_GET_UUID(s, BTRFS_SEND_A_UUID, )
        ctransid = TLV_GET_U64(s, BTRFS_SEND_A_CTRANSID, )
        clone_uuid = TLV_GET_UUID(s, BTRFS_SEND_A_CLONE_UUID, )
        clone_ctransid = TLV_GET_U64(s, BTRFS_SEND_A_CLONE_CTRANSID, )

        logger.debug(
            'Snapshot: %s/%d -> %s/%d %s',
            clone_uuid, clone_ctransid, uuid, ctransid, path
        )

        correct(
            BTRFS_SEND_A_UUID,
            's',
            'received UUID',
            uuid,
            receivedUUID,
            btrfs.uuid2bytes
        )
        correct(
            BTRFS_SEND_A_CTRANSID,
            t.u64,
            'received gen',
            ctransid,
            receivedGen
        )

        correct(
            BTRFS_SEND_A_CLONE_UUID,
            's',
            'parent UUID',
            clone_uuid,
            parentUUID,
            btrfs.uuid2bytes
        )
        correct(
            BTRFS_SEND_A_CLONE_CTRANSID,
            t.u64,
            'parent gen',
            clone_ctransid,
            parentGen
        )
    else:
        logger.warn("Didn't find volume UUID command")

    correctCRC()

    return data