*这是在生病前更新的最后一篇,时隔近2月有余,已经忘得差不多了,现在重新从这一节开始整理边看边补吧。
2017-9-23

ReaderFrom接口在bufio包的bufio.go中有实现,bufio是什么意思?一看名字可以猜得出,是带了buffer缓存的io包。
通俗地来讲,就是io的威力加强版。

可见,io就是一个简单的小骨架,他是开枝散叶的,散布于各方法之中,由其调用。

先看一段实现的方法:

1
2
3
4
5
6
7
func main() {
b := bytes.NewBuffer(make([]byte, 10))
s := strings.NewReader("Hello world!")
bw := bufio.NewWriter(b)
bw.ReadFrom(s)
fmt.Println(b)
}

输出:

1
Hello world!

这里我们先不用深究bufio是怎么用的,strings.NewReader是将“hello world”这个字符串转换成Reader的结构体。

1
2
3
4
5
type Reader struct {
s string
i int64 // current reading index
prevRune int // index of previous rune; or < 0
}

bufio.NewWriter就确定buffer的尺寸。
最后用ReadFrom将s读入到bw中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 同样,实现了io.ReaderFrom
// ReadFrom implements io.ReaderFrom.
func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
if b.Buffered() == 0 {
if w, ok := b.wr.(io.ReaderFrom); ok {
return w.ReadFrom(r)
}
}
var m int
for {
if b.Available() == 0 {
if err1 := b.Flush(); err1 != nil {
return n, err1
}
}
nr := 0
for nr < maxConsecutiveEmptyReads {
m, err = r.Read(b.buf[b.n:])
if m != 0 || err != nil {
break
}
nr++
}
if nr == maxConsecutiveEmptyReads {
return n, io.ErrNoProgress
}
b.n += m
n += int64(m)
if err != nil {
break
}
}
if err == io.EOF {
// If we filled the buffer exactly, flush preemptively.
if b.Available() == 0 {
err = b.Flush()
} else {
err = nil
}
}
return n, err
}

在ReadFrom中有一个判断:if b.Buffered() == 0 {}
翻到buffered()方法的代码中

1
2
// Buffered returns the number of bytes that have been written into the current buffer.
func (b *Writer) Buffered() int { return b.n }

会发现有一个b.n
这其实是一个Writer结构里的一个字段。

1
2
3
4
5
6
type Writer struct {
err error
buf []byte
n int
wr io.Writer
}

(注意:Writer implements buffering for an io.Writer object.)
这个Writer也是实现了io.Writer的。

不过我没找到n的用法,在我上面的例子中。

1
2
3
4
5
if b.Buffered() == 0 {
if w, ok := b.wr.(io.ReaderFrom); ok {
return w.ReadFrom(r)
}
}

b.wr是什么,b.wr就是b := bytes.NewBuffer(make([]byte, 10))
在这一段是必然会return了,因为b.Buffered()为0。到b.wr.(io.ReaderFrom)的时候,即强行把b.wr转换成了io.ReaderFrom(这个的意思是现在w只拥有ReadFrom这一个方法)并赋值给了w。
也就是说,这里的return w.ReadFrom(r),就是b.ReadFrom(r),是否如此,还需验证一下。

1
2
3
4
5
6
7
8
9
10
func main() {
b := bytes.NewBuffer(make([]byte, 10))
s := strings.NewReader("Hello world!")
bw := bufio.NewWriter(b)
val1,err := b.ReadFrom(s)
fmt.Println(val1)
val2,err := bw.ReadFrom(s)
fmt.Println(val2)
fmt.Println(err)
}

输出:

1
2
3
14
0
<nil>

奇怪,怎么val1是14,val2是0了,实际上原因就在于ReadFrom()只会读一次就把buff给清了。

1
2
3
4
// If buffer is empty, reset to recover space.
if b.off >= len(b.buf) {
b.Truncate(0)
}

b.off在第一次调用的时候会置为0。

如果我们把第一次的ReadFrom()去掉,即删除val1,err := b.ReadFrom(s),再打印的时候val2就是14了。

接下来,再做个简单的回顾:

首先:bytes.NewBuffer,返回了一个{ return &Buffer{buf: buf} }Buffer的对象。赋值给b。

1
2
3
4
5
6
type Buffer struct {
buf []byte // contents are the bytes buf[off : len(buf)]
off int // read at &buf[off], write at &buf[len(buf)]
bootstrap [64]byte // memory to hold first slice; helps small buffers avoid allocation.
lastRead readOp // last read operation, so that Unread* can work correctly.
}

此位于bytes包的buffer.go文件。

接下来:strings.NewReader,{ return &Reader{s, 0, -1} },返回了一个Reader对象。赋值给s。

1
2
3
4
5
type Reader struct {
s string
i int64 // current reading index
prevRune int // index of previous rune; or < 0
}

此位于strings包的reader.go文件。

再接下来:bufio.NewWriter(b),把b带入,最后返回的是一个Writer对象。赋值给bw

1
2
3
4
return &Writer{
buf: make([]byte, size),
wr: w,
}

这里就是关键,这个Writer对象里初始化了两个值,buf和wr,不过这两个值都是私有的,其中buf是大小,调用了系统的固定值,一个常量defaultBufSize,为4096。size = defaultBufSize

而w就是传入的b,也即Buffer的对象。传入的过程中已经被转换成了io.Writer对象。

现在的问题就是,这个转换过程,到底做了什么?

事实上我写了一段例子发现,传过去并没有做任何转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func main() {
b := bytes.NewBuffer(make([]byte, 10))
fmt.Println(reflect.TypeOf(b).String())
NewWriter(b)
}

type WriterTest interface {
Write(p []byte) (n int, err error)
}

// NewWriter returns a new Writer whose buffer has the default size.
func NewWriter(w WriterTest) { //io.Write只是一个接口
fmt.Println(reflect.TypeOf(w).String())
}

result:
*bytes.Buffer
*bytes.Buffer

这突然让我想起一件事情,io.Writer是个接口,而所有的变量的根都是继承自一个空的接口(interface{})
所以根本就没做什么转换,直接转过去就行了。

所以wr就是b本身。

最后:
bw.ReadFrom(s)
在目录的例子子,这段程序只执行到:

1
2
3
4
5
if b.Buffered() == 0 {
if w, ok := b.wr.(io.ReaderFrom); ok {
return w.ReadFrom(r)
}
}

就结束了,关键是w.ReadFrom(r)这一段,目前已经知道w就是b。实际上就也就是让b操作了一下ReadFrom(r)。

要注意的是这个ReadFrom(r)是buffer包里的ReadFrom(),位于src\bytes\buffer.go文件里面。
bw.ReadFrom(s)调用的是bufio包里的ReadFrom(),位于src\bufio\bufio.go文件里面,是不一样的!

bw.ReadFrom(s)里面,就把”Hello world!”这串string给了b。

所以就可以打印出来了

bufio的应用场景目前还不太清楚,这一段主要就是大概了解一下ReadFrom的使用情况。