zoukankan      html  css  js  c++  java
  • php源码分析之base64_encode函数

    base64_encode编码规律分析

    字符串长度除以3向上取整乘以4等于编码后的字符串长度

    ceil(strlen($string)/3)*4 = strlen(base64_encode($string));

    例如base64_encode("abcd") == "YWJjZA==" (2*4=8)


    如果字符串长度除以3的余数是0,则编码后没有“=”符号,且如果每相邻3个字符块相同,则编码后相邻4个字符串相同,例如base64_encode("abcabc")=="YWJjYWJj"
    如果字符串长度除以3的余数是1,则编码后有两个“=”符号,例如base64_encode("abcd") == "YWJjZA=="
    如果字符串长度除以3的余数是2,则编码后有一个“=”符号,例如base64_encode("abcde") == "YWJjZGU="

    这些规律是笔者在php源代码中总结出来的,如果感兴趣的话,请耐心听我分析

    首先,我们打开实现base64_encode函数的源码文件(php源码/ext/standard/base64.c文件

    我把主要部分代码贴出来

    ..........................
    static const char base64_table[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '' }; static const char base64_pad = '=';
    ............................................. PHPAPI unsigned
    char *php_base64_encode(const unsigned char *str, int length, int *ret_length) /* {{{ */ { const unsigned char *current = str; unsigned char *p; unsigned char *result; if (length < 0) { if (ret_length != NULL) { *ret_length = 0; } return NULL; } result = (unsigned char *) safe_emalloc((length + 2) / 3, 4 * sizeof(char), 1); p = result; while (length > 2) { /* keep going until we have less than 24 bits */ *p++ = base64_table[current[0] >> 2]; *p++ = base64_table[((current[0] & 0x03) << 4) + (current[1] >> 4)]; *p++ = base64_table[((current[1] & 0x0f) << 2) + (current[2] >> 6)]; *p++ = base64_table[current[2] & 0x3f]; current += 3; length -= 3; /* we just handle 3 octets of data */ } /* now deal with the tail end of things */ if (length != 0) { *p++ = base64_table[current[0] >> 2]; if (length > 1) { *p++ = base64_table[((current[0] & 0x03) << 4) + (current[1] >> 4)]; *p++ = base64_table[(current[1] & 0x0f) << 2]; *p++ = base64_pad; } else { *p++ = base64_table[(current[0] & 0x03) << 4]; *p++ = base64_pad; *p++ = base64_pad; } } if (ret_length != NULL) { *ret_length = (int)(p - result); } *p = ''; return result; } ................................................ PHP_FUNCTION(base64_encode) { char *str; unsigned char *result; int str_len, ret_length; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) { return; } result = php_base64_encode((unsigned char*)str, str_len, &ret_length); if (result != NULL) { RETVAL_STRINGL((char*)result, ret_length, 0); } else { RETURN_FALSE; } }

    PHP_FUNCTION(base64_encode)函数表示:注册base64_encode函数

    函数中先定义一个字符串指针变量str(用来保存base64_encode传递过来的字符串参数),无符号指针变量result(用来保存编码后的字符串),整形str_len(字符串参数长度),ret_length(编码后的字符串长度)

    zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len)将参数字符串保存到str中,字符串长度赋值给str_len

    调用php_base64_encode函数获取编码后的字符串到result中

    如果result不为空的话通过RETVAL_STRINGL返回,否则返回false

    php_base64_encode(const unsigned char *str, int length, int *ret_length)函数对字符串进行编码

     这里主要看这段代码

    //我命名为第一代码块
    while (length > 2) { /* keep going until we have less than 24 bits */
      *p++ = base64_table[current[0] >> 2];
      *p++ = base64_table[((current[0] & 0x03) << 4) + (current[1] >> 4)];
      *p++ = base64_table[((current[1] & 0x0f) << 2) + (current[2] >> 6)];
      *p++ = base64_table[current[2] & 0x3f];
    
      current += 3;
      length -= 3; /* we just handle 3 octets of data */
    }

    我们假设需要编码的字符串为“abc”3个字符,则

    第一个编码字符

    current[0]为a (a字符的二进制为01100001)

    current[0]>>2为24(00011000)

    所以第一个编码字符是Y(base64_table[24]等于Y,对应前面定义的base64_table数组)

    第二个编码字符

    current[0] & 0x03<<4为16

    current[1] >> 4为6(01100010>>4等于00000110等于6)

    所以第二个编码字符是W(base64_table[22]等于W)

    第三个编码字符

    current[1] & 0x0f为2(01100010&00001111等于00000010等于2)

    (current[1] & 0x0f) << 2为8(00000010<<2等于00001000等于8)

    所以第三个编码字符是J(base64_table[9]等于J)

    第四个编码字符

    current[2] & 0x3f为35(01100011&00111111等于00100011等于35)

    所以第四个编码字符为j(base64_table[35]等于j)

    最后current指针加3到''字符,length-3等于0,终止了while循环

    此时的编码字符是YWJj(如果字符串是‘abcabcabc’这样的,那么编码字符串是YWJjYWJjWYJj,因为current移动3个字符执行的都是同样的算法)

    我们继续往下看

    由于length-3后变成了0

    所以

    //我命名为第二代码块
    if (length != 0) {
      *p++ = base64_table[current[0] >> 2];
      if (length > 1) {
        *p++ = base64_table[((current[0] & 0x03) << 4) + (current[1] >> 4)];
        *p++ = base64_table[(current[1] & 0x0f) << 2];
        *p++ = base64_pad;
      } else {
        *p++ = base64_table[(current[0] & 0x03) << 4];
        *p++ = base64_pad;
        *p++ = base64_pad;
      }
    }

    是不执行的,最终abc的编码为YWJj,请大家验证

    假如字符串不是abc而是abcd,那么编码为YWJj.........了,我们继续来看上面的我命名的第二代码块

    if(length!=0)//其实length要么等于0,要么等于1,要么等于2

    abcd字符串经过while循环以后length=4-3=1了,current的指针转到d字符

    在我命名的第二代码块中

    *p++ = base64_table[current[0]>>2]的结果为Z

    if(length>1)//false因为length==1

    所以执行else里面的代码

    *p++ = base64_table[(current[0]&0x03)<<]的结果为A

    *p++ = base64_pad的结果为'='

    *p++ = base64_pad的结果为'='

    所以所以abcd字符的编码为"YWJjZA=="

    字符串经过while循环后,3个字符作为一个块编码成4个字符,剩下的字符由if判断,如果剩下一个字符,则为两个编码字符和两个“=”,如果剩下两个字符,则为3个编码字符和一个“=”

    这一点充分证明了我开始所讲的

    感谢大家的耐心阅读,语言组织不到位的地方,欢迎大家通过评论提醒,在下随时更正

  • 相关阅读:
    揭开Socket编程的面纱(留着自己慢慢看)
    XML 新手入门基础知识
    RocketMQ集群平滑下线或重启某个节点
    RocketMQ borker配置文件
    ES:在线迁移集群索引,数据不丢失
    SQL命令汇总
    Redis过期key淘汰策略
    中间件服务器内核参数优化
    在线做RAID命令
    CPU网卡亲和绑定
  • 原文地址:https://www.cnblogs.com/xiaozong/p/5030842.html
Copyright © 2011-2022 走看看