package goadb import ( "fmt" "io" "github.com/zach-klippenstein/goadb/wire" ) // syncFileReader wraps a SyncConn that has requested to receive a file. type syncFileReader struct { // Reader used to read data from the adb connection. scanner wire.SyncScanner // Reader for the current chunk only. chunkReader io.Reader } var _ io.ReadCloser = &syncFileReader{} func newSyncFileReader(s wire.SyncScanner) io.ReadCloser { return &syncFileReader{ scanner: s, } } func (r *syncFileReader) Read(buf []byte) (n int, err error) { if r.chunkReader == nil { chunkReader, err := readNextChunk(r.scanner) if err != nil { // If this is EOF, we've read the last chunk. // Either way, we want to pass it up to the caller. return 0, err } r.chunkReader = chunkReader } n, err = r.chunkReader.Read(buf) if err == io.EOF { // End of current chunk, don't return an error, the next chunk will be // read on the next call to this method. r.chunkReader = nil return n, nil } return n, err } func (r *syncFileReader) Close() error { return r.scanner.Close() } // readNextChunk creates an io.LimitedReader for the next chunk of data, // and returns io.EOF if the last chunk has been read. func readNextChunk(r wire.SyncScanner) (io.Reader, error) { id, err := r.ReadOctetString() if err != nil { return nil, err } switch id { case "DATA": return r.ReadBytes() case "DONE": return nil, io.EOF default: return nil, fmt.Errorf("expected chunk id 'DATA', but got '%s'", id) } }