Merge branch 'main' into redo-extract
This commit is contained in:
@@ -0,0 +1,72 @@
|
|||||||
|
package squashfslow
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errOutOfBounds = errors.New("out of bounds")
|
||||||
|
var errUnexpectedOutOfBounds = errors.New("unexpected out of bounds")
|
||||||
|
var errNilCollection = errors.New("nil collection")
|
||||||
|
|
||||||
|
// readPagedItems calls readBLockOrPartial the correct number of times to cache
|
||||||
|
// requestedItemIndex in currentItems, and then returns currentItems[requestedItemIndex].
|
||||||
|
// Parameters:
|
||||||
|
// - requestedItemIndex: The index of the item to be retrieved.
|
||||||
|
// - blockSize: The number of items per block.
|
||||||
|
// - currentItems: A slice of already-read items to manage in-memory storage. Must not be nil.
|
||||||
|
// - readBlockOrPartial: A callback function that reads the next block. It takes the index of the block
|
||||||
|
// to be read, and the number of items to read. It is normally passed block size, but if the last
|
||||||
|
// block is incomplete, it will be passed the number of items in the last block.
|
||||||
|
// Returns:
|
||||||
|
// - the T at requestedItemIndex
|
||||||
|
// - a non-nil error and the zero value of T if an error was encountered.
|
||||||
|
func readPagedItems[T any](
|
||||||
|
requestedItemIndex int,
|
||||||
|
blockSize int,
|
||||||
|
currentItems *[]T,
|
||||||
|
totalItems int,
|
||||||
|
readBlockOrPartial func(idxBlock, numItems int) ([]T, error),
|
||||||
|
) (T, error) {
|
||||||
|
var zero T // Zero value for the item type, used for default return in error cases.
|
||||||
|
if currentItems == nil {
|
||||||
|
return zero, errNilCollection
|
||||||
|
}
|
||||||
|
|
||||||
|
if requestedItemIndex < 0 || requestedItemIndex >= totalItems {
|
||||||
|
return zero, errOutOfBounds
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(*currentItems) > requestedItemIndex {
|
||||||
|
return (*currentItems)[requestedItemIndex], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate which block contains the requested item
|
||||||
|
blockNum := int(math.Ceil(float64(requestedItemIndex+1)/float64(blockSize))) - 1
|
||||||
|
|
||||||
|
// Calculate blocks to read
|
||||||
|
blocksRead := len(*currentItems) / blockSize
|
||||||
|
blocksToRead := blockNum - blocksRead + 1
|
||||||
|
|
||||||
|
// Read and append new blocks
|
||||||
|
for i := 0; i < blocksToRead; i++ {
|
||||||
|
startBlock := blocksRead + i
|
||||||
|
itemsLeft := totalItems - len(*currentItems)
|
||||||
|
itemsToRead := blockSize
|
||||||
|
if itemsToRead > itemsLeft {
|
||||||
|
itemsToRead = itemsLeft
|
||||||
|
}
|
||||||
|
items, err := readBlockOrPartial(startBlock, itemsToRead)
|
||||||
|
if err != nil {
|
||||||
|
return zero, err
|
||||||
|
}
|
||||||
|
*currentItems = append(*currentItems, items...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the slice contains the requested index after reading
|
||||||
|
if len(*currentItems) <= requestedItemIndex {
|
||||||
|
return zero, errUnexpectedOutOfBounds
|
||||||
|
}
|
||||||
|
|
||||||
|
return (*currentItems)[requestedItemIndex], nil
|
||||||
|
}
|
||||||
@@ -124,4 +124,4 @@ package squashfslow
|
|||||||
// assertEqual(t, 512, item)
|
// assertEqual(t, 512, item)
|
||||||
// assertLength(t, 612, currentItems)
|
// assertLength(t, 612, currentItems)
|
||||||
// })
|
// })
|
||||||
// }
|
// }
|
||||||
Reference in New Issue
Block a user