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文件)

我把主要部分代码贴出来

  1. ..........................
  2. static const char base64_table[] = {
  3. 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
  4. 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
  5. 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
  6. 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
  7. '', '', '', '', '', '', '', '', '', '', '+', '/', '\0'
  8. };
  9.  
  10. static const char base64_pad = '=';
  11. .............................................
  12.  
  13. PHPAPI unsigned char *php_base64_encode(const unsigned char *str, int length, int *ret_length) /* {{{ */
  14. {
  15. const unsigned char *current = str;
  16. unsigned char *p;
  17. unsigned char *result;
  18.  
  19. if (length < ) {
  20. if (ret_length != NULL) {
  21. *ret_length = ;
  22. }
  23. return NULL;
  24. }
  25.  
  26. result = (unsigned char *) safe_emalloc((length + ) / , * sizeof(char), );
  27. p = result;
  28.  
  29. while (length > ) { /* keep going until we have less than 24 bits */
  30. *p++ = base64_table[current[] >> ];
  31. *p++ = base64_table[((current[] & 0x03) << ) + (current[] >> )];
  32. *p++ = base64_table[((current[] & 0x0f) << ) + (current[] >> )];
  33. *p++ = base64_table[current[] & 0x3f];
  34.  
  35. current += ;
  36. length -= ; /* we just handle 3 octets of data */
  37. }
  38.  
  39. /* now deal with the tail end of things */
  40. if (length != ) {
  41. *p++ = base64_table[current[] >> ];
  42. if (length > ) {
  43. *p++ = base64_table[((current[] & 0x03) << ) + (current[] >> )];
  44. *p++ = base64_table[(current[] & 0x0f) << ];
  45. *p++ = base64_pad;
  46. } else {
  47. *p++ = base64_table[(current[] & 0x03) << ];
  48. *p++ = base64_pad;
  49. *p++ = base64_pad;
  50. }
  51. }
  52. if (ret_length != NULL) {
  53. *ret_length = (int)(p - result);
  54. }
  55. *p = '\0';
  56. return result;
  57. }
  58.  
  59. ................................................
  60.  
  61. PHP_FUNCTION(base64_encode)
  62. {
  63. char *str;
  64. unsigned char *result;
  65. int str_len, ret_length;
  66.  
  67. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) {
  68. return;
  69. }
  70. result = php_base64_encode((unsigned char*)str, str_len, &ret_length);
  71. if (result != NULL) {
  72. RETVAL_STRINGL((char*)result, ret_length, );
  73. } else {
  74. RETURN_FALSE;
  75. }
  76. }

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)函数对字符串进行编码

这里主要看这段代码

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

我们假设需要编码的字符串为“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到'\0'字符,length-3等于0,终止了while循环

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

我们继续往下看

由于length-3后变成了0

所以

  1. //我命名为第二代码块
  2. if (length != ) {
  3.   *p++ = base64_table[current[] >> ];
  4.   if (length > ) {
  5.     *p++ = base64_table[((current[] & 0x03) << ) + (current[] >> )];
  6.     *p++ = base64_table[(current[] & 0x0f) << ];
  7.     *p++ = base64_pad;
  8.   } else {
  9.     *p++ = base64_table[(current[] & 0x03) << ];
  10.     *p++ = base64_pad;
  11.     *p++ = base64_pad;
  12.   }
  13. }

是不执行的,最终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个编码字符和一个“=”

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

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

php源码分析之base64_encode函数的更多相关文章

  1. SequoiaDB 系列之五 :源码分析之main函数

    好久好久没有写博客了,因为一直要做各种事,工作上的,生活上的,这一下就是半年. 时光如梭. 这两天回头看了看写的博客,感觉都是贻笑大方. 但是还是想坚持把SequoiaDB系列写完. 初步的打算已经确 ...

  2. jQuery 源码分析(五) map函数 $.map和$.fn.map函数 详解

    $.map() 函数用于使用指定函数处理数组中的每个元素(或对象的每个属性),并将处理结果封装为新的数组返回,该函数有三个参数,如下: elems Array/Object类型 指定的需要处理的数组或 ...

  3. jQuery 源码分析(四) each函数 $.each和$.fn.each方法 详解

    $.each一般用来遍历一个数组或对象,$.fn.each()就是指jQuery实例可以执行的操作(因为$.fn是jQuery对象的原型) $.each用来遍历一个数组或对象,并依次执行回掉函数,最后 ...

  4. LiteOS-任务篇-源码分析-删除任务函数

    目录 前言 笔录草稿 源码分析 LOS_TaskDelete函数源码分析 完整源码 参考 链接 前言 20201009 LiteOS 2018 需要会通用链表 笔录草稿 源码分析 LOS_TaskDe ...

  5. 【LiteOS】LiteOS任务篇-源码分析-创建任务函数

    目录 前言 链接 参考 笔录草稿 部分源码分析 源码分析 LOS_TaskCreate函数 LOS_TaskCreateOnly函数 宏 OS_TCB_FROM_PENDLIST 和 宏 LOS_DL ...

  6. twemproxy源码分析1——入口函数及启动过程

    最近工作中需要写一个一致性哈希的代理,在网上找到了twemproxy,结合网上资料先学习一下源码. 一.Twemproxy简介 Twemproxy是memcache与redis的代理,由twitter ...

  7. MySQL源码分析之SQL函数执行

    1.MySQL中执行一条SQL的总体流程 2.SQL函数执行过程 1.MySQL中执行一条SQL的总体流程 一条包含函数的SQL语句,在mysql中会经过: 客户端发送,服务器连接,语法解析,语句执行 ...

  8. 源码分析MySQL mysql_real_query函数

    目录 目录 1 1. 前言 1 2. 调用路径 2 3. MAX_PACKET_LENGTH宏 2 4. DBUG_RETURN宏 3 5. COM_QUERY枚举值 3 6. mysql_query ...

  9. underscore.js 源码分析5 基础函数和each函数的使用

    isArrayLike 检测是数组对象还是纯数组 var property = function(key) { return function(obj) { return obj == null ? ...

随机推荐

  1. centos 安装ftp服务器

    CentOS下搭建FTP服务器简单记录. 1.安装vsftpd yum install vsftpd 2.编辑iptablesvi /etc/sysconfig/iptables -A INPUT - ...

  2. for计算100以内的偶数和

    #include "stdio.h" void main() { ,sum=; ;d++) { ==) { sum=sum+d; } }printf("100以内所有偶数 ...

  3. 杭电OJ分类

    基础题:1000.1001.1004.1005.1008.1012.1013.1014.1017.1019.1021.1028.1029.1032.1037.1040.1048.1056.1058.1 ...

  4. Apriori算法-java

    package com.yang; import java.util.*; public class Apriori { private double minsup = 0.2;// 最小支持度   ...

  5. 《C++ Primer》之面向对象编程(四)

    纯虚函数 在前面所提到过的 Disc_item 类提出了一个有趣的问题:该类从 Item_base 继承了 net_price 函数但没有重定义该函数.因为对 Disc_item 类而言没有可以给予该 ...

  6. openwrt ramips随记

    ar71xx / brcm47xx / brcm63xx / ramips是指cpu的系列,ramips是指ralink系列的

  7. svn revert

    本地开发环境出现一个问题,用revert完美解决. 问题描述: 文件static/image/common/jiqiaodaren.png已经被提交到svn上,但是我的开发环境因未与svn同步,所以没 ...

  8. Puppent 介绍原理及安装

    Puppet原理: Puppet是一个或者多个master,众多client,所有的客户端都定期(默认为30分钟)使用facter工具把 客户端的基本信息,通过https的xmlrpc协议发送给服务器 ...

  9. boost ASIO实例

    client端代码 #include <iostream> #include <boost/asio.hpp> #include <boost/bind.hpp> ...

  10. eclipse配置tomcat及修改tomcat默认根目录

    1.安装eclipse for j2ee和tomcat: 2.下载tomcat对eclipse的插件:http://www.eclipsetotale.com/tomcatPlugin.html 下载 ...