Algorithm-ConsistentHash-Ketama

 view release on metacpan or  search on metacpan

xs/Ketama.xs  view on Meta::CPAN

    int len;
    p->numbuckets++;
    p->totalweight += weight;

    if (p->numbuckets == 1) {
        Newxz( p->buckets, p->numbuckets, PerlKetama_Bucket );
    } else {
        Renew( p->buckets, p->numbuckets, PerlKetama_Bucket );
    }

    len = strlen(server);
    Newxz( p->buckets[p->numbuckets - 1].label, len + 1, char );
    Copy(server, p->buckets[p->numbuckets - 1].label, len + 1, char);

    p->buckets[p->numbuckets - 1].weight = weight;

    PerlKetama_clear_continuum( p );
}

static void
PerlKetama_remove_bucket(PerlKetama *p, char *server)
{
    unsigned int i;

    for( i = 0; i < p->numbuckets; i++ ) {
        if ( strEQ(p->buckets[i].label, server) ) {
            Safefree(p->buckets[i].label);
            p->totalweight -= p->buckets[i].weight;
            for( i += 1; i < p->numbuckets; i++) {
                StructCopy(&(p->buckets[i]), &(p->buckets[i - 1]), PerlKetama_Bucket);
            }
            p->numbuckets--;
            Renew(p->buckets, p->numbuckets, PerlKetama_Bucket);
            i = p->numbuckets;
        }
    }

    PerlKetama_clear_continuum( p );
}

static int
PerlKetama_buckets(PerlKetama *p)
{
    unsigned int i;
    SV *sv;
    dSP;
    PerlKetama_Bucket s;
    SP -= 1; /* must offset for object */

    for(i = 0; i < p->numbuckets; i++) {
        {
            s = p->buckets[i];
            ENTER;
            SAVETMPS;
            PUSHMARK(SP);
            mXPUSHp( "Algorithm::ConsistentHash::Ketama::Bucket", 41 );
            mXPUSHp( "label", 5 );
            mXPUSHp( s.label, strlen(s.label) );
            mXPUSHp( "weight", 6 );
            mXPUSHi( s.weight );
            PUTBACK;

            call_method("new", G_SCALAR);

            SPAGAIN;
    
            sv = POPs;
            SvREFCNT_inc(sv);

            PUTBACK;
            FREETMPS;
            LEAVE;
        }
        XPUSHs( sv );
    }
    return p->numbuckets;
}

static int
PerlKetama_continuum_compare( PerlKetama_Continuum_Point *a, PerlKetama_Continuum_Point *b )
{
    if (a->point < b->point) return -1;
    if (a->point > b->point) return 1;
    return 0;
}

#define MAX_SS_BUF 8192
static void
PerlKetama_create_continuum( PerlKetama *ketama )
{
    unsigned int i, k, h;
    char ss[MAX_SS_BUF];
    unsigned char digest[16];
    unsigned int continuum_idx;
    PerlKetama_Continuum_Point *continuum;

    continuum_idx = 0;
    Newxz(continuum, ketama->numbuckets * 160, PerlKetama_Continuum_Point);

    for ( i = 0; i < ketama->numbuckets; i++ ) {
        PerlKetama_Bucket *b = ketama->buckets + i;
        float pct = b->weight / (float) ketama->totalweight;
        unsigned int k_limit = floorf(pct * 40.0 * ketama->numbuckets);

        for ( k = 0; k < k_limit; k++ ) {
            /* 40 hashes, 4 numbers per hash = 160 points per bucket */
            if (snprintf(ss, MAX_SS_BUF, "%s-%d", b->label, k) >= MAX_SS_BUF) {
                croak("snprintf() overflow detected for key '%s-%d'. Please use shorter labels", b->label, k);
            }
            PerlKetama_md5_digest(ss, strlen(ss), digest);

            for( h = 0; h < 4; h++ ) {
                continuum[ continuum_idx ].point = ( digest[3 + h * 4] << 24 )
                                           | ( digest[2 + h * 4] << 16 )
                                           | ( digest[1 + h * 4] <<  8 )
                                           | ( digest[h * 4] )
                ;
                continuum[ continuum_idx ].bucket = b;
                continuum_idx++;
            }
        }
    }

    Renew( continuum, continuum_idx, PerlKetama_Continuum_Point );
    qsort( (void *) continuum, continuum_idx, sizeof(PerlKetama_Continuum_Point), (compfn) PerlKetama_continuum_compare );

    if (ketama->numpoints > 0) {
        Safefree(ketama->continuum);
    }



( run in 2.429 seconds using v1.01-cache-2.11-cpan-13bb782fe5a )