• 古老的pike


    快速略读了一下源码,记了一些东西。

          


      先看看mapping

           mapping其实就是C++中的multimap,但是支持更多。

      array values(mapping)。这个方法可以返回所有mapping中的value,那么values()究竟作了什么呢?源码中是这样定义的:

     1 PMOD_EXPORT struct array *mapping_values(struct mapping *m)
     2 {
     3   INT32 e;
     4   struct keypair *k;
     5   struct array *a;
     6   struct svalue *s;
     7 
     8 #ifdef PIKE_DEBUG
     9   if(m->data->refs <=0)
    10     Pike_fatal("Zero refs in mapping->data
    ");
    11 #endif
    12 
    13   check_mapping_for_destruct(m);
    14 
    15   a=allocate_array(m->data->size);
    16   s=ITEM(a);
    17 
    18   /* no locking required */
    19   NEW_MAPPING_LOOP(m->data) assign_svalue(s++, & k->val);
    20 
    21   a->type_field = m->data->val_types;
    22 
    23 #ifdef PIKE_DEBUG
    24   if(d_flag > 1) check_mapping_type_fields(m);
    25 #endif
    26   
    27   return a;
    28 }

      可以看到,在第15行直接申请了足够存储所有value的内存大小,第19行是一个宏,其循环了所有的可能的value,然后assign_svalue就会将每个k->value给拷贝到返回值array中,并且在27行返回a。

      mapping也可以将字符串作为key的,但是它是如何依靠一个ke=string来迅速地找到对应的value的呢?其实mapping使用的siphash(维基百科原著论文)来哈希字符串的,也就是先将字符串哈希成一个值,再跟其他的key的哈希值来匹配,这样就容易找到了。如果有兴趣可以看pike中的实现:

     1 static size_t low_hashmem_siphash24( const void *s, size_t len, size_t nbytes, size_t key )
     2 {
     3   const unsigned char * in = (const unsigned char*)s;
     4   unsigned long long inlen = MINIMUM(len, nbytes);
     5 
     6   /* "some pseudo randomly generated bytes" 伪随机产生的字节 */
     7   unsigned INT64 v0 = 0x736f6d6570736575ULL;
     8   unsigned INT64 v1 = 0x646f72616e646f6dULL;
     9   unsigned INT64 v2 = 0x6c7967656e657261ULL;
    10   unsigned INT64 v3 = 0x7465646279746573ULL;
    11   unsigned INT64 b;
    12   unsigned INT64 k0 = (unsigned INT64)key;
    13   unsigned INT64 k1 = (unsigned INT64)key;
    14   unsigned INT64 m;
    15   const unsigned char *end = in + inlen - ( inlen % sizeof( unsigned INT64 ) );
    16   const int left = inlen & 7;
    17   b = ( ( unsigned INT64 )inlen ) << 56;
    18   v3 ^= k1;
    19   v2 ^= k0;
    20   v1 ^= k1;
    21   v0 ^= k0;
    22 
    23   for ( ; in != end; in += 8 )
    24   {
    25     m = U8TO64_LE( in );
    26     v3 ^= m;
    27     SIPROUND;
    28     SIPROUND;
    29     v0 ^= m;
    30   }
    31 
    32   switch( left )
    33   {
    34   case 7: b |= ( ( unsigned INT64 )in[ 6] )  << 48;
    35 
    36   case 6: b |= ( ( unsigned INT64 )in[ 5] )  << 40;
    37 
    38   case 5: b |= ( ( unsigned INT64 )in[ 4] )  << 32;
    39 
    40   case 4: b |= ( ( unsigned INT64 )in[ 3] )  << 24;
    41 
    42   case 3: b |= ( ( unsigned INT64 )in[ 2] )  << 16;
    43 
    44   case 2: b |= ( ( unsigned INT64 )in[ 1] )  <<  8;
    45 
    46   case 1: b |= ( ( unsigned INT64 )in[ 0] ); break;
    47 
    48   case 0: break;
    49   }
    50 
    51   v3 ^= b;
    52   SIPROUND;
    53   SIPROUND;
    54   v0 ^= b;
    55   v2 ^= 0xff;
    56   SIPROUND;
    57   SIPROUND;
    58   SIPROUND;
    59   SIPROUND;
    60   b = v0 ^ v1 ^ v2  ^ v3;
    61   return (size_t)b;
    62 }
    low_hashmen_siphash24

      将字符串哈希成一个值后,pike是以一个表来管理字符串的(不知是否为所有),定义在stralloc.c中的原型如下:

    static struct pike_string **base_table=0;

      shared string table。当字符串被哈希成一个值后,就在这个table中寻找(这是简化了的说法,其实在找之前还与htable_mask做了位与)。但是哈希值是不能保证唯一的,所以需要一个二级指针,每个桶中存放着所有的哈希值相同的字符串呢。那如果重叠率太高了呢?pike还定义了一个全局变量:

    static unsigned int need_new_hashkey_depth=0;

      这个变量在每次找不到的时候就可以会有变化,这跟“字符串哈希”所取的前缀长短有关(如果每次都将整个字符串用于哈希的话,也太长了吧?其实不知道是否是整个字符串哈希),可能是会根据这个变量值作哈希的调整。函数原型如下:

    static struct pike_string *internal_findstring(const char *s,
                                                   ptrdiff_t len,
                                                   enum size_shift size_shift,
                                                   size_t hval)        /* hval是用siphash计算出来的结果 */

       

      每对key-value的信息是以一个struct keypair来记录的,其中仅记录了一些信息,以及一个指针(应该是指向真正数据的),初始mapping时需要申请一些内存来存放keypair。

    #define MD_KEYPAIRS(MD, HSIZE)   
    ( (struct keypair *)    
    DO_ALIGN( PTR_TO_INT(MD) + OFFSETOF(mapping_data, hash) + HSIZE * sizeof(struct keypair *), 
    ALIGNOF(struct keypair)) )

      DO_ALIGN的定义:

    #define DO_ALIGN(X,Y) (((size_t)(X)+((Y)-1)) & ~((Y)-1))

      ALIGNIOF是内置函数,用于计算对齐内存所占的大小。

      mapping中提用了几个操作,很是方便,比如 &与操作,|或操作,xor异或操作等等。

      先拿xor开刀,就是将两个mapping中的具有相同key的key-value都给删除掉,留下唯一的key的那种。实现起来也没有多大的优化技巧,就是先将小的mapping先拷贝出来,然后再将大的mapping中的key-value逐个往里面插,如果key已经存在了,就一块删掉,如果不存在,就插进去。仍然需要考虑特殊情况的,比如相同mapping作异或操作等。源码:

     1 static struct mapping *xor_mappings(struct mapping *a, struct mapping *b)
     2 {
     3     struct mapping *res;
     4     struct keypair *k;
     5     struct mapping_data *a_md = a->data;    
     6     struct mapping_data *b_md = b->data;
     7     INT32 e;
     8     ONERROR err;
     9 
    10     /* First some special cases. */
    11     if (!a_md->size) return destructive_copy_mapping(b);    /* 只是有可能会删除真数据 */
    12     if (!b_md->size) return destructive_copy_mapping(a);
    13     if (a_md == b_md) return allocate_mapping(0);            /* 数据一样,返回空mapping */
    14 
    15     /* Copy the largest mapping. 保持a<=b */
    16     if (a_md->size > b_md->size) {
    17         struct mapping *tmp = a;
    18         a = b;
    19         b = tmp;
    20         a_md = b_md;
    21         b_md = b->data;
    22     }
    23     res = destructive_copy_mapping(b);    /* 先拷贝个小的进去 */
    24     SET_ONERROR(err, do_free_mapping, res);
    25 
    26     /* Add elements in a that aren't in b, and remove those that are. */
    27     NEW_MAPPING_LOOP(a_md) {
    28         size_t h = k->hval & (b_md->hashsize - 1);
    29         struct keypair *k2;
    30         for (k2 = b_md->hash[h]; k2; k2 = k2->next) 
    31         {
    32             if ((k2->hval == k->hval) && is_eq(&k2->ind, &k->ind)) /* 先比对哈希值,再比对真实数据 */
    33             {
    34                 break;
    35             }
    36         }
    37         if (!k2)        /* k2=0表示没有找到相同的    */ 
    38         {
    39             mapping_insert(res, &k->ind, &k->val);
    40         }
    41            else
    42            {
    43             map_delete(res, &k2->ind);
    44         }
    45         b_md = b->data;
    46     }
    47     UNSET_ONERROR(err);
    48     return res;
    49 }

      看看逻辑或运算,c=a|b 。这也是很普通的操作而已,实现起来也没有什么优化。思路是,1,若b比较大,那么先拷贝b到c,而对于a,逐个key判断是否存在于c,若在则不操作,否则插入。2,若a比较大,直接将a拷贝到c,再将b逐个插入到c,若key已存在,直接覆盖。

     1 static struct mapping *or_mappings(struct mapping *a, struct mapping *b)
     2 {
     3     struct mapping *res;
     4     struct keypair *k;
     5     struct mapping_data *a_md = a->data;
     6     struct mapping_data *b_md = b->data;
     7     INT32 e;
     8     ONERROR err;
     9 
    10     /* First some special cases. */
    11     if (!a_md->size) return destructive_copy_mapping(b);
    12     if (!b_md->size) return destructive_copy_mapping(a);
    13     if (a_md == b_md) return destructive_copy_mapping(a);
    14 
    15     if (a_md->size <= b_md->size) {        /* a的数据比较少 */
    16         /* Copy the second mapping. */
    17         res = destructive_copy_mapping(b);
    18         SET_ONERROR(err, do_free_mapping, res);
    19 
    20         if (!b_md->hashsize) {
    21             Pike_fatal("Invalid hashsize.
    ");
    22         }
    23 
    24         /* Add elements in a that aren't in b. */
    25         NEW_MAPPING_LOOP(a_md) {
    26             size_t h = k->hval & (b_md->hashsize - 1);
    27             struct keypair *k2;
    28             for (k2 = b_md->hash[h]; k2; k2 = k2->next) {    /* 如果b中已经存在的,a就不能再插进去了 */
    29                 if ((k2->hval == k->hval) && is_eq(&k2->ind, &k->ind)) {
    30                     break;
    31                 }
    32             }
    33             if (!k2) {
    34                 mapping_insert(res, &k->ind, &k->val);
    35                 b_md = b->data;
    36             }
    37         }
    38         UNSET_ONERROR(err);
    39     } else {
    40         /* Copy the first mapping. */
    41         res = destructive_copy_mapping(a);
    42         SET_ONERROR(err, do_free_mapping, res);
    43 
    44         /* Add all elements in b. */
    45         NEW_MAPPING_LOOP(b_md) {    /* 直接将b插进去,如果a中已经存在key,则value会自动覆盖 */
    46             mapping_insert(res, &k->ind, &k->val);
    47         }
    48         UNSET_ONERROR(err);
    49     }
    50     return res;
    51 }

      and操作,即c=a+b,对于那些“key在a和b中皆存在,key-value属于b”的key-value于c中。源码的实现思路是,先将b拷贝到c,再将a中每个元素判断key是否存在于c中,若存在,则不操作,否则,删除。

     1 static struct mapping *and_mappings(struct mapping *a, struct mapping *b)
     2 {
     3     struct mapping *res;
     4     struct keypair *k;
     5     struct mapping_data *a_md = a->data;
     6     struct mapping_data *b_md = b->data;
     7     INT32 e;
     8     ONERROR err;
     9 
    10     /* First some special cases. */
    11     if (!a_md->size || !b_md->size) return allocate_mapping(0);
    12     if (a_md == b_md) return destructive_copy_mapping(a);
    13 
    14     /* Copy the second mapping. */
    15     res = copy_mapping(b);    /* 先拷贝了b */
    16     SET_ONERROR(err, do_free_mapping, res);
    17 
    18     /* Remove elements in res that aren't in a. */
    19     NEW_MAPPING_LOOP(b_md) {        /* 如果已经存在key,则不操作;否则删除已存在的key-value */
    20         size_t h = k->hval & (a_md->hashsize - 1);
    21         struct keypair *k2;
    22         for (k2 = a_md->hash[h]; k2; k2 = k2->next) {
    23             if ((k2->hval == k->hval) && is_eq(&k2->ind, &k->ind)) {
    24                 break;
    25             }
    26         }
    27         if (!k2) {
    28             map_delete(res, &k->ind);
    29         }
    30     }
    31     UNSET_ONERROR(err);
    32     return res;
    33 }
    and_mappings

      substract操作,即c=a-b,对于所有在b中出现的key,如果a中也存在,则删除,否则不操作。

     1 static struct mapping *subtract_mappings(struct mapping *a, struct mapping *b)
     2 {
     3     struct mapping *res;
     4     struct keypair *k;
     5     struct mapping_data *a_md = a->data;
     6     struct mapping_data *b_md = b->data;
     7     INT32 e;
     8     ONERROR err;
     9 
    10     /* First some special cases. */
    11     if (!a_md->size || !b_md->size || !a_md->hashsize || !b_md->hashsize) {
    12         return destructive_copy_mapping(a);
    13     }
    14     if (a_md == b_md) {
    15         return allocate_mapping(0);
    16     }
    17     /* FIXME: The break-even point should probably be researched. */
    18     if (a_md->size < b_md->size) {
    19         /* Add the elements in a that aren't in b. */
    20         res = allocate_mapping(a_md->size);
    21         SET_ONERROR(err, do_free_mapping, res);
    22         NEW_MAPPING_LOOP(a_md) {
    23             size_t h = k->hval & (b_md->hashsize - 1);
    24             struct keypair *k2;
    25             for (k2 = b_md->hash[h]; k2; k2 = k2->next) {
    26                 if ((k2->hval == k->hval) && is_eq(&k2->ind, &k->ind)) {
    27                     break;
    28                 }
    29             }
    30             if (!k2) {
    31                 mapping_insert(res, &k->ind, &k->val);
    32             }
    33         }
    34     } else {
    35         /* Remove the elements in a that are in b. */
    36         res = destructive_copy_mapping(a);
    37         SET_ONERROR(err, do_free_mapping, res);
    38         NEW_MAPPING_LOOP(b_md) {
    39             map_delete(res, &k->ind);
    40         }
    41     }
    42     UNSET_ONERROR(err);
    43     return res;
    44 }
    subtract_mappings

      replace(mapping,old,new)函数,将mapping中所有value=old的替换成value=new。由于pike中使用了索引的概念,替换之后可能会有value需要清除了,如果它的引用次数=1的话。

     1 PMOD_EXPORT void mapping_replace(struct mapping *m, struct svalue *from, struct svalue *to)
     2 {
     3     INT32 e;
     4     struct keypair *k;
     5     struct mapping_data *md;
     6 
     7 #ifdef PIKE_DEBUG
     8     if (m->data->refs <= 0)
     9         Pike_fatal("Zero refs in mapping->data
    ");
    10 #endif
    11 
    12     md = m->data;
    13     if (md->size) {
    14         add_ref(md);    /* 增加md的引用一次 */
    15         NEW_MAPPING_LOOP(md) {    /* 扫描所有键值对 */
    16             if (is_eq(& k->val, from)) { /* 如果与from相同的话就申请内存并替换成to */
    17                 PREPARE_FOR_DATA_CHANGE();    /* 为k申请新内存 */
    18                 assign_svalue(& k->val, to);
    19                 md->val_types |= 1 << TYPEOF(*to);
    20             }
    21         }
    22         free_mapping_data(md);    /* 释放掉引用次数=1的value,不一定是真的释放 */
    23     }
    24 
    25 #ifdef PIKE_DEBUG
    26     if (d_flag > 1) check_mapping_type_fields(m);
    27 #endif
    28 }

      mkmapping(array ind,array val)函数,创建一个mapping对象,并且以ind作为索引,val作为值来组成key-value初始化该对象。这里的实现是以ind的元素个数作为mapping的元素个数的,所以如果val的元素个数比ind要大的话,是会自动忽略后面的多余部分。还有一个点,就是初始化时,hashsize(也称之为桶的数量)应该是多少的问题,这里依靠MAP_SLOTS这个宏来计算的,这样的计算方式的优劣性未明~

     1 #define MAP_SLOTS(X) ((X)?((X)+((X)>>4)+8):0)
     2 PMOD_EXPORT struct mapping *mkmapping(struct array *ind, struct array *val)
     3 {
     4     struct mapping *m;
     5     struct svalue *i, *v;
     6     INT32 e;
     7 
     8 #ifdef PIKE_DEBUG
     9     if (ind->size != val->size)
    10         Pike_fatal("mkmapping on different sized arrays.
    ");
    11 #endif
    12 
    13     m = allocate_mapping(MAP_SLOTS(ind->size)); /* 申请一个mapping,这里关系到hashsize的大小  */
    14     i = ITEM(ind);
    15     v = ITEM(val);
    16     for (e = 0; e < ind->size; e++) /* 逐个插入*/
    17         low_mapping_insert(m, i++, v++, 2);
    18 
    19     return m;
    20 }
  • 相关阅读:
    设计者模式详解--代理模式
    设计者模式详解--适配器模式
    设计者模式详解--原型模式
    设计者模式详解--建造者模式
    设计者模式详解--单例模式
    设计者模式详解--抽象工厂模式
    设计者模式详解--工厂方法模式
    angularjs 选项卡 --- 自定义属性
    AngularJS 自定义指令
    Jquery中的prop()方法 全选或全不选
  • 原文地址:https://www.cnblogs.com/xcw0754/p/5085806.html
Copyright © 2020-2023  润新知