Cpython源码阅读16-Unicode字符串底层存储结构
off999 2024-11-06 11:28 49 浏览 0 评论
底层有一个函数叫Py_Unicode_New的函数,为Unicode字符串申请空间,借助这个函数看一下unicode字符串的内存分布情况。像这样的特性API很多。
PyObject *
PyUnicode_New(Py_ssize_t size, Py_UCS4 maxchar)//这个函数负责为Unicode字符串申请空间
{/* 传入两个参数,size表示字符的个数,多出来一个maxchar;
最大字符,最大字符不同,申请的空间不同*/
/* 对空字符串的优化 */
if (size == 0) {
return unicode_new_empty();//字符个数为0,则不用申请空间
}
PyObject *obj; //所有对象的父类
PyCompactUnicodeObject *unicode;//字符型对象
void *data;//空类型,需要时将数据转化为实际存储的类型
enum PyUnicode_Kind kind;//字符实际编码使用的类型
int is_sharing, is_ascii;
Py_ssize_t char_size; //实际编码需要几个字符
Py_ssize_t struct_size;//PyASCIIObject基本类型结构体的大小
is_ascii = 0;
is_sharing = 0;
struct_size = sizeof(PyCompactUnicodeObject);
//如果maxchar不小于128,基本类型为PyCompactUnicodeObject
//最大字符小于128,并且字符位宽为1个字节,即标准的ASCII可识别的有效字符仅有128个
//此时创建PyASCIIObject对象,字符实际编码使用的类型为PyUnicode_1BYTE_KIND()
if (maxchar < 128) {
kind = PyUnicode_1BYTE_KIND;
char_size = 1;
is_ascii = 1;
struct_size = sizeof(PyASCIIObject);
}
//最大字符大于128小于256,并且字符位宽为1个字节,即标准的ASCII可识别的有效字符仅有128个
//此时创建PyCompactUnicodeObject对象,字符实际编码使用的类型为PyUnicode_1BYTE_KIND()
else if (maxchar < 256) {
kind = PyUnicode_1BYTE_KIND;
char_size = 1;
}
//最大字符大于256小于65536,并且字符位宽为2个字节
//此时创建PyCompactUnicodeObject对象,字符实际编码使用的类型为PyUnicode_2BYTE_KIND()
else if (maxchar < 65536) {
kind = PyUnicode_2BYTE_KIND;
char_size = 2;
if (sizeof(wchar_t) == 2)
is_sharing = 1;
}
//最大字符大于65536,并且字符位宽为4个字节
//此时创建PyCompactUnicodeObject对象,字符实际编码使用的类型为PyUnicode_4BYTE_KIND()
else {
if (maxchar > MAX_UNICODE) {
PyErr_SetString(PyExc_SystemError,
"invalid maximum character passed to PyUnicode_New");
return NULL;
}
kind = PyUnicode_4BYTE_KIND;
char_size = 4;
if (sizeof(wchar_t) == 4)
is_sharing = 1;
}
/*确保字符个数没有溢出. */
if (size < 0) {
PyErr_SetString(PyExc_SystemError,
"Negative size passed to PyUnicode_New");
return NULL;
}
if (size > ((PY_SSIZE_T_MAX - struct_size) / char_size - 1))
return PyErr_NoMemory();
/* 来自_PyObject_New()的重复分配代码,而不是对PyObject_New()的调用,
因此我们能够为对象及其数据缓冲区分配空间。
*/
obj = (PyObject *) PyObject_MALLOC(struct_size + (size + 1) * char_size);
if (obj == NULL) {
return PyErr_NoMemory();
}
//绑定PyUnicode_Type的类型信息
_PyObject_Init(obj, &PyUnicode_Type);
//根据使用的不同结构体头,通过指针偏移找到实际字符串data的开始位置
unicode = (PyCompactUnicodeObject *)obj;
if (is_ascii)
data = ((PyASCIIObject*)obj) + 1;
else
data = unicode + 1;
//设定state内部类的状态信息
_PyUnicode_LENGTH(unicode) = size;
_PyUnicode_HASH(unicode) = -1;
_PyUnicode_STATE(unicode).interned = 0;
_PyUnicode_STATE(unicode).kind = kind;
_PyUnicode_STATE(unicode).compact = 1;
_PyUnicode_STATE(unicode).ready = 1;
_PyUnicode_STATE(unicode).ascii = is_ascii;
//实际data使用的编码字节
if (is_ascii) {
((char*)data)[size] = 0;
_PyUnicode_WSTR(unicode) = NULL;
}
//一个字节
else if (kind == PyUnicode_1BYTE_KIND) {
((char*)data)[size] = 0;
_PyUnicode_WSTR(unicode) = NULL;
_PyUnicode_WSTR_LENGTH(unicode) = 0;
unicode->utf8 = NULL;
unicode->utf8_length = 0;
}
//两个字节
else {
unicode->utf8 = NULL;
unicode->utf8_length = 0;
if (kind == PyUnicode_2BYTE_KIND)
((Py_UCS2*)data)[size] = 0;
//四个字节
else /* kind == PyUnicode_4BYTE_KIND */
((Py_UCS4*)data)[size] = 0;
if (is_sharing) {
_PyUnicode_WSTR_LENGTH(unicode) = size;
_PyUnicode_WSTR(unicode) = (wchar_t *)data;
}
else {
_PyUnicode_WSTR_LENGTH(unicode) = 0;
_PyUnicode_WSTR(unicode) = NULL;
}
}
#ifdef Py_DEBUG
unicode_fill_invalid((PyObject*)unicode, 0);
#endif
assert(_PyUnicode_CheckConsistency((PyObject*)unicode, 0));
return obj;
}
这个函数通过maxchar的不同创建了两个字符串对象,PyASCIIObject和PyCompactUnicodeObject,通过源码可以发现,PyCompactUnicodeObject是继承PyASCIIObject的。整个函数流程为:maxchar小于128,并且字符位宽为1个字节,创建PyASCIIObject对象;maxchar小于256,并且字符位宽为1个字节,创建PyCompactUnicodeObject对象。maxchar小于65536,并且字符位宽为2个字节,创建PyCompactUnicodeObject对象;码位个数大于65536且小于MAX_UNICODE,,创建PyCompactUnicodeObject对象。通过这句代码obj = (PyObject *) PyObject_MALLOC(struct_size + (size + 1) * char_size);实际分配了内存。调用PyObject_INIT(obj, &PyUnicode_Type)函数来将PyUnicode_Type实例绑定到字符串对象的头部。就赋予了字符串对象实际类型和属性。
根据不同的maxchar对应不同的kind
根据不同的kind对应不同的底层结构体,和字符存储单元
typedef struct {
PyObject_HEAD//不可变长对象公用头
Py_ssize_t length; //字符串长度
Py_hash_t hash; /* 字符串哈希值 */
struct {
unsigned int interned:2;//是否interned机制开启
unsigned int kind:3; //字符类型,根据maxchar区分
unsigned int compact:1;//是否紧凑,实际数据域对象头是否分离
unsigned int ascii:1;//是否为纯ASCII
unsigned int ready:1;//针对传统字符串,使用ready函数复制到data块中
unsigned int :24;//保留字段
} state;//Unicode对象标志位
wchar_t *wstr; /* wchar_t representation (null-terminated) */
} PyASCIIObject;
PyASCIIObject内存结构,是纯ASCII字符串通过PyUnicode_New函数申请的。申请时state.ascii和state.compact赋值为1,数据紧跟在头部结构后面,叫做紧凑型。
我们以纯ASCII字符串“hua”,看一下它的底层数据结构
从这个结构可以看出,Unicode字符串应该是可以变长的,但是实际没有使用可变长的PyVarObject头,而是使用了不可变长PyObject头,又增加了length这个字段。PyObject_VAR_HEAD用于描述每个元素大小都一样的变长对象,元素的大小由ob_size字段描述;而Unicode字符串对象,每个字符到底用多大的存储单元,与字符范围(maxchar)决定,底层做了特殊处理。
typedef struct {
PyASCIIObject _base;
Py_ssize_t utf8_length; /* Number of bytes in utf8, excluding thterminating \0. */
char *utf8; /* UTF-8 representation (null-terminated) */
Py_ssize_t wstr_length; /* Number of code points in wstr, possible
* surrogates count as two code points. */
} PyCompactUnicodeObject;
当maxchar大于128小于256 时,虽然一个字节可以存储,此时为非ASCII,Unicode字符串对象由PyCompactUnicodeObject结构体保存。但是它的state中的字段ascii为0,举例图示一下“xiaohuaê”
当maxchar大于256小于65536 时,Unicode字符串对象由PyCompactUnicodeObject结构体保存。但是它的state中的字段ascii为0,state中的字段kind为2举例图示一下“xiaohua”
当maxchar大于65536小于2^32 时,Unicode字符串对象由PyCompactUnicodeObject结构体保存。但是它的state中的字段ascii为0,state中的字段kind为4,举例图示一下“xiaohua+表情符号”
相关推荐
- 下载免费的小说(免费下载小说软件推荐)
-
http://www.ziweishuwu.comhttp://www.txtbook.com.cn/https://www.xiashutxt.com/https://www.jjxs.la/都可以...
- 安装播放器 app下载(安装播放器软件)
-
1.首先,打开浏览器,访问播放器官网,找到下载地址,点击下载。2.点击下载后,会弹出一个提示框,点击“保存”,然后把文件保存到本地磁盘。3.打开保存的文件,双击运行安装程序,按照提示安装播放器。4.安...
- 游戏蜂窝(游戏蜂窝免root)
-
人人蜂窝和游戏蜂窝有以下几点区别:1.目标用户不同:人人蜂窝是一家提供移动网络服务的运营商,主要面向一般用户提供通信服务;而游戏蜂窝是一个游戏信息平台,主要为游戏爱好者提供游戏相关资讯和社交互动。2...
- 正版win7旗舰版官网(win7旗舰版官方)
-
从来就没有win7官网这样的说法,这是因为win7本身就是微软公司旗下产品,是Windows系统的一个版本而已,并不存在win7官网,当然主要的相关资源还是可以到微软官网去查找下载。首先,官网下载的W...
- 打米传奇手游可提现(打米传奇手游怎么提现)
-
个人感觉有些传奇游戏还是可以提现的,也就是现在所谓的搬砖服,不过想要提现也是需要付出的,普通的游戏玩家一天可以得到的收益并不是很高。想要获得高额收益是需要投资的。个人建议投资之前最好先观望一下,免的造...
- 虚拟号码发送短信平台(虚拟手机号收短信平台)
-
用虚拟手机号给别人发送短信的方法如下1、下载安装定时达人软件(安卓手机端),进入首页,点击下部的“添加新任务”,左边选择“通信”项。2、这时就可以看到右边的“虚拟电话”和“虚拟短信”选项。3、点击进入...
- 免费阅读软件(一念永恒小说免费阅读软件)
-
追书免费全本小说、追书神器免费版、易追书、全本追书阅读器等软件都是比较好用的免费读书软件。具体介绍如下: 1、追书免费全本小说,免费阅读热门网络小说; 2、追书神器免费版,有海量的书库,更...
- qq聊天软件免费下载安装(qq聊天应用下载)
-
首先右击你想下载的聊天记录的好友头像,这时把鼠标放到“聊天记录”上,你会看到三个选项,点击“查看聊天记录”,进入“信息管理器”界面。界面分为两部分,左边是你的好友名单,右边是详细的聊天记录,同...
- 下载官方浏览器(下载官方浏览器安装到桌面)
-
当我们在使用ie浏览器时或是操作电脑时,由于偶尔的不小心或是误点了某些安全提示,可能会导致ie浏览器无法使用下载功能,而导致ie浏览器无法下载文件和软件。首先我们来确认是安全软件阻止还是防火墙阻止还是...
- 万能种子搜索神器app(万能种子搜索神器2.9.9)
-
你好,万能播放器是一款功能强大的音视频播放软件,通常需要购买激活码或者注册码来激活使用。具体的激活方式可能因软件版本和开发者而异,以下是一般的激活步骤:1.购买激活码:前往官方网站或授权渠道购买激活...
- word免费版电脑版(word office免费版)
-
不同版本也不一样1.目前电脑中office365是订阅制,没有永久免费的政策。office2016与office2019可以永久激活使用。2、永久激活版本在电脑中只要自带office2016或者off...
- 可以免费下载音乐的app(可以免费下载音乐的软件并传到U盘)
-
目前市场上有不少免费的音乐下载软件,比如QQ音乐、网易云音乐等。这些免费的音乐下载软件一般会提供不同种类的音乐,从流行歌曲到经典老歌都可以找到,除此之外,这些软件还会针对用户进行推荐,根据用户的音乐口...
- office2007免安装绿色版(office2007绿色完整版)
-
把你的绿色版删除!附件的压缩包里有微软提供的卸载程序!运行一遍就可以了!office2007哪个版本好?针对office2007哪个版本好,office2007的版本主要有office2007官方下载...
- 魔兽官方对战平台(魔兽官方对战平台什么时候回归)
-
魔兽争霸的对战平台每个时间段都不一样。以前最后的就是浩方队员平台,还有QQ对战平台,后来又出来了vs对战平台。其中开始的浩方平台高手多,后来vs平台出来后大量高手都跑过去了,QQ平台一般菜鸟比较多。...
- 三国策略手游(三国策略手游试玩)
-
率土之滨官方版是一款超级好玩的全新策略三国竞技冒险手游,高清质风格的游戏画面能够带你体验全新的策略战斗,高度自由的竞技玩法,玩家可以在游戏中打造属于自己的三国世界1、《极无双》:3D动作军团征战手游《...
欢迎 你 发表评论:
- 一周热门
-
-
抖音上好看的小姐姐,Python给你都下载了
-
全网最简单易懂!495页Python漫画教程,高清PDF版免费下载
-
飞牛NAS部署TVGate Docker项目,实现内网一键转发、代理、jx
-
Python 3.14 的 UUIDv6/v7/v8 上新,别再用 uuid4 () 啦!
-
python入门到脱坑 输入与输出—str()函数
-
Python三目运算基础与进阶_python三目运算符判断三个变量
-
(新版)Python 分布式爬虫与 JS 逆向进阶实战吾爱分享
-
失业程序员复习python笔记——条件与循环
-
系统u盘安装(win11系统u盘安装)
-
Python 批量卸载关联包 pip-autoremove
-
- 最近发表
- 标签列表
-
- python计时 (73)
- python安装路径 (56)
- python类型转换 (93)
- python进度条 (67)
- python吧 (67)
- python的for循环 (65)
- python格式化字符串 (61)
- python静态方法 (57)
- python列表切片 (59)
- python面向对象编程 (60)
- python 代码加密 (65)
- python串口编程 (77)
- python封装 (57)
- python写入txt (66)
- python读取文件夹下所有文件 (59)
- python操作mysql数据库 (66)
- python获取列表的长度 (64)
- python接口 (63)
- python调用函数 (57)
- python多态 (60)
- python匿名函数 (59)
- python打印九九乘法表 (65)
- python赋值 (62)
- python异常 (69)
- python元祖 (57)
