microPython的源码解析之 objstringio.c

本文聚焦于MicroPython的objstringio.c模块,详细解析其内存中的StringIO对象实现,包括读写操作、I/O控制及核心逻辑。通过对源码的解读,帮助读者理解MicroPython最小内核的实现细节,掌握Python解析器的设计原理。
摘要由CSDN通过智能技术生成

MicroPython 是一种适用于微控制器和其他受限环境的 Python 编程语言的实现。它旨在提供与 Python 3 语言的紧密兼容,同时考虑到内存和计算资源的限制。MicroPython 库是这门语言的核心组成部分,提供了一系列的模块和函数,使得开发者能够在硬件上执行各种任务。
下面将通过系列文章,逐一解读microPython,以便让读者了解掌握microPython的整个核心逻辑.,以便读者可以透过这个Python的最小内核,掌握Python解析器的核心实现逻辑,学习世界上最优秀的源码设计之一.


microPython Python最小内核源码解析
NI-motion运动控制c语言示例代码解析
python编程示例系列 python编程示例系列二
python的Web神器Streamlit


这段代码是MicroPython中StringIO和BytesIO对象的实现,它们提供了在内存中读写字符串或字节序列的功能。代码中包含了对象的创建、读写、I/O控制操作以及本地方法字典的定义。这些对象可以像文件一样使用,但它们操作的是内存中的字符串或字节数据,而不是磁盘文件。

#include <stdio.h>
#include <string.h>

// 引入MicroPython对象和字符串I/O相关的头文件
#include "py/objstr.h"
#include "py/objstringio.h"
#include "py/runtime.h"
#include "py/stream.h"

#if MICROPY_PY_IO

// 如果定义了MICROPY_CPYTHON_COMPAT,则检查StringIO对象是否打开
#if MICROPY_CPYTHON_COMPAT
static void check_stringio_is_open(const mp_obj_stringio_t *o) {
   
    if (o->vstr == NULL) {
   
        mp_raise_ValueError(MP_ERROR_TEXT("I/O operation on closed file")); // 引发值错误,因为尝试对已关闭的文件执行I/O操作
    }
}
#else
#define check_stringio_is_open(o)
#endif

// 打印StringIO对象的内容
static void stringio_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
   
    (void)kind;
    mp_obj_stringio_t *self = MP_OBJ_TO_PTR(self_in);
    mp_printf(print, self->base.type == &mp_type_stringio ? "<io.StringIO 0x%x>" : "<io.BytesIO 0x%x>", self); // 打印对象类型和内存地址
}

// 从StringIO对象中读取数据
static mp_uint_t stringio_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) {
   
    (void)errcode;
    mp_obj_stringio_t *o = MP_OBJ_TO_PTR(o_in);
    check_stringio_is_open(o); // 检查对象是否打开
    if (o->vstr->len <= o->pos) {
     // 如果读取到文件末尾或之后,则返回0
        return 0;
    }
    mp_uint_t remaining = o->vstr->len - o->pos;
    if (size > remaining) {
   
        size = remaining; // 调整读取大小以不超过剩余内容
    }
    memcpy(buf, o->vstr->buf + o->pos, size); // 将数据从内部缓冲区复制到提供的缓冲区
    o->pos += size; // 更新读取位置
    return size; // 返回实际读取的字节数
}

// 在写操作时复制字符串I/O对象的内容,以便可以修改它
static void stringio_copy_on_write(mp_obj_stringio_t *o) {
   
    const void *buf = o->vstr->buf;
    o->vstr->buf = m_new(char, o->vstr->len); // 分配新的内存
    o->vstr->fixed_buf = false; // 标记为非固定缓冲区
    o->ref_obj = MP_OBJ_NULL; // 清除引用对象
    memcpy(o->vstr->buf, buf, o->vstr->len); // 复制原始数据
}

// 向StringIO对象写入数据
static mp_uint_t stringio_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) {
   
    (void)errcode;
    mp_obj_stringio_t *o = MP_OBJ_TO_PTR(o_in);
    check_stringio_is_open(o); // 检查对象是否打开

    if (o->vstr->fixed_buf) {
   
        stringio_copy_on_write(o); // 如果是固定缓冲区,则先复制内容
    }

    mp_uint_t new_pos = o->pos + size; // 计算新的写入位置
    if (new_pos < size) {
   
        // 如果写入大小导致位置溢出,则返回错误代码
        *errcode = MP_EFBIG;
        return MP_STREAM_ERROR;
    }
    mp_uint_t org_len = o->vstr->len;
    if (new_pos > o->vstr->alloc) {
   
        // 如果新位置超出了已分配的内存,则重新分配更多内存
        vstr_add_len(o->vstr, new_pos - o->vstr->alloc);
    }
    // 如果之前已经移动到了文件末尾之后,将中间的空洞用零填充
    if (o->pos > org_len) {
   
        memset(o->vstr->buf + org_len, 0, o->pos - org_len);
    }
    memcpy(o->vstr->buf + o->pos, buf, size); // 将数据写入内部缓冲区
    o->pos = new_pos; // 更新写入位置
    if (new_pos > o->vstr->len) {
   
        o->vstr->len = new_pos; // 更新字符串长度
    }
    return size; // 返回实际写入的字节数
}

// 执行I/O控制操作,如移动读写位置、刷新缓冲区、关闭文件等
static mp_uint_t stringio_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) {
   
    (void)errcode;
    mp_obj_stringio_t *o = MP_OBJ_TO_PTR(o_in);
    switch (request) {
   
        case MP_STREAM_SEEK: {
   
            struct mp_stream_seek_t *s = (struct mp_stream_seek_t *)arg;
            mp_uint_t ref = 0;
            switch (s->whence) {
   
                case MP_SEEK_CUR:
                    ref = o->pos; // 从当前位置移动
                    break;
                case MP_SEEK_END:
                    ref = o->vstr->len; // 从文件末尾移动
                    break;
            }
            mp_uint_t new_pos = ref + s->offset; // 计算新的位置

            // 检查新位置是否有效
            if (s->whence != MP_SEEK_SET && s->offset < 0) {
   
                if (new_pos > ref) {
   
                    new_pos = 0; // 如果移动后的位置小于0,则重置为0
                }
            } else if (new_pos < ref) {
   
                *errcode = MP_EINVAL; // 如果移动后的位置大于当前位置,则返回错误代码
                return MP_STREAM_ERROR;
            
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

openwin_top

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值