blob: cf5e21d85c87e6bd3971ef0e1965d16199d7eef6 [file] [log] [blame]
Lorenz Brun1e0e3a42023-06-28 16:40:18 +02001package blockdev
2
3import (
4 "errors"
5 "fmt"
6 "io"
7 "math/bits"
8)
9
10// Memory is a memory-backed implementation of BlockDev. It is optimal
11// for testing and temporary use, as it is fast and platform-independent.
12type Memory struct {
13 blockSize int64
14 blockCount int64
15 data []byte
16}
17
18// NewMemory returns a new memory-backed block device with the given geometry.
19func NewMemory(blockSize, blockCount int64) (*Memory, error) {
20 if blockSize <= 0 {
21 return nil, errors.New("block size cannot be zero or negative")
22 }
23 if bits.OnesCount64(uint64(blockSize)) > 1 {
24 return nil, fmt.Errorf("block size must be a power of two (got %d)", blockSize)
25 }
26 if blockCount < 0 {
27 return nil, errors.New("block count cannot be negative")
28 }
29 return &Memory{
30 blockSize: blockSize,
31 blockCount: blockCount,
32 data: make([]byte, blockSize*blockCount),
33 }, nil
34}
35
36// MustNewMemory works exactly like NewMemory, but panics when NewMemory would
37// return an error. Intended for use in tests.
38func MustNewMemory(blockSize, blockCount int64) *Memory {
39 m, err := NewMemory(blockSize, blockCount)
40 if err != nil {
41 panic(err)
42 }
43 return m
44}
45
Jan Schära6da1712024-08-21 15:12:11 +020046func (m *Memory) ReadAt(p []byte, off int64) (n int, err error) {
47 if off < 0 {
48 return 0, errors.New("blockdev.Memory.ReadAt: negative offset")
49 }
50 if off > int64(len(m.data)) {
Lorenz Brun1e0e3a42023-06-28 16:40:18 +020051 return 0, io.EOF
52 }
53 // TODO: Alignment checks?
Jan Schära6da1712024-08-21 15:12:11 +020054 n = copy(p, m.data[off:])
Lorenz Brun1e0e3a42023-06-28 16:40:18 +020055 if n < len(p) {
Jan Schära6da1712024-08-21 15:12:11 +020056 err = io.EOF
Lorenz Brun1e0e3a42023-06-28 16:40:18 +020057 }
Jan Schära6da1712024-08-21 15:12:11 +020058 return
Lorenz Brun1e0e3a42023-06-28 16:40:18 +020059}
60
Jan Schära6da1712024-08-21 15:12:11 +020061func (m *Memory) WriteAt(p []byte, off int64) (n int, err error) {
62 if off < 0 || off > int64(len(m.data)) {
63 return 0, ErrOutOfBounds
Lorenz Brun1e0e3a42023-06-28 16:40:18 +020064 }
65 // TODO: Alignment checks?
Jan Schära6da1712024-08-21 15:12:11 +020066 n = copy(m.data[off:], p)
Lorenz Brun1e0e3a42023-06-28 16:40:18 +020067 if n < len(p) {
Jan Schära6da1712024-08-21 15:12:11 +020068 err = ErrOutOfBounds
Lorenz Brun1e0e3a42023-06-28 16:40:18 +020069 }
Jan Schära6da1712024-08-21 15:12:11 +020070 return
Lorenz Brun1e0e3a42023-06-28 16:40:18 +020071}
72
73func (m *Memory) BlockCount() int64 {
74 return m.blockCount
75}
76
Jan Schära6da1712024-08-21 15:12:11 +020077func (m *Memory) BlockSize() int64 {
78 return m.blockSize
79}
80
Lorenz Brun1e0e3a42023-06-28 16:40:18 +020081func (m *Memory) OptimalBlockSize() int64 {
82 return m.blockSize
83}
84
Lorenz Brun1e0e3a42023-06-28 16:40:18 +020085func (m *Memory) Discard(startByte, endByte int64) error {
Jan Schära6da1712024-08-21 15:12:11 +020086 if err := validAlignedRange(m, startByte, endByte); err != nil {
Lorenz Brun1e0e3a42023-06-28 16:40:18 +020087 return err
88 }
89 for i := startByte; i < endByte; i++ {
90 // Intentionally don't set to zero as Discard doesn't guarantee
91 // any specific contents. Call Zero if you need this.
92 m.data[i] = 0xaa
93 }
94 return nil
95}
96
97func (m *Memory) Zero(startByte, endByte int64) error {
Jan Schära6da1712024-08-21 15:12:11 +020098 if err := validAlignedRange(m, startByte, endByte); err != nil {
Lorenz Brun1e0e3a42023-06-28 16:40:18 +020099 return err
100 }
101 for i := startByte; i < endByte; i++ {
102 m.data[i] = 0x00
103 }
104 return nil
105}
Jan Schära6da1712024-08-21 15:12:11 +0200106
107func (m *Memory) Sync() error {
108 return nil
109}