From e6907db19ee7df1f1fa6d15a1bd60b09d7534524 Mon Sep 17 00:00:00 2001 From: soypat Date: Mon, 15 Jan 2024 20:09:27 -0300 Subject: [PATCH] add BlockDevice --- sd/blockdevice.go | 150 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 sd/blockdevice.go diff --git a/sd/blockdevice.go b/sd/blockdevice.go new file mode 100644 index 0000000..680a7fa --- /dev/null +++ b/sd/blockdevice.go @@ -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() +}