DjanFey的基础库解读--io包

DjanFy · · 640 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

// io包提供了原始I/O的基础接口
// 它的主要工作是去包含这些原始I/O已经存在的实现
// 比如在os包中的这些,抽象出功能做成共享接口,加上其它相关的原始I/O

// 因为这些接口和原始I/O包裹着各种各样的实现的低级操作,除非非常了解客户端否则不能假定他们是并发安全的
package my_io

import "errors"

// Seek值的来源
const (
   SeekStart   = 0 //seek对应文件的起始处
   SeekCurrent = 1 // seek对应当前的偏距
   SeekEnd     = 2 // seek对应结尾
)

// ErrShortWrite意味着一个write接收到少于要求字节数的数据,并没有返回一个明确的错误
var ErrShortWrite = errors.New("short write")

// ErrShortBuffer意味着一个read需要一个比提供更长的buffer
var ErrShortBuffer = errors.New("short buffer")

// EOF是Read没有更多输入的时候返回的一个错误
// 函数只有要发送一个优雅结束输入的信号时返回一个EOF
// 如果EOF在一个结构化数据流中意外发生,那么合适的错误是返回ErrUnexpectedEOF或给出更多详细信息的其它错误
var EOF = errors.New("EOF")

// ErrUnexpectedEOF意味着在要读取一个固定大小的数据块或者数据结构的中途遇到了EOF
var UnExpectedEOF = errors.New("unexpected EOF")

// ErrNoProgress是io.Reader的一些客户端在多次调用Read却没有返回任何数据和错误时返回的,通常标志着io.Reader错误的实现。
var ErrNoProgress = errors.New("multiple Read calls return no data or error")

// Reader是包含着基础Read方法的接口
// Read读取长度为len(p)的字节到p中。它返回读取到的字节数以及遇到的任何错误(0<=n<=len(p))
// 即使返回的字节数少于len(p),在调用过程中也会使用整个p作为暂存空间
// **** 如果可读取的字节数少于len(p),按照惯例会返回可读取到的字节而不是等待更多。这和ReadAt是不同的
//
// 当在成功读取n>0个字节后遇到错误或者读到文件结尾的情况,返回读取到的字节数以及可能返回非空的错误。或者从下次调用中返回n=0个字节数以及非空的错误。
// 一个具体的案例是:一个Reader在输入流结束的时候返回非零的字节数的同时,可能返回err==EOF或者err==nil。下一次调用会返回0,EOF
//
// 调用者在考虑错误前应先处理读取到的字节。这样做能正确处理读取了一些字节和允许的EOF行为后发生的I/O错误。
//
// 返回0字节并返回空的错误是不友好的,除非len(p)==0。调用者应该忽视这种情况当做什么都没发生。特别注意这并不意味着EOF。
//
// 所有实现一定不能保留p
type Reader interface {
   Read(p []byte) (n int, err error)
}

// Writer是包含基础Write的接口
//
// Write把p中len(p)个字节写入到潜在的数据流中。它返回从p中写入的字节数(0<=n<=len(p))和造成写入动作提早结束的错误。
// Write在返回字节数n<len(p)的情况下必须返回一个非空的错误
// 我们一定不能修改切片数据,即使是短暂的也不可以。
//
// 所有实现一定不能保留p
type Writer interface {
   Write(p []byte) (n int, err error)
}

// Closer是包含着基本Close方法的接口
//
// 在首次调用后的Close行为是未定义的。
// 具体的实现可以查阅它们自己行为的文档
type Closer interface {
   Close() error
}

// Seeker是包含着基本Seek方法的接口
//
// Seek为下一次读取或写入设置偏距,具体解释看whence:
// SeekStart表示文件的开始
// SeekCurrent表示当前的偏距
// SeekEnd表示结尾
// Seek返回相对于开始的新的偏距和一个错误(如果有的话)
// 设置偏距到文件开始之前是错误的
// 设置任何位置的偏距是合法的,但是接下来的在潜在的对象上的I/O操作行为是依赖于实现的。
type Seeker interface {
   Seek(offset int64, whence int) (int64, error)
}

// ReadWriter是组合基础Read和Write方法的接口
type ReadWriter interface {
   Reader
   Writer
}

// ReadCloser是组合基础Read和Close方法的接口
type ReadCloser interface {
   Reader
   Closer
}

// WriterCloser是组合基础Write和Close方法的接口
type WriteCloser interface {
   Writer
   Closer
}

// ReadWriteCloser是组合基础Read,Write和Close方法的接口
type ReadWriteCloser interface {
   Reader
   Writer
   Closer
}

// ReadSeeker是组合基础Read和Seek方法的接口
type ReadSeeker interface {
   Reader
   Seeker
}

// ReadWriteSeeker是组合基础Read,Write和Seek方法的接口
type ReadWriteSeeker interface {
   Reader
   Writer
   Seeker
}

// ReaderFrom是包含着ReadFrom方法的接口
//
// ReadFrom从r中读取数据直到遇到EOF或者错误
// n是从r中读取到的字节数
// 在读取过程中,任何错误都期望是io.EOF,并且它也会返回
//
// Copy函数如果可以的话会使用ReaderFrom
type ReaderFrom interface {
   ReadFrom(r Reader) (n int64, err error)
}

// WriterTo是包含着WriteTo方法的接口
//
// WriteTo是把数据写入到w中直到没有数据可写或者发生错误。返回的数字是已经写入的字节数。在写入过程中遇到的任何错误也会被返回。
// Copy函数会优先使用WriterTo
type WriterTo interface {
   WriteTo(w Writer) (n int64, err error)
}

// ReaderAt是包含着ReadAt方法的接口
// ReadAt在潜在的输入源从偏距off开始读取len(p)个字节到p中。返回读取到的字节数(0<=n<=len(p))和任何遇到的错误。
//
// 当ReadAt返回的字节数n<len(p)时,也会返回一个错误来解释为什么没有更多的字节返回,从这方面来说,ReadAt是比Read要严格的
//
// 即使ReadAt可读取到的字节少于len(p),在调用过程中也会使用整个p作为暂存空间
// ***如果有可以读取的字节但是数量少于len(p),ReadAt会阻塞,直到要么读取到所有数据,要么有错误发生。这点和Read是不同的。
//
// 如果ReadAt在输入源结尾返回len(p)个字节,ReadAt应要么返回err==EOF或者err==nil
//
// 如果ReadAt正在用一个seek offset从输入源读取数据,ReadAt既不应该影响潜在的seek offset,也不应该被潜在的seek offset影响
//
// ReadAt的客户端在相同的输入源可以并行地调用ReadAt
//
// 所有的实现不能保留p
type ReaderAt interface {
   ReadAt(p []byte, off int64) (n int, err error)
}

// WriterAt是一个包含着基础WriteAt方法的接口
//
// WriteAt从p将len(p)个字节在off位置写入到潜在的数据流中
// 它返回从p中写入的字节数和造成写入动作提前结束的错误
// WriteAt如果返回的字节数小于len(p),则必须返回一个非空的错误
//
// 如果WriteAt正在用一个seek偏距写入到目标中,WriteAt既不应该影响潜在的seek偏距也不应该被其影响
// 如果范围没有重叠的话,在同一个目标上,WriteAt的客户端可以并发地调用WriteAt方法
//
// 所有实现一定不能保留p
type WriterAt interface {
   WriteAt(p []byte, off int64) (n int, err error)
}

// ByteReader是一个包含ReadByte方法的接口
//
// ReadByte从输入中读取并返回下一个字节和遇到的错误
// 如果返回了一个错误,那么将不会从输入中消费一个字节,并且返回的字节值是未定义的
type ByteReader interface {
   ReadByte() (byte, error)
}

// ByteScanner是一个把UnreadByte方法和基础ReadByte方法加到一起的接口
//
// UnreadByte使下一次对ReadByte的调用跟前一次调用一样返回相同的字节
// 如果中间没有调用ReadByte而连续调用两次UnreadByte的话可能会发生错误
type ByteScanner interface {
   ByteReader
   UnreadByte() error
}

// ByteWriter是一个包含着WriteByte方法的接口
type ByteWriter interface {
   WriteByte(c byte) error
}

// RuneReader是一个包含着ReadRune方法的接口
//
// ReadRune读取单个UTF-8编码的Unicode字符并将这个rune返回并且返回它的字节大小
// 如果没有可得到的字符,将会设置错误
type RuneReader interface {
   ReadRune() (r rune, size int, err error)
}

// RuneScanner是一个将UnreadRune方法和基础的ReadRune方法加在一起的接口
//
// UnreadRune造成下一次对ReadRune的调用和前一次对ReadRune的调用一样返回相同的rune
// 连续两次调用UnreadRune而中间不调用ReadRune可能会报错
type RuneScanner interface {
   RuneReader
   UnreadRune() error
}

// StringWriter是一个包含WriteString方法的接口
type StringWriter interface {
   WriteString(s string) (n int, err error)
}

// WriteString将字符串s的内容写入到w(w接收字节切片)
// 如果w实现了StringWriter接口,它的WriteString方法将被直接调用
// 否则w.Write被精确调用一次
func WriteString(w Writer, s string) (n int, err error) {
   if sw, ok := w.(StringWriter); ok {
      return sw.WriteString(s)
   }
   return w.Write([]byte(s))
}

// ReadAtLeast从r中读取数据到buf直到至少已经读取了min个字节
// 它返回复制的字节数以及如果读取了少于min个字节时的错误
// 只有当没有字节可读取的时候error才为EOF
// 如果min大于len(buf),ReadAtLeast返回ErrShortBuffer错误
// 对于返回结果,当且仅当err==nil时,n>=min
// 如果r返回了一个错误但是已读取的字节大于min,那么这个错误将被丢弃
func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error) {
   if len(buf) < min {
      return 0, ErrShortBuffer
   }
   for n < min && err == nil {
      var nn int
      nn, err = r.Read(buf[n:])
      n += nn
   }
   if n >= min {
      err = nil
   } else if n > 0 && err == EOF {
      err = UnExpectedEOF
   }
   return
}

// ReadFull从r中读取精确len(buf)个字节到buf
// 它返回复制的字节数和一个如果读取了少于len(buf)个字节的错误
// 只有当没有字节可读的时候错误才会为EOF
// 如果只读取了部分字节而不是len(buf)个字节就发生了EOF,那么ReadFull会返回UnexpectedEOF错误
// 在返回上,当且仅当err==nil的时候,n==len(buf)
// 如果在读取过程中已经读了至少len(buf)个字节然后遇到了错误,那么这个错误将被丢弃
func ReadFull(r Reader, buf []byte) (n int, err error) {
   return ReadAtLeast(r, buf, len(buf))
}

// CopyN从src拷贝n个字节(或者遇到一个错误)到dst
// 它返回拷贝的字节数和在拷贝过程中最先遇到的错误
// 在返回上,当且仅当err==nil的时候,written==n
//
// 如果dst实现了ReadFrom接口,copy会用它来实现
func CopyN(dst Writer, src Reader, n int64) (written int64, err error) {
   written, err = Copy(dst, LimitReader(src, n))
   if written == n {
      return n, nil
   }
   if written < n && err == nil {
      // 就算src提前停止,也必须是EOF
      err = EOF
   }
   return
}

// Copy从src中拷贝数据到dst,直到src遇到EOF或者拷贝期间发生一个错误
// 它返回已经拷贝的字节数和在拷贝时遇到的第一个错误(如果有的话)
//
// 一次成功的拷贝是返回err==nil而不是err==EOF
// 因为Copy本来就是被定义成从src中读取直到EOF,它不会把EOF当做一个错误上报
//
// 如果src实现了WriterTo接口,那么copy是靠调用WriteTo(dst)方法来实现
// 否则如果dst实现了ReaderFrom接口,那么copy是靠调用ReadFrom(src)方法来实现
func Copy(dst Writer, src Reader) (written int64, err error) {
   return copyBuffer(dst, src, nil)
}

// CopyBuffer和Copy是一样的除了它是通过提供的buf来组织(如果是必须的话)而不是自动分配一个临时的buf。
// 如果buf是nil的话,会自动分配一个
// 否则如果len(buf)为0的话,会panic
func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
   if buf != nil && len(buf) == 0 {
      panic("empty buffer in io.CopyBuffer")
   }
   return copyBuffer(dst, src, buf)
}

// copyBuffer是Copy和CopyBuffer的具体实现
// 如果buf为nil,那么自动分配一个
func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
   // 如果reader实现了WriteTo方法,用它来做copy,避免一次分配和拷贝
   if wt, ok := src.(WriterTo); ok {
      return wt.WriteTo(dst)
   }
   // 相似的,如果Writer实现ReadFrom方法,那么就用它来实现copy
   if rf, ok := dst.(ReaderFrom); ok {
      rf.ReadFrom(src)
   }
   // 如果没有buf,则创建一个
   if buf == nil {
      // 默认是3kb
      size := 32 * 1024
      // 如果src是*LimitedReader类型并且size比l.N大,则根据l.N的情况调整size
      if l, ok := src.(*LimitedReader); ok && int64(size) > l.N {
         if l.N < 1 {
            size = 1
         } else {
            size = int(l.N)
         }
      }
      // 根据最终size创建buf
      buf = make([]byte, size)
   }
   // 循环读取写入作为复制的实现
   for {
      // 先从Reader中读取
      nr, er := src.Read(buf)
      // 在错误处理之前先处理已经读取到的字节
      if nr > 0 {
         // 把已经读到的字节写入
         nw, ew := dst.Write(buf[:nr])
         // 如果写入了数据则更新written
         if nw > 0 {
            written += int64(nw)
         }
         // 如果写入发生错误,则中断复制,并将错误置为ew
         if ew != nil {
            err = ew
            break
         }
         // 如果写入的字节数少于读取的字节数,则将错误置为ErrShortWrite错误并中断复制
         if nw < nr {
            err = ErrShortWrite
            break
         }
      }
      // 如果读取有错误
      if er != nil {
         // 并且er!=EOF,则返回错误
         if er != EOF {
            err = er
         }
         break
      }
   }
   return
}

// LimitReader返回一个从r中读取,但是在读取n个字节后停止并返回EOF
// 潜在的实现是一个*LimitedReader
func LimitReader(r Reader, n int64) Reader {
   return &LimitedReader{
      R: r,
      N: n,
   }
}

// 一个LimitedReader从R中读取但是限制数据量只到N字节
// 每次对Read的调用要更新N以反映最新剩余可以读取的字节数
// 当N<=0或者潜在的R返回EOF时,Read会返回EOF
type LimitedReader struct {
   R Reader // 潜在的Reader
   N int64  // 保存的最大字节数
}

func (l *LimitedReader) Read(p []byte) (n int, err error) {
   if l.N <= 0 {
      return 0, EOF
   }
   if int64(len(p)) > l.N {
      p = p[0:l.N]
   }
   n, err = l.R.Read(p)
   l.N -= int64(n)
   return
}

func NewSectionReader(r ReaderAt, off int64, n int64) *SectionReader {
   return &SectionReader{
      r:     r,
      base:  off,
      off:   off,
      limit: off + n,
   }
}

// SectionReader在一个潜在的ReaderAt的片段上上实现了Read Seek 和ReadAt
type SectionReader struct {
   r     ReaderAt
   base  int64
   off   int64
   limit int64
}

func (s *SectionReader) Read(p []byte) (n int, err error) {
   // 如果偏距大于限制读取的位置,则直接返回EOF
   if s.off >= s.limit {
      return 0, EOF
   }
   if max := s.limit - s.off; int64(len(p)) > max {
      p = p[:max]
   }
   n, err = s.r.ReadAt(p, s.off)
   s.off += int64(n)
   return
}

var errWhence = errors.New("Seek: invalid whence")
var errOffset = errors.New("Seek: invalid offset")

func (s *SectionReader) Seek(offset int64, whence int) (int64, error) {
   switch whence {
   default:
      return 0, errWhence
   case SeekStart:
      offset += s.base
   case SeekCurrent:
      offset += s.off
   case SeekEnd:
      offset += s.limit
   }
   if offset < s.base {
      return 0, errOffset
   }
   s.off = offset
   return offset - s.base, nil
}

func (s *SectionReader) ReadAt(p []byte, off int64) (n int, err error) {
   if off < 0 || off >= s.limit-s.base {
      return 0, EOF
   }
   off += s.base
   if max := s.limit - off; int64(len(p)) > max {
      p = p[:max]
      n, err = s.r.ReadAt(p, off)
      if err == nil {
         err = EOF
      }
      return n, err
   }
   return s.r.ReadAt(p, off)
}

// Size返回这个片段的字节大小
func (s *SectionReader) Size() int64 {
   return s.limit - s.base
}

// TeeReader返回一个Reader--这个Reader把从r中读取到的数据直接写入到w
// 通过它从r中的所有读取相应地写入到w。
// 没有内部缓冲区,写操作必须在读完成之前完成
// 任何写操作进行时的错误都会作为读操作错误返回
func TeeReader(r Reader, w Writer) Reader {
   return &teeReader{
      r: r,
      w: w,
   }
}

type teeReader struct {
   r Reader
   w Writer
}

func (t *teeReader) Read(p []byte) (n int, err error) {
   n, err = t.r.Read(p)
   if n > 0 {
      if n, err := t.w.Write(p[:n]); err != nil {
         return n, err
      }
   }
   return
}

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

640 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传