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  }