结构

LimitedReader

定义

限制从Reader中读取的字节数。

type LimitedReader struct {
R Reader // underlying reader
N int64 // max bytes remaining
}

方法

//Limited
func (l *LimitedReader) Read(p []byte) (n int, err error) {
//已经读完了返回EOF
if l.N <= 0 {
return 0, EOF
}
//限制读取的长度
if int64(len(p)) > l.N {
p = p[0:l.N]
}
//直接调用Reader的Read
n, err = l.R.Read(p)
//更新N,保证一个LimitReader限制字节数
l.N -= int64(n)
return
}

SectionReader

定义

实现了对底层满足ReadAt接口的输入流某个片段的Read、ReadAt、Seek方法.

type SectionReader struct {
r ReaderAt
base int64 //读取的起始偏移
off int64 //读取时的指针
limit int64 //最终位置
}

方法


//读
func (s *SectionReader) Read(p []byte) (n int, err error) {
//超过limit返回EOF
if s.off >= s.limit {
return 0, EOF
}
//计算最大可以读取的长度,进而修改slice
if max := s.limit - s.off; int64(len(p)) > max {
p = p[0:max]
}
//从off开始读取len(p)个字节
n, err = s.r.ReadAt(p, s.off)
s.off += int64(n)
return
} //跳转后的偏移能大于limit吗??
//Seeker
//返回跳转后与起始位置的偏移
func (s *SectionReader) Seek(offset int64, whence int) (int64, error) {
switch whence {
default:
return 0, errWhence
case SeekStart: //SeekStart = 0 // 相对起始位置跳转
offset += s.base
case SeekCurrent: //SeekCurrent = 1 // 相对当前位置跳转
offset += s.off
case SeekEnd: //SeekEnd = 2 // 相对最后位置跳转
offset += s.limit
}
//跳转后偏移必须大于起始位置
if offset < s.base {
return 0, errOffset
}
s.off = offset
return offset - s.base, nil
} //off为相对base偏移量
func (s *SectionReader) ReadAt(p []byte, off int64) (n int, err error) {
//不能大于Size()
if off < 0 || off >= s.limit-s.base {
return 0, EOF
}
//设置ReadAt的初始位置,该off不能影响SectionReader.off
off += s.base
//计算可以读取的长度
if max := s.limit - off; int64(len(p)) > max {
p = p[0:max]
n, err = s.r.ReadAt(p, off)
//此时len(p)>max,因此需要设置文件结束的err
if err == nil {
err = EOF
}
return n, err
}
//可以直接读取
return s.r.ReadAt(p, off)
} func (s *SectionReader) Size() int64 { return s.limit - s.base }

teeReader

定义

私有结构,需要通过TeeReader函数创建一个teeReader,使读取r的数据之前均写入到w中

type teeReader struct {
r Reader
w Writer
}

方法

func (t *teeReader) Read(p []byte) (n int, err error) {
//从r中读数据
n, err = t.r.Read(p)
if n > 0 {
//想w写入数据
if n, err := t.w.Write(p[:n]); err != nil {
return n, err
}
}
return
}

multiReader

定义

通过MultiReader创建,从多个Reader中连续读取

type multiReader struct {
readers []Reader
}

方法

//实现Reader接口
//从readers中按顺序读取数据
func (mr *multiReader) Read(p []byte) (n int, err error) { for len(mr.readers) > 0 {
// Optimization to flatten nested multiReaders (Issue 13558).
//嵌套multiReader
if len(mr.readers) == 1 {
if r, ok := mr.readers[0].(*multiReader); ok {
mr.readers = r.readers
continue
}
}
n, err = mr.readers[0].Read(p)
//判断当前Reader是否读完
if err == EOF {
//释放对已读完的Reader的引用
mr.readers[0] = eofReader{} // permit earlier GC
//剔除第一个元素
mr.readers = mr.readers[1:]
}
//如果readers还有则不能返回EOF
if n > 0 || err != EOF {
if err == EOF && len(mr.readers) > 0 {
// Don't return EOF yet. More readers remain.
err = nil
}
return
}
}
return 0, EOF
}

multiWriter

定义

通过MultiWriter创建,每次写入数据会同时写入到这一组Writer

type multiWriter struct {
writers []Writer
}

方法

func (t *multiWriter) Write(p []byte) (n int, err error) {
//循环写入
for _, w := range t.writers {
n, err = w.Write(p)
//某个报错则返回
if err != nil {
return
}
//必须将p都写入
if n != len(p) {
err = ErrShortWrite
return
}
}
return len(p), nil
}
//写字符串
func (t *multiWriter) WriteString(s string) (n int, err error) {
var p []byte // lazily initialized if/when needed
for _, w := range t.writers {
//判断是否实现了stringWriter接口
if sw, ok := w.(stringWriter); ok {
n, err = sw.WriteString(s)
} else {
//将string转为slice
if p == nil {
p = []byte(s)
}
//调用Write
n, err = w.Write(p)
}
if err != nil {
return
}
//所有Writer必须都写完
if n != len(s) {
err = ErrShortWrite
return
}
}
return len(s), nil
}

pipe

定义

PipeReader和PipeWriter的底层实现

type pipe struct {
rl sync.Mutex // gates readers one at a time
wl sync.Mutex // gates writers one at a time
l sync.Mutex // protects remaining fields
data []byte // data remaining in pending write
rwait sync.Cond // waiting reader
wwait sync.Cond // waiting writer
rerr error // if reader closed, error to give writes
werr error // if writer closed, error to give reads
}

方法

func (p *pipe) read(b []byte) (n int, err error) {
// One reader at a time.
//上Reader的锁
p.rl.Lock()
defer p.rl.Unlock()
//锁其他字段
p.l.Lock()
defer p.l.Unlock()
//死循环直到有数据可读了
for {
//reader关闭了
if p.rerr != nil {
return 0, ErrClosedPipe
}
//可以读数据了
if p.data != nil {
break
}
//writer关闭了
if p.werr != nil {
return 0, p.werr
}
//阻塞读
p.rwait.Wait()
}
//读数据
n = copy(b, p.data)
p.data = p.data[n:]
//pipe中数据读完了则可以notify Writer
if len(p.data) == 0 {
p.data = nil
p.wwait.Signal()
}
return
} func (p *pipe) write(b []byte) (n int, err error) {
// pipe uses nil to mean not available
if b == nil {
b = zero[:]
} // One writer at a time.
p.wl.Lock()
defer p.wl.Unlock() p.l.Lock()
defer p.l.Unlock()
//Writer关闭抛异常
if p.werr != nil {
err = ErrClosedPipe
return
}
//写入数据
p.data = b
//notify Reader
p.rwait.Signal()
//阻塞,直到Reader读完
for {
if p.data == nil {
break
}
if p.rerr != nil {
err = p.rerr
break
}
if p.werr != nil {
err = ErrClosedPipe
break
}
p.wwait.Wait()
}
n = len(b) - len(p.data)
p.data = nil // in case of rerr or werr
return
}

PipeReader/PipeWriter

定义

通过Pipe()函数创建(*PipeReader, *PipeWriter)

type PipeReader struct {
p *pipe
}
type PipeWriter struct {
p *pipe
}

方法

  • Write,调用pipe.write
  • Read,调用pipe.read

Golang标准库——io-结构的更多相关文章

  1. Golang 标准库log的实现

      原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://gotaly.blog.51cto.com/8861157/1406905 前 ...

  2. golang 标准库间依赖的可视化展示

    简介 国庆看完 << Go 语言圣经 >>,总想做点什么,来加深下印象.以可视化的方式展示 golang 标准库之间的依赖,可能是一个比较好的切入点.做之前,简单搜了下相关的内 ...

  3. Golang 标准库提供的Log(一)

      原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://gotaly.blog.51cto.com/8861157/1405754 G ...

  4. golang标准库分析之net/rpc

    net/rpc是golang提供的一个实现rpc的标准库.

  5. C 标准库IO缓冲区和内核缓冲区的区别

    1.C标准库的I/O缓冲区          UNIX的传统 是Everything is a file,键盘.显示器.串口.磁盘等设备在/dev 目录下都有一个特殊的设备文件与之对应,这些设备文件也 ...

  6. golang 标准库

    前言 不做文字搬运工,多做思路整理 就是为了能速览标准库,只整理我自己看过的...... 最好能看看英文的 标准库 fmt strconv string 跳转 golang知识库总结

  7. golang中的标准库IO操作

    参考链接 输入输出的底层原理 终端其实是一个文件,相关实例如下: os.Stdin:标准输入的文件实例,类型为*File os.Stdout:标准输出的文件实例,类型为*File os.Stderr: ...

  8. 使用C++/C qsort 标准库对结构体进行快速排序

    C++标准快速排序库qsort进行结构体快速排序 代码如下 #include <stdio.h> #include <stdlib.h> typedef struct { in ...

  9. golang标准库 context的使用

    本文索引 问题引入 context包简介 示例 问题引入 goroutine为我们提供了轻量级的并发实现,作为golang最大的亮点之一更是备受推崇. goroutine的简单固然有利于我们的开发,但 ...

  10. python 标准库 —— io(StringIO)

    0. io流(io stream) 流是一种抽象概念,它代表了数据的无结构化传递.按照流的方式进行输入输出,数据被当成无结构的字节序或字符序列.从流中取得数据的操作称为提取操作,而向流中添加数据的操作 ...

随机推荐

  1. 725. Split Linked List in Parts把链表分成长度不超过1的若干部分

    [抄题]: Given a (singly) linked list with head node root, write a function to split the linked list in ...

  2. Java 后台验证的工具类

    Java 后台验证的工具类 public class ValidationUtil {         //手机号     public static String mobile = "^( ...

  3. eclipse 安装lombok插件

    下载lombok 下载地址:https://projectlombok.org/downloads/lombok.jar 或者访问官网下载  https://projectlombok.org/ 安装 ...

  4. C++中的仿函数

    仿函数:实质就是重载了小括号(),通过类,定义一个对象,对象可以被实例化,具有内存可以存储数据,把需要比较的数据事先给到类对象的成员,这样在比较两个值的时候,可以只传入需要被比较的值即可.因为比较的值 ...

  5. JMD Handy Baby 2 to Decode & Adding New BMW 525 ID46 Key

    Here OBD2TOOL share the guide on how to use JMD Handy Baby II to decode and add new keys for BMW 525 ...

  6. linux之用户密码破解的操作

    一 无引导介质救援模式破解root用户密码 1 启动虚拟用户,在GRUB启动画面停留的那段时间,用上下键选择启动项. 2 用‘e’键进入你选择的启动项 ,然后用上下键将光标移动到“linux16... ...

  7. ABP框架系列之二十:(Dependency-Injection-依赖注入)

    What is Dependency Injection If you already know Dependency Injection concept, Constructor and Prope ...

  8. nginx启动或者重启失败,报错nginx: [error] open() "/usr/local/var/run/nginx.pid" failed (2: No such file or directory)

    第一种方案: 1. 执行命令 :open /usr/local/etc/nginx 打开nginx安装目录 nginx安装目录默认位置有:(找到适合你的) /etc/nginx/nginx.conf, ...

  9. getResource()和getResourceAsStream以及路径问题

    用JAVA获取文件,听似简单,但对于很多像我这样的新人来说,还是掌握颇浅,用起来感觉颇深,大常最经常用的,就是用JAVA的File类,如要取得c:/test.txt文件,就会这样用File file ...

  10. Beta冲刺 (7/7)

    Part.1 开篇 队名:彳艮彳亍团队 组长博客:戳我进入 作业博客:班级博客本次作业的链接 Part.2 成员汇报 组员1:(组长)柯奇豪 过去两天完成了哪些任务 部分代码的整合 编辑文章部分的完成 ...