shad-go/lectures/10-io/lecture.slide

464 lines
13 KiB
Text
Raw Normal View History

2020-04-23 01:40:13 +00:00
io
2022-04-20 09:30:48 +00:00
Лекция 10
2020-04-23 01:40:13 +00:00
Арсений Балобанов
2020-04-23 01:40:13 +00:00
* io.Reader & io.Writer
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
*Conceptually:*
- *Reader* has data, and you read it out and make use of that data.
- You have data and you want to shove it into a *Writer* where something happens to that data.
* io.Reader
Read(p []byte) (n int, err error)
- Reads up to len(p) bytes into p.
- Returns the number of bytes read (0 <= n <= len(p)) and any error encountered.
- If some data is available but not len(p) bytes, Read conventionally returns what is available instead of waiting for more.
* io.Reader
Read(p []byte) (n int, err error)
- Non-zero number of bytes at the end may result in either err == EOF or err == nil. The next Read should return 0, EOF.
- Even if Read returns n < len(p), it may use all of p as scratch space during the call (e.g. decompression).
- Process the n > 0 bytes returned before considering the error err.
- Implementations must not retain p.
* Reader Variants
type ReadWriter interface { Reader; Writer }
type ReadCloser interface { Reader; Closer }
type ReadSeeker interface { Reader; Seeker }
type ReadWriteCloser interface { Reader; Writer; Closer }
type ReadWriteSeeker interface { Reader; Writer; Seeker }
type ByteReader interface { ReadByte() (byte, error) } // reads single byte
type RuneReader interface { ReadRune() (r rune, size int, err error) } // reads single rune
type LimitedReader struct // limited to N bytes
type PipeReader struct // read half of a pipe
type SectionReader struct // read interior section
* SectionReader
.play -edit sectionreader/main.go /^func main/,/^}/
* Reader Composition
// LimitReader returns a Reader that reads from r but stops with EOF after n bytes.
func LimitReader(r Reader, n int64) Reader
// MultiReader returns a Reader that's the logical concatenation of the provided input readers.
func MultiReader(readers ...Reader) Reader
// TeeReader returns a Reader that writes to w what it reads from r.
func TeeReader(r Reader, w Writer) Reader
* LimitReader
.play -edit limitreader/main.go /^func main/,/^}/
* MultiReader
.play -edit multireader/main.go /^func main/,/^}/
* TeeReader
.play -edit teereader/main.go /^func main/,/^}/
* Example: http chunking
2020-04-23 17:56:09 +00:00
* Example: http chunking
Chunked transfer encoding is a streaming data transfer mechanism.
4\r\n
Wiki\r\n
5\r\n
pedia\r\n
E\r\n
in\r\n
\r\n
chunks.\r\n
0\r\n
Date: Sun, 06 Nov 1994 08:49:37 GMT\r\n
Content-MD5: 1B2M2Y8AsgTpgAmY7PhCfg==\r\n
\r\n
Actual body
Wikipedia in
chunks.
* Example: http chunking
Problem: proxy a chunked HTTP in a stream.
func transfer(clientWriter io.Writer, responseBody io.Reader)
- send chunks as is
- validate MD5
* Example: http chunking
.play -edit httpchunking/solution1/main.go
* Example: http chunking
.play -edit httpchunking/solution2/main.go /^func transfer/,/^}/
* Example: http chunking
.play -edit httpchunking/solution3/main.go /^func transfer/,/^}/
2022-04-21 15:13:36 +00:00
* ioutil (deprecated)
2020-04-23 01:40:13 +00:00
Package *io/ioutil* implements some I/O utility functions.
var Discard io.Writer = devNull(0)
func NopCloser(r io.Reader) io.ReadCloser
func ReadAll(r io.Reader) ([]byte, error)
func ReadDir(dirname string) ([]os.FileInfo, error)
func ReadFile(filename string) ([]byte, error)
func TempDir(dir, pattern string) (name string, err error)
func TempFile(dir, pattern string) (f *os.File, err error)
func WriteFile(filename string, data []byte, perm os.FileMode) error
2021-04-15 16:02:43 +00:00
* ioutil as of Go 1.16
The same functionality is now provided by package *io* or package *os*, and those implementations should be preferred in new code.
ioutil.Discard -> io.Discard
ioutil.NopCloser -> io.NopCloser
ioutil.ReadAll -> io.ReadAll
ioutil.ReadDir -> os.ReadDir
ioutil.ReadFile -> os.ReadFile
ioutil.TempDir -> os.TempDir
ioutil.TempFile -> os.TempFile
ioutil.WriteFile -> os.WriteFile
2020-04-23 01:40:13 +00:00
* ReadAll
Convenience method for Reader → []byte conversion.
.play -edit readall/main.go /^func main/,/^}/
* ReadAll misuse
ReaderAll(Reader) → []byte → Writer
Consider *io.Copy* instead
io.Copy(dst Writer, src Reader)
* io.Copy
func Copy(dst Writer, src Reader) (written int64, err error)
- Allocates a 32KB buffer to read from src and then write to dst.
func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error)
- Reuse your own buffer with CopyBuffer()
2020-04-23 01:40:13 +00:00
Can we avoid using an intermediate buffer entirely?
type ReaderFrom interface {
ReadFrom(r Reader) (n int64, err error)
}
type WriterTo interface {
WriteTo(w Writer) (n int64, err error)
}
* Example: sendfile
* Example: sendfile
2020-04-23 01:40:13 +00:00
.code -edit sendfile/main.go /^func readFileHandler/,/^}/
* Example: sendfile
✗ strace ./sendfile
fstat(6, {st_mode=S_IFREG|0644, st_size=1338, ...}) = 0
read(6, "{\"id\":\"hello\",\"type"..., 1850) = 1338
read(6, "", 512) = 0
close(6) = 0
write(4, "HTTP/1.1 200 OK\r\nContent-Disposi"..., 1515) = 1515
* Example: sendfile
.code -edit sendfile/main.go /^func copyHandler/,/^}/
* Example: sendfile
✗ strace ./sendfile
read(6, "{\"id\":\"hello\",\"type"..., 512) = 512
fstat(6, {st_mode=S_IFREG|0644, st_size=1338, ...}) = 0
lseek(6, 0, SEEK_SET) = 0
fstat(6, {st_mode=S_IFREG|0644, st_size=1338, ...}) = 0
write(4, "HTTP/1.1 200 OK\r\nContent-Disposi"..., 177) = 177
sendfile(4, 6, NULL, 4194304) = 1338
sendfile(4, 6, NULL, 4194304) = 0
close(6) = 0
* Example: sendfile
2022-04-21 15:13:36 +00:00
- http.ResponseWriter is an io.ReaderFrom that uses the implementation of underlying tcp conn.
2020-04-23 01:40:13 +00:00
// ReadFrom is here to optimize copying from an *os.File regular file
// to a *net.TCPConn with sendfile.
func (w *response) ReadFrom(src io.Reader) (n int64, err error) {
- net.TCPConn also implements io.ReaderFrom that uses *sendfile* system call.
func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) {
2022-04-21 15:13:36 +00:00
* io.Discard
2020-04-23 01:40:13 +00:00
2022-04-21 15:13:36 +00:00
var Discard Writer = discard{}
type discard struct{}
func (discard) Write(p []byte) (int, error) {
return len(p), nil
}
2020-04-23 01:40:13 +00:00
- Implements io.ReaderFrom!
.play -edit discard/main.go
* Reader implementations
Readers are all over the standard library:
- bufio iotest bytes strings crypto debug packet
- archive/...
- image/...
- compress/...
- encoding/...
- text/...
- // and many more...
* io.Writer
Write(p []byte) (n int, err error)
- Write must return a non-nil error if it returns n < len(p)
- Write must not modify the slice data, even temporarily
- Implementations must not retain p
* Writer Variants
type ReadWriter interface { Reader; Writer }
type StringWriter interface { WriteString(s string) (n int, err error) }
type ByteWriter interface { WriteByte(c byte) error }
type PipeWriter struct // the write half of a pipe
*Composition*
// MultiWriter creates a writer that duplicates its writes to all the provided writers, similar to the Unix tee(1) command.
func MultiWriter(writers ...Writer) Writer
* Writer implementations
- iotest/...
- archive/...
- compress/...
- text/...
2020-04-23 16:30:31 +00:00
- net/http/ResponseWriter
2020-04-23 01:40:13 +00:00
- // and many more
* io.Pipe
.play -edit pipe/main.go /^func main/,/^}/
- synchronous
- in-memory
- no internal buffering
* iotest
Package iotest implements Readers and Writers useful mainly for testing.
// DataErrReader creates a reader that returns (n > 0, EOF) at the end.
func DataErrReader(r io.Reader) io.Reader
// HalfReader returns a Reader that implements Read
// by reading half as many requested bytes from r.
func HalfReader(r io.Reader) io.Reader
// OneByteReader returns a Reader that
// implements each non-empty Read by reading one byte from r.
func OneByteReader(r io.Reader) io.Reader
// TimeoutReader returns ErrTimeout on the second read with no data.
// Subsequent calls to read succeed.
func TimeoutReader(r io.Reader) io.Reader
// TruncateWriter returns a Writer that writes to w but stops silently after n bytes.
func TruncateWriter(w io.Writer, n int64) io.Writer
* bufio
Package bufio implements buffered I/O. It wraps an io.Reader or io.Writer objects.
type Reader
func NewReader(rd io.Reader) *Reader
func (b *Reader) Discard(n int) (discarded int, err error)
func (b *Reader) Peek(n int) ([]byte, error)
func (b *Reader) Read(p []byte) (n int, err error)
func (b *Reader) ReadByte() (byte, error)
func (b *Reader) ReadBytes(delim byte) ([]byte, error)
func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error)
func (b *Reader) ReadRune() (r rune, size int, err error)
func (b *Reader) ReadSlice(delim byte) (line []byte, err error)
func (b *Reader) ReadString(delim byte) (string, error)
type Writer
func NewWriter(w io.Writer) *Writer
func (b *Writer) Flush() error
func (b *Writer) ReadFrom(r io.Reader) (n int64, err error)
func (b *Writer) Write(p []byte) (nn int, err error)
func (b *Writer) WriteByte(c byte) error
func (b *Writer) WriteRune(r rune) (size int, err error)
func (b *Writer) WriteString(s string) (int, error)
* bufio.Scanner
Utility type to efficiently read independent lines of text from an io.Reader.
.code -edit scanner/main.go
* bufio read line
There are multiple options to read single line. Which one to use
*ReadBytes('\n')* or *ReadString('\n')* or *ReadLine* or *Scanner*?
- *ReadBytes* returns the slice together with delimiter
- *ReadLine* doesnt handle lines longer than internal buffer (default size 4096)
- *Scanner* has limited max size of the token (64 * 1024)
- *ReadLine* needs to be called for the second time to retrieve rest of the stream
- *ReadBytes* doesnt have any limit
- *Scanner* has the simplest API and provides nicest abstraction for common cases
* bufio
- The net/http package already buffers data (using bufio itself) so you don't need this package for that
- If you are reading a file in one or a few large steps, you probably don't need buffering
2020-04-23 01:40:13 +00:00
* bytes.Buffer
A handy wrapper around byte slice implementing `io.Reader` and `io.Writer`.
Useful when you want to use code that takes an io.Writer, and store the results in memory for the later use.
.play -edit bytesbuffer/main.go
* bytes.Buffer vs strings.Builder
bytes.Buffer
func NewBufferString(s string) *Buffer
func (b *Buffer) Bytes() []byte
func (b *Buffer) Grow(n int)
func (b *Buffer) Read(p []byte) (n int, err error)
func (b *Buffer) ReadByte() (byte, error)
// other read methods
func (b *Buffer) Reset()
func (b *Buffer) String() string
func (b *Buffer) Write(p []byte) (n int, err error)
func (b *Buffer) WriteByte(c byte) error
func (b *Buffer) WriteRune(r rune) (n int, err error)
func (b *Buffer) WriteString(s string) (n int, err error)
strings.Builder
func (b *Builder) Grow(n int)
func (b *Builder) Reset()
func (b *Builder) String() string
func (b *Builder) Write(p []byte) (int, error)
func (b *Builder) WriteByte(c byte) error
func (b *Builder) WriteRune(r rune) (int, error)
func (b *Builder) WriteString(s string) (int, error)
* bytes.Buffer vs strings.Builder
- *strings.Builder* is immutable and can only grow or reset
- *bytes.Buffer*'s internal byte slice can escape: (*Buffer).Bytes().
- strings.Builder.String() does not allocate/copy
// String returns the accumulated string.
func (b *Builder) String() string {
return *(*string)(unsafe.Pointer(&b.buf))
}
- bytes.Buffer.String() does
func (b *Buffer) String() string {
if b == nil {
// Special case, useful in debugging.
return "<nil>"
}
return string(b.buf[b.off:])
}
2022-04-21 15:13:36 +00:00
* bytes.Buffer vs strings.Builder
.play -edit stringsbuilder/main.go
* bytes.Buffer vs strings.Builder
- *strings.Builder* has a copy check
var b1 strings.Builder
b1.WriteString("ABC")
b2 := b1
b2.WriteString("DEF")
// illegal use of non-zero Builder copied by value
- Use pointer to share.
2020-04-23 01:40:13 +00:00
* *os.File
- portable
- implements io.Reader, and io.Writer which stream bytes to or from a file on disk
- useful if you don't want to read the whole file into memory
- has no internal buffers
2020-04-23 01:40:13 +00:00
2022-04-21 15:13:36 +00:00
*os.ReadFile* reads an entire file into memory (as a []byte) in a single call
2020-04-23 01:40:13 +00:00
- allocates a byte slice of the correct size (no need to Read + append in a loop)
- closes the file
- returns the first error that prevented it from working
There are also
func WriteFile(filename string, data []byte, perm os.FileMode) error
func TempFile(dir, pattern string) (f *os.File, err error)
* Summary
- *io* defines interfaces that handle streams of bytes (Reader, Writer, etc...) as well as functions that work generically with types implement these interfaces (e.g. io.Copy)
2021-04-15 16:02:43 +00:00
- *io/ioutil* (deprecated) provides helper functions for some non-trivial file and io tasks
- *testing/iotest* implements Readers and Writers useful mainly for testing
- *bufio* provides buffering wrapper for io.Reader and io.Writer that can improve efficiency
- *bytes* provides helper functions and types for interacting with byte slices
- ***os.File* implements both io.Reader and io.Writer (among others)
* Links
.link https://medium.com/go-walkthrough/go-walkthrough-io-package-8ac5e95a9fbd io walkthrough
.link https://medium.com/golangspec/introduction-to-bufio-package-in-golang-ad7d1877f762 bufio walkthrough
.link https://www.youtube.com/watch?v=kTAsciVuZLQ advanced patterns with io.ReadWriter