-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
When serving data from remote location, one might need to pass the request context to backend storage of entries, for example for distributed tracing to work.
- Loading branch information
1 parent
bc5258f
commit 0b4c75e
Showing
8 changed files
with
424 additions
and
60 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,3 @@ | ||
module github.com/martin-sucha/zipserve | ||
|
||
go 1.12 | ||
|
||
require go4.org v0.0.0-20180417224846-9599cf28b011 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +0,0 @@ | ||
go4.org v0.0.0-20180417224846-9599cf28b011 h1:i0QTVNl3j6yciHiQIHxz+mnsSQqo/xi78EGN7yNpMVw= | ||
go4.org v0.0.0-20180417224846-9599cf28b011/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
package zipserve | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"io" | ||
"sort" | ||
) | ||
|
||
// ReaderAt is like io.ReaderAt, but also takes context. | ||
type ReaderAt interface { | ||
// ReadAtContext has same semantics as ReadAt from io.ReaderAt, but takes context. | ||
ReadAtContext(ctx context.Context, p []byte, off int64) (n int, err error) | ||
} | ||
|
||
type sizeReaderAt interface { | ||
io.ReaderAt | ||
Size() int64 | ||
} | ||
|
||
type offsetAndData struct { | ||
offset int64 | ||
data ReaderAt | ||
} | ||
|
||
// multiReaderAt is a ReaderAt that joins multiple ReaderAt sequentially together. | ||
type multiReaderAt struct { | ||
parts []offsetAndData | ||
size int64 | ||
} | ||
|
||
// add a part to the multiContextReader. | ||
// add can be used only before the reader is read from. | ||
func (mcr *multiReaderAt) add(data ReaderAt, size int64) { | ||
switch { | ||
case size < 0: | ||
panic(fmt.Sprintf("size cannot be negative: %v", size)) | ||
case size == 0: | ||
return | ||
} | ||
mcr.parts = append(mcr.parts, offsetAndData{ | ||
offset: mcr.size, | ||
data: data, | ||
}) | ||
mcr.size += size | ||
} | ||
|
||
// addSizeReaderAt is like add, but takes sizeReaderAt | ||
func (mcr *multiReaderAt) addSizeReaderAt(r sizeReaderAt) { | ||
mcr.add(ignoreContext{r: r}, r.Size()) | ||
} | ||
|
||
// endOffset is offset where the given part ends. | ||
func (mcr *multiReaderAt) endOffset(partIndex int) int64 { | ||
if partIndex == len(mcr.parts)-1 { | ||
return mcr.size | ||
} | ||
return mcr.parts[partIndex+1].offset | ||
} | ||
|
||
func (mcr *multiReaderAt) ReadAtContext(ctx context.Context, p []byte, off int64) (n int, err error) { | ||
if len(p) == 0 { | ||
return 0, nil | ||
} | ||
if off >= mcr.size { | ||
return 0, io.EOF | ||
} | ||
// find first part that has data for p | ||
firstPartIndex := sort.Search(len(mcr.parts), func(i int) bool { | ||
return mcr.endOffset(i) > off | ||
}) | ||
for partIndex := firstPartIndex; partIndex < len(mcr.parts) && len(p) > 0; partIndex++ { | ||
if partIndex > firstPartIndex { | ||
off = mcr.parts[partIndex].offset | ||
} | ||
partRemainingBytes := mcr.endOffset(partIndex) - off | ||
sizeToRead := int64(len(p)) | ||
if sizeToRead > partRemainingBytes { | ||
sizeToRead = partRemainingBytes | ||
} | ||
n2, err2 := mcr.parts[partIndex].data.ReadAtContext(ctx, p[0:sizeToRead], off - mcr.parts[partIndex].offset) | ||
n += n2 | ||
if err2 != nil { | ||
return n, err2 | ||
} | ||
p = p[n2:] | ||
} | ||
if len(p) > 0 { | ||
// tried reading beyond size | ||
return n, io.EOF | ||
} | ||
return n, nil | ||
} | ||
|
||
func (mcr *multiReaderAt) ReadAt(p []byte, off int64) (n int, err error) { | ||
return mcr.ReadAtContext(context.TODO(), p, off) | ||
} | ||
|
||
func (mcr *multiReaderAt) Size() int64 { | ||
return mcr.size | ||
} | ||
|
||
// ignoreContext converts io.ReaderAt to ReaderAt | ||
type ignoreContext struct { | ||
r io.ReaderAt | ||
} | ||
|
||
func (a ignoreContext) ReadAtContext(_ context.Context, p []byte, off int64) (n int, err error) { | ||
return a.r.ReadAt(p, off) | ||
} | ||
|
||
// withContext converts ReaderAt to io.ReaderAt. | ||
// | ||
// While usually we shouldn't store context in a structure, we ensure that withContext lives only within single | ||
// request. | ||
type withContext struct { | ||
ctx context.Context | ||
r ReaderAt | ||
} | ||
|
||
func (w withContext) ReadAt(p []byte, off int64) (n int, err error) { | ||
return w.r.ReadAtContext(w.ctx, p, off) | ||
} |
Oops, something went wrong.