2018-04-11 13:46:19 +01:00
|
|
|
// Copyright (C) 2018 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package ranger
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2018-06-19 16:59:09 +01:00
|
|
|
"context"
|
2018-04-11 13:46:19 +01:00
|
|
|
"io"
|
2018-04-17 14:39:14 +01:00
|
|
|
"io/ioutil"
|
|
|
|
|
2018-10-25 09:24:39 +01:00
|
|
|
"storj.io/storj/internal/readcloser"
|
2018-04-11 13:46:19 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
// A Ranger is a flexible data stream type that allows for more effective
|
|
|
|
// pipelining during seeking. A Ranger can return multiple parallel Readers for
|
|
|
|
// any subranges.
|
|
|
|
type Ranger interface {
|
|
|
|
Size() int64
|
2018-06-19 16:59:09 +01:00
|
|
|
Range(ctx context.Context, offset, length int64) (io.ReadCloser, error)
|
2018-04-11 13:46:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// ByteRanger turns a byte slice into a Ranger
|
|
|
|
type ByteRanger []byte
|
|
|
|
|
2018-04-11 14:18:35 +01:00
|
|
|
// Size implements Ranger.Size
|
2018-04-11 13:46:19 +01:00
|
|
|
func (b ByteRanger) Size() int64 { return int64(len(b)) }
|
|
|
|
|
2018-04-11 14:18:35 +01:00
|
|
|
// Range implements Ranger.Range
|
2018-06-19 16:59:09 +01:00
|
|
|
func (b ByteRanger) Range(ctx context.Context, offset, length int64) (io.ReadCloser, error) {
|
2018-04-11 13:46:19 +01:00
|
|
|
if offset < 0 {
|
2018-06-18 17:46:49 +01:00
|
|
|
return nil, Error.New("negative offset")
|
2018-04-17 14:39:14 +01:00
|
|
|
}
|
|
|
|
if length < 0 {
|
2018-06-18 17:46:49 +01:00
|
|
|
return nil, Error.New("negative length")
|
2018-04-11 13:46:19 +01:00
|
|
|
}
|
|
|
|
if offset+length > int64(len(b)) {
|
2018-06-18 17:46:49 +01:00
|
|
|
return nil, Error.New("buffer runoff")
|
2018-04-11 13:46:19 +01:00
|
|
|
}
|
|
|
|
|
2018-06-18 17:46:49 +01:00
|
|
|
return ioutil.NopCloser(bytes.NewReader(b[offset : offset+length])), nil
|
2018-04-11 13:46:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
type concatReader struct {
|
2018-09-14 15:10:43 +01:00
|
|
|
r1 Ranger
|
|
|
|
r2 Ranger
|
2018-04-11 13:46:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *concatReader) Size() int64 {
|
|
|
|
return c.r1.Size() + c.r2.Size()
|
|
|
|
}
|
|
|
|
|
2018-06-19 16:59:09 +01:00
|
|
|
func (c *concatReader) Range(ctx context.Context, offset, length int64) (io.ReadCloser, error) {
|
2018-04-11 13:46:19 +01:00
|
|
|
r1Size := c.r1.Size()
|
|
|
|
if offset+length <= r1Size {
|
2018-06-19 16:59:09 +01:00
|
|
|
return c.r1.Range(ctx, offset, length)
|
2018-04-11 13:46:19 +01:00
|
|
|
}
|
|
|
|
if offset >= r1Size {
|
2018-06-19 16:59:09 +01:00
|
|
|
return c.r2.Range(ctx, offset-r1Size, length)
|
2018-04-11 13:46:19 +01:00
|
|
|
}
|
2018-06-19 16:59:09 +01:00
|
|
|
r1Range, err := c.r1.Range(ctx, offset, r1Size-offset)
|
2018-06-18 17:46:49 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-04-17 14:39:14 +01:00
|
|
|
return readcloser.MultiReadCloser(
|
2018-06-18 17:46:49 +01:00
|
|
|
r1Range,
|
|
|
|
readcloser.LazyReadCloser(func() (io.ReadCloser, error) {
|
2018-06-19 16:59:09 +01:00
|
|
|
return c.r2.Range(ctx, 0, length-(r1Size-offset))
|
2018-06-18 17:46:49 +01:00
|
|
|
})), nil
|
2018-04-11 13:46:19 +01:00
|
|
|
}
|
|
|
|
|
2018-09-14 15:10:43 +01:00
|
|
|
func concat2(r1, r2 Ranger) Ranger {
|
2018-04-11 13:46:19 +01:00
|
|
|
return &concatReader{r1: r1, r2: r2}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Concat concatenates Rangers
|
2018-09-14 15:10:43 +01:00
|
|
|
func Concat(r ...Ranger) Ranger {
|
2018-04-11 13:46:19 +01:00
|
|
|
switch len(r) {
|
|
|
|
case 0:
|
|
|
|
return ByteRanger(nil)
|
|
|
|
case 1:
|
|
|
|
return r[0]
|
|
|
|
case 2:
|
|
|
|
return concat2(r[0], r[1])
|
|
|
|
default:
|
|
|
|
mid := len(r) / 2
|
|
|
|
return concat2(Concat(r[:mid]...), Concat(r[mid:]...))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type subrange struct {
|
2018-09-14 15:10:43 +01:00
|
|
|
r Ranger
|
2018-04-11 13:46:19 +01:00
|
|
|
offset, length int64
|
|
|
|
}
|
|
|
|
|
|
|
|
// Subrange returns a subset of a Ranger.
|
2018-09-14 15:10:43 +01:00
|
|
|
func Subrange(data Ranger, offset, length int64) (Ranger, error) {
|
2018-04-11 13:46:19 +01:00
|
|
|
dSize := data.Size()
|
|
|
|
if offset < 0 || offset > dSize {
|
|
|
|
return nil, Error.New("invalid offset")
|
|
|
|
}
|
|
|
|
if length+offset > dSize {
|
|
|
|
return nil, Error.New("invalid length")
|
|
|
|
}
|
|
|
|
return &subrange{r: data, offset: offset, length: length}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *subrange) Size() int64 {
|
|
|
|
return s.length
|
|
|
|
}
|
|
|
|
|
2018-06-19 16:59:09 +01:00
|
|
|
func (s *subrange) Range(ctx context.Context, offset, length int64) (io.ReadCloser, error) {
|
|
|
|
return s.r.Range(ctx, offset+s.offset, length)
|
2018-04-11 13:46:19 +01:00
|
|
|
}
|