refactor: ifaces write an io reader
This commit is contained in:
		| @@ -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) | ||||
|   | ||||
| @@ -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 | ||||
| 	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 | ||||
| } | ||||
|   | ||||
| @@ -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: <nil> 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 | ||||
| } | ||||
		Reference in New Issue
	
	Block a user