EV-Kafka

 view release on metacpan or  search on metacpan

src/EV__Kafka.xs  view on Meta::CPAN

        const char *tname; int16_t tnlen;
        n = kf_read_string(p, end, &tname, &tnlen);
        if (n < 0) goto done;
        p += n;
        hv_store(th, "topic", 5, newSVpvn(tname ? tname : "", tname ? tnlen : 0), 0);

        /* partitions: ARRAY */
        if (end - p < 4) goto done;
        int32_t part_count = kf_read_i32(p); p += 4;
        AV *parts_av = (AV*)sv_2mortal((SV*)newAV());
        int32_t j;

        for (j = 0; j < part_count; j++) {
            HV *ph = (HV*)sv_2mortal((SV*)newHV());
            if (end - p < 4) goto done;
            int32_t pid = kf_read_i32(p); p += 4;
            hv_store(ph, "partition", 9, newSViv(pid), 0);

            if (end - p < 2) goto done;
            int16_t err = kf_read_i16(p); p += 2;
            hv_store(ph, "error_code", 10, newSViv(err), 0);

            if (end - p < 8) goto done;
            int64_t base_offset = kf_read_i64(p); p += 8;
            hv_store(ph, "base_offset", 11, newSViv(base_offset), 0);

            /* log_append_time (v2+) */
            if (version >= 2) {
                if (end - p < 8) goto done;
                p += 8;
            }

            /* log_start_offset (v5+) */
            if (version >= 5) {
                if (end - p < 8) goto done;
                p += 8;
            }

            av_push(parts_av, newRV_inc((SV*)ph));
        }
        hv_store(th, "partitions", 10, newRV_inc((SV*)parts_av), 0);
        av_push(topics_av, newRV_inc((SV*)th));
    }

    /* throttle_time_ms (v1+) */
    if (version >= 1 && end - p >= 4) {
        int32_t throttle = kf_read_i32(p); p += 4;
        hv_store(result, "throttle_time_ms", 16, newSViv(throttle), 0);
    }

done:
    hv_store(result, "topics", 6, newRV_inc((SV*)topics_av), 0);
    return sv_2mortal(newRV_noinc((SV*)result));
}

/* ================================================================
 * RecordBatch decoder (for Fetch responses)
 * ================================================================ */

/* Decode records from a RecordBatch, push them as hashrefs onto records_av.
 * Returns number of records decoded, or -1 on error. */
static int kf_decode_record_batch(pTHX_ const char *data, size_t len,
    AV *records_av, int64_t *out_base_offset)
{
    const char *p = data;
    const char *end = data + len;
    int n;

    if (end - p < 12) return -1;
    int64_t base_offset = kf_read_i64(p); p += 8;
    if (out_base_offset) *out_base_offset = base_offset;
    int32_t batch_length = kf_read_i32(p); p += 4;

    /* compare in 64-bit so a huge batch_length cannot wrap the check */
    if (batch_length < 0 || (int64_t)(end - p) < (int64_t)batch_length) return -1;
    const char *batch_end = p + batch_length;

    if (batch_end - p < 9) return -1;
    /* int32_t partition_leader_epoch = kf_read_i32(p); */ p += 4;
    int8_t magic = (int8_t)*p; p += 1;
    if (magic != 2) return -1; /* only support magic=2 (current format) */
    uint32_t expected_crc = (uint32_t)kf_read_i32(p); p += 4;
    /* CRC32C covers the bytes from attributes to end of batch. */
    if (crc32c(p, (size_t)(batch_end - p)) != expected_crc) return -1;

    if (batch_end - p < 36) return -1;
    int16_t attributes = kf_read_i16(p); p += 2;
    int compression_type = attributes & 0x07;
    /* int32_t last_offset_delta = kf_read_i32(p); */ p += 4;
    int64_t first_timestamp = kf_read_i64(p); p += 8;
    /* int64_t max_timestamp = kf_read_i64(p); */ p += 8;
    /* int64_t producer_id = kf_read_i64(p); */ p += 8;
    /* int16_t producer_epoch = kf_read_i16(p); */ p += 2;
    /* int32_t base_sequence = kf_read_i32(p); */ p += 4;

    if (batch_end - p < 4) return -1;
    int32_t record_count = kf_read_i32(p); p += 4;

    /* Decompress if needed */
    const char *rec_data = p;
    const char *rec_end = batch_end;
    char *decompressed = NULL;

    if (compression_type != COMPRESS_NONE && batch_end > p) {
        size_t compressed_len = batch_end - p;
        size_t decomp_cap = compressed_len * 4;
        if (decomp_cap < 4096) decomp_cap = 4096;
        /* set only by a codec branch that fully succeeded (see below) */
        int decompressed_ok = 0;

#ifdef HAVE_ZLIB
        if (compression_type == COMPRESS_GZIP) {
            int zok = 0;
            while (!zok && decomp_cap < 64 * 1024 * 1024) {
                Newx(decompressed, decomp_cap, char);
                z_stream zs;
                Zero(&zs, 1, z_stream);
                int zinit = inflateInit2(&zs, MAX_WBITS + 16);
                if (zinit != Z_OK) {
                    Safefree(decompressed);
                    decompressed = NULL;



( run in 1.082 second using v1.01-cache-2.11-cpan-3fabe0161c3 )