github.com/jflude/taocp@v0.0.0-20240210234939-99f2a91af3c2/mix/tape.go (about) 1 package mix 2 3 import ( 4 "encoding/binary" 5 "errors" 6 "fmt" 7 "io" 8 ) 9 10 const maxTapeBlock = 46000 11 12 type Tape struct { 13 rwsc readWriteSeekCloser 14 name string 15 here int64 16 } 17 18 var ErrInvalidBlock = errors.New("mix: invalid block") 19 20 // see Section 5.4.6 21 func NewTape(rwsc readWriteSeekCloser, unit int) (*Tape, error) { 22 return &Tape{rwsc, fmt.Sprintf("TAPE%02d", unit), 0}, nil 23 } 24 25 func (t *Tape) Name() string { 26 return t.name 27 } 28 29 func (*Tape) BlockSize() int { 30 return 100 31 } 32 33 func (t *Tape) Read(block []Word) (int64, error) { 34 if t.isPastEnd(4 * int64(t.BlockSize())) { 35 return 0, ErrInvalidBlock 36 } 37 buf := make([]byte, 4*len(block)) 38 if _, err := io.ReadFull(t.rwsc, buf); err != nil { 39 return 0, err 40 } 41 for i, j := 0, 0; i < len(block); i, j = i+1, j+4 { 42 block[i] = Word(binary.LittleEndian.Uint32(buf[j : j+4])) 43 } 44 t.here += int64(4 * t.BlockSize()) 45 return 8000, nil 46 } 47 48 func (t *Tape) Write(block []Word) (int64, error) { 49 if t.isPastEnd(4 * int64(t.BlockSize())) { 50 return 0, ErrInvalidBlock 51 } 52 buf := make([]byte, 4*len(block)) 53 for i, j := 0, 0; i < len(block); i, j = i+1, j+4 { 54 binary.LittleEndian.PutUint32(buf[j:j+4], uint32(block[i])) 55 } 56 _, err := t.rwsc.Write(buf) 57 if err == nil { 58 t.here += int64(4 * t.BlockSize()) 59 } 60 return 8000, err 61 } 62 63 func (t *Tape) Control(m int) (int64, error) { 64 var off, duration int64 65 var wh int 66 if m == 0 { 67 off = 0 68 wh = io.SeekStart 69 duration = 5000 * t.here / int64(4*t.BlockSize()) 70 } else { 71 off = int64(4 * t.BlockSize() * m) 72 if t.isPastEnd(off) { 73 return 0, ErrInvalidBlock 74 } 75 if t.here+off < 0 { 76 off = -t.here 77 } 78 wh = io.SeekCurrent 79 duration = 5000 * abs64(int64(m)) 80 } 81 var err error 82 t.here, err = t.rwsc.Seek(off, wh) 83 return duration, err 84 } 85 86 func (t *Tape) Close() error { 87 return t.rwsc.Close() 88 } 89 90 func (t *Tape) isPastEnd(offset int64) bool { 91 return t.here+offset > 4*int64(t.BlockSize())*maxTapeBlock 92 }