结构

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. 利用travis自动化构建与部署(文档项目)

    背景 保持网站上文档的最新性有比较重要的意义, travis ci 提供了免费的解决方案,本文基于 latex 构建+ aliyun oss 部署对此作了尝试. 项目链接为 https://travi ...

  2. thinkphp 查表返回的数组,js解析有UNICode编码,解决办法

    public function getDeviceMsg(){ $allDevicesMsg = M("newdevicesstatus")->getField(" ...

  3. js多维数组转一维数组

    1,使用map方法 var arr = [1,[2,[[3,4],5],6]]; function unid(arr){ var arr1 = (arr + '').split(',');//将数组转 ...

  4. 201621123002《JAVA程序设计》第三章学习总结

    1. 本周学习总结 1.1 写出你认为本周学习中比较重要的知识点关键词,如类.对象.封装等 关键词:类 对象 封装 构造函数 this,static,final 1.2 用思维导图或者Onenote或 ...

  5. Spring事务传递

    2018-09-25 @Transactional(propagation=Propagation.NEVER) public void update(){ Session s = sessionFa ...

  6. Vux项目搭建

    1.快速搭建项目模板 因为项目使用vux,所以推荐使用vux官网的airyland/vux2 模板,vue-cli工具是vue项目的搭建脚手架 默认为 webpack2 模板,如果你需要使用webpa ...

  7. Vue中出现Do not use built-in or reserved HTML elements as component id:footer等等vue warn问题

    错误示图: 原因:是因为在本地项目对应文件的<script>中,属性name出现了错误的命名方式,导致浏览器控制台报错! 诸如: name: header .  . name: menu  ...

  8. 5阶m序列

    void echo32(int m) { printf("%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d\n ...

  9. AX_Function

    formrun.owner().GetItemId() if (fr.name() == formstr(inventTransEditDimensions) || fr.name() == form ...

  10. 【ElasticSearch】 安装

    Elasticsearch简介 Elasticsearch 是一个开源的分布式 RESTful 搜索和分析引擎,能够解决越来越多不同的应用场景 官网地址:https://www.elastic.co ...