mirror of
https://github.com/minio/minio.git
synced 2025-04-08 13:45:37 -04:00
XL/erasure-read: Avoid memory copy, write to writer directly all the dataBlocks.
This commit is contained in:
parent
d4bea5fbf8
commit
9b82e64a11
@ -189,6 +189,7 @@ func erasureReadFile(writer io.Writer, disks []StorageAPI, volume string, path s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var outSize, outOffset int64
|
||||||
// enBlocks data can have 0-padding hence we need to figure the exact number
|
// enBlocks data can have 0-padding hence we need to figure the exact number
|
||||||
// of bytes we want to read from enBlocks.
|
// of bytes we want to read from enBlocks.
|
||||||
blockSize := eInfo.BlockSize
|
blockSize := eInfo.BlockSize
|
||||||
@ -196,26 +197,24 @@ func erasureReadFile(writer io.Writer, disks []StorageAPI, volume string, path s
|
|||||||
// For the last block, the block size can be less than BlockSize.
|
// For the last block, the block size can be less than BlockSize.
|
||||||
blockSize = totalLength % eInfo.BlockSize
|
blockSize = totalLength % eInfo.BlockSize
|
||||||
}
|
}
|
||||||
data, err := getDataBlocks(enBlocks, eInfo.DataBlocks, int(blockSize))
|
|
||||||
if err != nil {
|
|
||||||
return bytesWritten, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this is start block, skip unwanted bytes.
|
// If this is start block, skip unwanted bytes.
|
||||||
if block == startBlock {
|
if block == startBlock {
|
||||||
data = data[bytesToSkip:]
|
outOffset = bytesToSkip
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(data) > int(length-bytesWritten) {
|
// Total data to be read.
|
||||||
|
outSize = blockSize
|
||||||
|
if length-bytesWritten < blockSize {
|
||||||
// We should not send more data than what was requested.
|
// We should not send more data than what was requested.
|
||||||
data = data[:length-bytesWritten]
|
outSize = length - bytesWritten
|
||||||
}
|
}
|
||||||
|
// Write data blocks.
|
||||||
_, err = writer.Write(data)
|
n, err := writeDataBlocks(writer, enBlocks, eInfo.DataBlocks, outOffset, outSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return bytesWritten, err
|
return bytesWritten, err
|
||||||
}
|
}
|
||||||
bytesWritten += int64(len(data))
|
bytesWritten += n
|
||||||
}
|
}
|
||||||
|
|
||||||
return bytesWritten, nil
|
return bytesWritten, nil
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
"hash"
|
"hash"
|
||||||
"io"
|
"io"
|
||||||
@ -62,30 +63,74 @@ func hashSum(disk StorageAPI, volume, path string, writer hash.Hash) ([]byte, er
|
|||||||
return writer.Sum(nil), nil
|
return writer.Sum(nil), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getDataBlocks - fetches the data block only part of the input encoded blocks.
|
// getDataBlockLen - get length of data blocks from encoded blocks.
|
||||||
func getDataBlocks(enBlocks [][]byte, dataBlocks int, curBlockSize int) (data []byte, err error) {
|
func getDataBlockLen(enBlocks [][]byte, dataBlocks int) int {
|
||||||
if len(enBlocks) < dataBlocks {
|
|
||||||
return nil, reedsolomon.ErrTooFewShards
|
|
||||||
}
|
|
||||||
size := 0
|
size := 0
|
||||||
blocks := enBlocks[:dataBlocks]
|
// Figure out the data block length.
|
||||||
for _, block := range blocks {
|
for _, block := range enBlocks[:dataBlocks] {
|
||||||
size += len(block)
|
size += len(block)
|
||||||
}
|
}
|
||||||
if size < curBlockSize {
|
return size
|
||||||
return nil, reedsolomon.ErrShortData
|
}
|
||||||
|
|
||||||
|
// Writes all the data blocks from encoded blocks until requested
|
||||||
|
// outSize length. Provides a way to skip bytes until the offset.
|
||||||
|
func writeDataBlocks(dst io.Writer, enBlocks [][]byte, dataBlocks int, outOffset int64, outSize int64) (int64, error) {
|
||||||
|
// Do we have enough blocks?
|
||||||
|
if len(enBlocks) < dataBlocks {
|
||||||
|
return 0, reedsolomon.ErrTooFewShards
|
||||||
}
|
}
|
||||||
|
|
||||||
write := curBlockSize
|
// Do we have enough data?
|
||||||
for _, block := range blocks {
|
if int64(getDataBlockLen(enBlocks, dataBlocks)) < outSize {
|
||||||
if write < len(block) {
|
return 0, reedsolomon.ErrShortData
|
||||||
data = append(data, block[:write]...)
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
data = append(data, block...)
|
|
||||||
write -= len(block)
|
|
||||||
}
|
}
|
||||||
return data, nil
|
|
||||||
|
// Counter to decrement total left to write.
|
||||||
|
write := outSize
|
||||||
|
|
||||||
|
// Counter to increment total written.
|
||||||
|
totalWritten := int64(0)
|
||||||
|
|
||||||
|
// Write all data blocks to dst.
|
||||||
|
for _, block := range enBlocks[:dataBlocks] {
|
||||||
|
// Skip blocks until we have reached our offset.
|
||||||
|
if outOffset >= int64(len(block)) {
|
||||||
|
// Decrement offset.
|
||||||
|
outOffset -= int64(len(block))
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
// Skip until offset.
|
||||||
|
block = block[outOffset:]
|
||||||
|
|
||||||
|
// Reset the offset for next iteration to read everything
|
||||||
|
// from subsequent blocks.
|
||||||
|
outOffset = 0
|
||||||
|
}
|
||||||
|
// We have written all the blocks, write the last remaining block.
|
||||||
|
if write < int64(len(block)) {
|
||||||
|
n, err := io.Copy(dst, bytes.NewReader(block[:write]))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
totalWritten += n
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Copy the block.
|
||||||
|
n, err := io.Copy(dst, bytes.NewReader(block))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrement output size.
|
||||||
|
write -= n
|
||||||
|
|
||||||
|
// Increment written.
|
||||||
|
totalWritten += n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success.
|
||||||
|
return totalWritten, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getBlockInfo - find start/end block and bytes to skip for given offset, length and block size.
|
// getBlockInfo - find start/end block and bytes to skip for given offset, length and block size.
|
||||||
|
@ -840,15 +840,17 @@ func (s *MyAPIXLSuite) TestPartialContent(c *C) {
|
|||||||
c.Assert(response.StatusCode, Equals, http.StatusOK)
|
c.Assert(response.StatusCode, Equals, http.StatusOK)
|
||||||
|
|
||||||
// Prepare request
|
// Prepare request
|
||||||
var table = []struct {
|
var testCases = []struct {
|
||||||
byteRange string
|
byteRange string
|
||||||
expectedString string
|
expectedString string
|
||||||
}{
|
}{
|
||||||
{"6-7", "Wo"},
|
{"4-7", "o Wo"},
|
||||||
|
{"1-", "ello World"},
|
||||||
{"6-", "World"},
|
{"6-", "World"},
|
||||||
|
{"-2", "ld"},
|
||||||
{"-7", "o World"},
|
{"-7", "o World"},
|
||||||
}
|
}
|
||||||
for _, t := range table {
|
for _, t := range testCases {
|
||||||
request, err = newTestRequest("GET", s.testServer.Server.URL+"/partial-content/bar",
|
request, err = newTestRequest("GET", s.testServer.Server.URL+"/partial-content/bar",
|
||||||
0, nil, s.testServer.AccessKey, s.testServer.SecretKey)
|
0, nil, s.testServer.AccessKey, s.testServer.SecretKey)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user