diff --git a/interfaces/17-write-an-io-reader/main.go b/interfaces/17-write-an-io-reader/main.go index 2be89e6..42aeb82 100644 --- a/interfaces/17-write-an-io-reader/main.go +++ b/interfaces/17-write-an-io-reader/main.go @@ -16,6 +16,7 @@ import ( ) func main() { + // resp, err := http.Get("https://inancgumus.github.com/x/rosie.jpg") resp, err := http.Get("https://inancgumus.github.com/x/rosie.unknown") if err != nil { fmt.Fprintln(os.Stderr, err) @@ -30,8 +31,6 @@ func main() { } defer file.Close() - // io.Copy reads from pngReader first - // pngReader reads from the resp.Body n, err := io.Copy(file, pngReader(resp.Body)) if err != nil { fmt.Fprintln(os.Stderr, err) diff --git a/interfaces/17-write-an-io-reader/reader.go b/interfaces/17-write-an-io-reader/reader.go index 4b265b8..23c6d14 100644 --- a/interfaces/17-write-an-io-reader/reader.go +++ b/interfaces/17-write-an-io-reader/reader.go @@ -9,41 +9,48 @@ package main import ( - "errors" + "bytes" + "fmt" "io" ) -// create a reader for detecting the png signatures. -func pngReader(r io.Reader) io.Reader { - return &signatureReader{r: r, sign: []byte("\x89PNG\r\n\x1a\n")} -} - -// reader reads from `r` if the stream starts with PNG signature. +// reader reads from `r` if the stream starts with a given signature. // otherwise it stops and returns with an error. type signatureReader struct { - r io.Reader // reads from the response.Body (or from any reader) - sign []byte // stream should start with this initial signature + r io.Reader // reads from the response.Body (or from any reader) + signature []byte // stream should start with this initial signature } // Read implements the io.Reader interface. func (sr *signatureReader) Read(b []byte) (n int, err error) { n, err = sr.r.Read(b) - l := len(sr.sign) - - // simply return if the signature has already been detected. + l := len(sr.signature) if l == 0 { + // simply return if the signature has already been detected. return } - // limit the compared bytes at most to the buffer length. - if lb := len(b); l > lb { + // 1) buffer : **** -> b[:3] -> *** + // signature: *** -> sr.signature[:3] -> *** + // 2) buffer : ** -> b[:2] -> ** + // signature: **** -> sr.signature[:2] -> ** + if lb := len(b); lb < l { l = lb } - if string(b[:l]) != string(sr.sign[:l]) { - err = errors.New("not png") + if got, want := b[:l], sr.signature[:l]; !bytes.Equal(got, want) { + err = fmt.Errorf("signature doesn't match, got: % x, want: % x", got, want) + } + // Assuming the `len(b)` is 4. + // 1st Read(): pr.signature[0:4] -> first part + // 2nd Read(): pr.signature[0:4] -> second part + sr.signature = sr.signature[l:] + return n, err +} + +// create a reader for detecting only the png signatures. +func pngReader(r io.Reader) io.Reader { + return &signatureReader{ + r: r, + signature: []byte("\x89PNG\r\n\x1a\n"), } - // the next read will compare the rest of the signature after `l` - // `sr.sign` will be zero after one or several reads later. - sr.sign = sr.sign[l:] - return } diff --git a/interfaces/17-write-an-io-reader/reader_test.go b/interfaces/17-write-an-io-reader/reader_test.go deleted file mode 100644 index ca5b14f..0000000 --- a/interfaces/17-write-an-io-reader/reader_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package main - -import ( - "io" - "strings" - "testing" -) - -func Test_PNG_Reader_Correct_Signature(t *testing.T) { - const input = "\x89PNG\r\n\x1a\nHELLO" - - out, err := readSignature(input) - if err != nil { - t.Fatalf("got: %q; want: err", err) - } - if out != input { - t.Fatalf("invalid output, got: '%x'; want: '%x'", out, input) - } -} - -func Test_PNG_Reader_Incorrect_Signature(t *testing.T) { - _, err := readSignature("\x89INCORRECT") - if err == nil { - t.Fatal("got: nil; want: !nil err") - } -} - -func readSignature(in string) (string, error) { - r := strings.NewReader(in) - w := strings.Builder{} - _, err := io.Copy(&w, pngReader(r)) - return w.String(), err -}