microPython的源码解析之 stream.c

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


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


这段代码定义了 MicroPython 中流的通用操作方法,包括读写、定位、刷新等。代码中使用了 MicroPython 的内部对象和方法,如 mp_obj_t 类型、mp_stream_p_t 流协议等。此外,还提供了类似 POSIX 的接口,以便将现有的 POSIX 兼容软件移植到 MicroPython 流上。代码中的注释详细说明了每个函数的功能和用法。

#include <string.h>
#include <unistd.h>

#include "py/objstr.h"
#include "py/stream.h"
#include "py/runtime.h"

// 此文件定义了通用的 Python 流读写方法,这些方法分派到对象的底层流接口。

// TODO: 应该在 mpconfig.h 中定义
#define DEFAULT_BUFFER_SIZE 256

// 读取流中的所有数据
static mp_obj_t stream_readall(mp_obj_t self_in);

// 返回 *errcode 非零时的错误条件,如果有,返回发生错误之前的字节数。
// 如果 *errcode == 0,返回总字节数写入。
mp_uint_t mp_stream_rw(mp_obj_t stream, void *buf_, mp_uint_t size, int *errcode, byte flags) {
   
    byte *buf = buf_;
    // 定义读写函数类型
    typedef mp_uint_t (*io_func_t)(mp_obj_t obj, void *buf, mp_uint_t size, int *errcode);
    io_func_t io_func;
    const mp_stream_p_t *stream_p = mp_get_stream(stream);
    // 根据标志确定是读取还是写入操作
    if (flags & MP_STREAM_RW_WRITE) {
   
        io_func = (io_func_t)stream_p->write;
    } else {
   
        io_func = stream_p->read;
    }

    *errcode = 0;
    mp_uint_t done = 0;
    while (size > 0) {
   
        mp_uint_t out_sz = io_func(stream, buf, size, errcode);
        // 对于读取,out_sz == 0 表示 EOF。对于写入,它的具体含义是不确定的,
        // 但我们没有取得任何进展,所以返回仍然是最佳选择。
        if (out_sz == 0) {
   
            return done;
        }
        if (out_sz == MP_STREAM_ERROR) {
   
            // 如果我们在得到 EAGAIN 之前读到了一些数据,不要泄露它
            if (mp_is_nonblocking_error(*errcode) && done != 0) {
   
                *errcode = 0;
            }
            return done;
        }
        if (flags & MP_STREAM_RW_ONCE) {
   
            return out_sz;
        }

        buf += out_sz;
        size -= out_sz;
        done += out_sz;
    }
    return done;
}

// 根据 offset 和 whence 参数进行流定位
mp_off_t mp_stream_seek(mp_obj_t stream, mp_off_t offset, int whence, int *errcode) {
   
    struct mp_stream_seek_t seek_s;
    seek_s.offset = offset;
    seek_s.whence = whence;
    const mp_stream_p_t *stream_p = mp_get_stream(stream);
    mp_uint_t res = stream_p->ioctl(MP_OBJ_FROM_PTR(stream), MP_STREAM_SEEK, (mp_uint_t)(uintptr_t)&seek_s, errcode);
    if (res == MP_STREAM_ERROR) {
   
        return (mp_off_t)-1;
    }
    return seek_s.offset;
}

// 获取流对象并检查是否支持指定的操作,如果不支持则抛出异常
const mp_stream_p_t *mp_get_stream_raise(mp_obj_t self_in, int flags) {
   
    const mp_obj_type_t *type = mp_obj_get_type(self_in);
    if (MP_OBJ_TYPE_HAS_SLOT(type, protocol)) {
   
        const mp_stream_p_t *stream_p = MP_OBJ_TYPE_GET_SLOT(type, protocol);
        // 检查流是否支持读取、写入或ioctl操作
        if (!((flags & MP_STREAM_OP_READ) && stream_p->read == NULL)
            && !((flags & MP_STREAM_OP_WRITE) && stream_p->write == NULL)
            && !((flags & MP_STREAM_OP_IOCTL) && stream_p->ioctl == NULL)) {
   
            return stream_p;
        }
    }
    // 如果流不支持操作,则抛出异常
    mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("stream operation not supported"));
}

// 通用的流读取函数,支持读取指定数量的字节或读取所有数据
static mp_obj_t stream_read_generic(size_t n_args, const mp_obj_t *args, byte flags) {
   
    // 如果未指定读取大小或大小为 -1,则读取所有数据
    mp_int_t sz;
    if (n_args == 1 || ((sz = mp_obj_get_int(args[1])) == -1)) {
   
        return stream_readall(args[0]);
    }

    const mp_stream_p_t *stream_p = mp_get_stream(args[0]);

    // 如果流是文本模式,则需要读取指定数量的 Unicode 字符
    #if MICROPY_PY_BUILTINS_STR_UNICODE
    if (stream_p->is_text) {
   
        // 由于我们没有缓冲,并且流 API 只能读取字节,我们必须按字节读取
        // 并且不能过度读取。如果我们想要 sz 个字符,那么读取 sz 字节永远不会过度读取,
        // 因此我们遵循这种方法,在循环中继续读取,直到我们有足够数量的字符。
        // 这对于只有 ASCII 字符的文本将是 1 次读取,对于有几个非 ASCII 字符的文本
        // 将是大约 2 次读取。对于有很多非 ASCII 字符的文本,这将是非常低效的
        // 在时间和内存上。

        vstr_t vstr;
        vstr_init(&vstr, sz);
        mp_uint_t more_bytes = sz;
        mp_uint_t last_buf_offset = 0;
        while (more_bytes > 0) {
   
            char *p = vstr_add_len(&vstr, more_bytes);
            int error;
            mp_uint_t out_sz = mp_stream_read_exactly(args[0], p, more_bytes, &error);
            if (error != 0) {
   
                vstr_cut_tail_bytes(&vstr, more_bytes);
                if (mp_is_nonblocking_error(error)) {
   
                    // 对于非阻塞流,我们尽可能多地读取。
                    // 如果我们什么也没读到,就像 read() 一样返回 None。
                    // 否则,返回到目前为止读取的数据。
                    // TODO 如果我们只读取了半个非 ASCII 字符怎么办?
                    if (vstr.len == 0) {
   
                        vstr_clear(&vstr);
                        return mp_const_none;
                    }
                    break;
                }
                mp_raise_OSError(error);
            }

            if (out_sz < more_bytes) {
   
                // 完成读取。
                // TODO 如果我们只读取了半个非 ASCII 字符怎么办?
                vstr_cut_tail_bytes(&vstr, more_bytes - out_sz);
                if (out_sz == 0) {
   
                    break;
                }
            }

            // 从刚刚读取的字节中计算字符数
            for (mp_uint_t off = last_buf_offset;;) {
   
                byte b = vstr.buf[off];
                int n;
                if (!UTF8_IS_NONASCII(b)) {
   
                    // 1 字节的 ASCII 字符
                    n = 1;
                } else if ((b & 0xe0) == 0xc0) {
   
                    // 2 字节的字符
                    n = 2;
                } else if (
  • 30
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

openwin_top

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

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

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

打赏作者

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

抵扣说明:

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

余额充值