add BlockDevice

This commit is contained in:
soypat
2024-01-15 20:09:27 -03:00
parent 45e207fe2e
commit e6907db19e
+150
View File
@@ -0,0 +1,150 @@
package sd
import (
"errors"
"math/bits"
)
var (
errNegativeOffset = errors.New("sd: negative offset")
)
type Card interface {
WriteBlocks(data []byte, startBlockIdx int64) error
ReadBlocks(dst []byte, startBlockIdx int64) error
EraseBlockSize() int64
EraseBlocks(start, len int64) error
}
func NewBlockDevice(card Card, blockSize int, numBlocks int64) *BlockDevice {
if card == nil || blockSize <= 0 {
panic("invalid arguments")
}
tz := bits.TrailingZeros(uint(blockSize))
if blockSize>>tz != 1 {
panic("blockSize must be a power of 2")
}
bd := &BlockDevice{
card: card,
blockbuf: make([]byte, blockSize),
blockshift: tz,
blockmask: (1 << tz) - 1,
numblocks: numBlocks,
}
return bd
}
// BlockDevice implements tinyfs.BlockDevice interface.
type BlockDevice struct {
card Card
blockbuf []byte
blockshift int
blockmask int64
numblocks int64
}
func (bd *BlockDevice) moduloBlockSize(n int64) int64 {
return n &^ bd.blockmask
}
func (bd *BlockDevice) divideBlockSize(n int64) int64 {
return n >> bd.blockshift
}
func (bd *BlockDevice) ReadAt(p []byte, off int64) (n int, err error) {
if off < 0 {
return 0, errNegativeOffset
}
blockSize := len(bd.blockbuf)
blockIdx := bd.divideBlockSize(off)
blockOff := bd.moduloBlockSize(off)
if blockOff != 0 {
// Non-aligned first block case.
if err := bd.card.ReadBlocks(bd.blockbuf, blockIdx); err != nil {
return n, err
}
n += copy(p, bd.blockbuf[blockOff:])
p = p[n:]
blockIdx++
}
remaining := len(p) - n
if remaining >= blockSize {
// 1 or more full blocks case.
endOffset := remaining - int(bd.moduloBlockSize(int64(remaining)))
err = bd.card.ReadBlocks(p[:endOffset], blockIdx)
if err != nil {
return n, err
}
p = p[endOffset:]
n += endOffset
blockIdx += int64(endOffset / blockSize)
}
if len(p) > 0 {
// Non-aligned last block case.
if err := bd.card.ReadBlocks(bd.blockbuf, blockIdx); err != nil {
return n, err
}
n += copy(p, bd.blockbuf)
}
return n, nil
}
func (bd *BlockDevice) WriteAt(p []byte, off int64) (n int, err error) {
if off < 0 {
return 0, errNegativeOffset
}
blockSize := len(bd.blockbuf)
blockIdx := bd.divideBlockSize(off)
blockOff := bd.moduloBlockSize(off)
if blockOff != 0 {
// Non-aligned first block case.
if err := bd.card.ReadBlocks(bd.blockbuf, blockIdx); err != nil {
return n, err
}
n += copy(bd.blockbuf[blockOff:], p)
if err := bd.card.WriteBlocks(bd.blockbuf, blockIdx); err != nil {
return n, err
}
p = p[n:]
blockIdx++
}
remaining := len(p) - n
if remaining >= blockSize {
// 1 or more full blocks case.
endOffset := remaining - int(bd.moduloBlockSize(int64(remaining)))
err = bd.card.WriteBlocks(p[:endOffset], blockIdx)
if err != nil {
return n, err
}
p = p[endOffset:]
n += endOffset
blockIdx += int64(endOffset / blockSize)
}
if len(p) > 0 {
// Non-aligned last block case.
if err := bd.card.ReadBlocks(bd.blockbuf, blockIdx); err != nil {
return n, err
}
n += copy(bd.blockbuf, p)
if err := bd.card.WriteBlocks(bd.blockbuf, blockIdx); err != nil {
return n, err
}
}
return n, nil
}
func (bd *BlockDevice) Size() int64 {
return int64(len(bd.blockbuf)) * bd.numblocks
}
func (bd *BlockDevice) EraseBlocks(start, len int64) error {
return bd.card.EraseBlocks(start, len)
}
func (bd *BlockDevice) EraseBlockSize() int64 {
return bd.card.EraseBlockSize()
}