hook-reader.go 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. /*
  2. * MinIO Go Library for Amazon S3 Compatible Cloud Storage
  3. * Copyright 2015-2017 MinIO, Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package minio
  18. import (
  19. "fmt"
  20. "io"
  21. )
  22. // hookReader hooks additional reader in the source stream. It is
  23. // useful for making progress bars. Second reader is appropriately
  24. // notified about the exact number of bytes read from the primary
  25. // source on each Read operation.
  26. type hookReader struct {
  27. source io.Reader
  28. hook io.Reader
  29. }
  30. // Seek implements io.Seeker. Seeks source first, and if necessary
  31. // seeks hook if Seek method is appropriately found.
  32. func (hr *hookReader) Seek(offset int64, whence int) (n int64, err error) {
  33. // Verify for source has embedded Seeker, use it.
  34. sourceSeeker, ok := hr.source.(io.Seeker)
  35. if ok {
  36. n, err = sourceSeeker.Seek(offset, whence)
  37. if err != nil {
  38. return 0, err
  39. }
  40. }
  41. // Verify if hook has embedded Seeker, use it.
  42. hookSeeker, ok := hr.hook.(io.Seeker)
  43. if ok {
  44. var m int64
  45. m, err = hookSeeker.Seek(offset, whence)
  46. if err != nil {
  47. return 0, err
  48. }
  49. if n != m {
  50. return 0, fmt.Errorf("hook seeker seeked %d bytes, expected source %d bytes", m, n)
  51. }
  52. }
  53. return n, nil
  54. }
  55. // Read implements io.Reader. Always reads from the source, the return
  56. // value 'n' number of bytes are reported through the hook. Returns
  57. // error for all non io.EOF conditions.
  58. func (hr *hookReader) Read(b []byte) (n int, err error) {
  59. n, err = hr.source.Read(b)
  60. if err != nil && err != io.EOF {
  61. return n, err
  62. }
  63. // Progress the hook with the total read bytes from the source.
  64. if _, herr := hr.hook.Read(b[:n]); herr != nil {
  65. if herr != io.EOF {
  66. return n, herr
  67. }
  68. }
  69. return n, err
  70. }
  71. // newHook returns a io.ReadSeeker which implements hookReader that
  72. // reports the data read from the source to the hook.
  73. func newHook(source, hook io.Reader) io.Reader {
  74. if hook == nil {
  75. return source
  76. }
  77. return &hookReader{source, hook}
  78. }