multireader.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. package multireader
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "os"
  7. )
  8. type pos struct {
  9. idx int
  10. offset int64
  11. }
  12. type multiReadSeeker struct {
  13. readers []io.ReadSeeker
  14. pos *pos
  15. posIdx map[io.ReadSeeker]int
  16. }
  17. func (r *multiReadSeeker) Seek(offset int64, whence int) (int64, error) {
  18. var tmpOffset int64
  19. switch whence {
  20. case os.SEEK_SET:
  21. for i, rdr := range r.readers {
  22. // get size of the current reader
  23. s, err := rdr.Seek(0, os.SEEK_END)
  24. if err != nil {
  25. return -1, err
  26. }
  27. if offset > tmpOffset+s {
  28. if i == len(r.readers)-1 {
  29. rdrOffset := s + (offset - tmpOffset)
  30. if _, err := rdr.Seek(rdrOffset, os.SEEK_SET); err != nil {
  31. return -1, err
  32. }
  33. r.pos = &pos{i, rdrOffset}
  34. return offset, nil
  35. }
  36. tmpOffset += s
  37. continue
  38. }
  39. rdrOffset := offset - tmpOffset
  40. idx := i
  41. rdr.Seek(rdrOffset, os.SEEK_SET)
  42. // make sure all following readers are at 0
  43. for _, rdr := range r.readers[i+1:] {
  44. rdr.Seek(0, os.SEEK_SET)
  45. }
  46. if rdrOffset == s && i != len(r.readers)-1 {
  47. idx++
  48. rdrOffset = 0
  49. }
  50. r.pos = &pos{idx, rdrOffset}
  51. return offset, nil
  52. }
  53. case os.SEEK_END:
  54. for _, rdr := range r.readers {
  55. s, err := rdr.Seek(0, os.SEEK_END)
  56. if err != nil {
  57. return -1, err
  58. }
  59. tmpOffset += s
  60. }
  61. r.Seek(tmpOffset+offset, os.SEEK_SET)
  62. return tmpOffset + offset, nil
  63. case os.SEEK_CUR:
  64. if r.pos == nil {
  65. return r.Seek(offset, os.SEEK_SET)
  66. }
  67. // Just return the current offset
  68. if offset == 0 {
  69. return r.getCurOffset()
  70. }
  71. curOffset, err := r.getCurOffset()
  72. if err != nil {
  73. return -1, err
  74. }
  75. rdr, rdrOffset, err := r.getReaderForOffset(curOffset + offset)
  76. if err != nil {
  77. return -1, err
  78. }
  79. r.pos = &pos{r.posIdx[rdr], rdrOffset}
  80. return curOffset + offset, nil
  81. default:
  82. return -1, fmt.Errorf("Invalid whence: %d", whence)
  83. }
  84. return -1, fmt.Errorf("Error seeking for whence: %d, offset: %d", whence, offset)
  85. }
  86. func (r *multiReadSeeker) getReaderForOffset(offset int64) (io.ReadSeeker, int64, error) {
  87. var offsetTo int64
  88. for _, rdr := range r.readers {
  89. size, err := getReadSeekerSize(rdr)
  90. if err != nil {
  91. return nil, -1, err
  92. }
  93. if offsetTo+size > offset {
  94. return rdr, offset - offsetTo, nil
  95. }
  96. if rdr == r.readers[len(r.readers)-1] {
  97. return rdr, offsetTo + offset, nil
  98. }
  99. offsetTo += size
  100. }
  101. return nil, 0, nil
  102. }
  103. func (r *multiReadSeeker) getCurOffset() (int64, error) {
  104. var totalSize int64
  105. for _, rdr := range r.readers[:r.pos.idx+1] {
  106. if r.posIdx[rdr] == r.pos.idx {
  107. totalSize += r.pos.offset
  108. break
  109. }
  110. size, err := getReadSeekerSize(rdr)
  111. if err != nil {
  112. return -1, fmt.Errorf("error getting seeker size: %v", err)
  113. }
  114. totalSize += size
  115. }
  116. return totalSize, nil
  117. }
  118. func (r *multiReadSeeker) getOffsetToReader(rdr io.ReadSeeker) (int64, error) {
  119. var offset int64
  120. for _, r := range r.readers {
  121. if r == rdr {
  122. break
  123. }
  124. size, err := getReadSeekerSize(rdr)
  125. if err != nil {
  126. return -1, err
  127. }
  128. offset += size
  129. }
  130. return offset, nil
  131. }
  132. func (r *multiReadSeeker) Read(b []byte) (int, error) {
  133. if r.pos == nil {
  134. // make sure all readers are at 0
  135. r.Seek(0, os.SEEK_SET)
  136. }
  137. bLen := int64(len(b))
  138. buf := bytes.NewBuffer(nil)
  139. var rdr io.ReadSeeker
  140. for _, rdr = range r.readers[r.pos.idx:] {
  141. readBytes, err := io.CopyN(buf, rdr, bLen)
  142. if err != nil && err != io.EOF {
  143. return -1, err
  144. }
  145. bLen -= readBytes
  146. if bLen == 0 {
  147. break
  148. }
  149. }
  150. rdrPos, err := rdr.Seek(0, os.SEEK_CUR)
  151. if err != nil {
  152. return -1, err
  153. }
  154. r.pos = &pos{r.posIdx[rdr], rdrPos}
  155. return buf.Read(b)
  156. }
  157. func getReadSeekerSize(rdr io.ReadSeeker) (int64, error) {
  158. // save the current position
  159. pos, err := rdr.Seek(0, os.SEEK_CUR)
  160. if err != nil {
  161. return -1, err
  162. }
  163. // get the size
  164. size, err := rdr.Seek(0, os.SEEK_END)
  165. if err != nil {
  166. return -1, err
  167. }
  168. // reset the position
  169. if _, err := rdr.Seek(pos, os.SEEK_SET); err != nil {
  170. return -1, err
  171. }
  172. return size, nil
  173. }
  174. // MultiReadSeeker returns a ReadSeeker that's the logical concatenation of the provided
  175. // input readseekers. After calling this method the initial position is set to the
  176. // beginning of the first ReadSeeker. At the end of a ReadSeeker, Read always advances
  177. // to the beginning of the next ReadSeeker and returns EOF at the end of the last ReadSeeker.
  178. // Seek can be used over the sum of lengths of all readseekers.
  179. //
  180. // When a MultiReadSeeker is used, no Read and Seek operations should be made on
  181. // its ReadSeeker components. Also, users should make no assumption on the state
  182. // of individual readseekers while the MultiReadSeeker is used.
  183. func MultiReadSeeker(readers ...io.ReadSeeker) io.ReadSeeker {
  184. if len(readers) == 1 {
  185. return readers[0]
  186. }
  187. idx := make(map[io.ReadSeeker]int)
  188. for i, rdr := range readers {
  189. idx[rdr] = i
  190. }
  191. return &multiReadSeeker{
  192. readers: readers,
  193. posIdx: idx,
  194. }
  195. }