9fans.net/go@v0.0.7/cmd/acme/internal/disk/disk.go (about)

     1  package disk
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"io/ioutil"
     7  	"log"
     8  	"os"
     9  	"reflect"
    10  	"unsafe"
    11  
    12  	"9fans.net/go/cmd/acme/internal/runes"
    13  	"9fans.net/go/cmd/acme/internal/util"
    14  )
    15  
    16  const blockincr = 256
    17  
    18  const maxblock = 32 * 1024
    19  
    20  var disk *Disk
    21  
    22  type Disk struct {
    23  	fd   *os.File
    24  	addr int64
    25  	free [maxblock/blockincr + 1]*block
    26  }
    27  
    28  type block struct {
    29  	addr int64
    30  	u    struct {
    31  		n    int
    32  		next *block
    33  	}
    34  }
    35  
    36  func Init() {
    37  	disk = newDisk()
    38  }
    39  
    40  func newDisk() *Disk {
    41  	d := new(Disk)
    42  	d.fd = TempFile()
    43  	return d
    44  }
    45  
    46  func roundSize(n int, ip *int) int {
    47  	if n > maxblock {
    48  		util.Fatal("internal error: ntosize")
    49  	}
    50  	size := n
    51  	if size&(blockincr-1) != 0 {
    52  		size += blockincr - (size & (blockincr - 1))
    53  	}
    54  	/* last bucket holds blocks of exactly Maxblock */
    55  	if ip != nil {
    56  		*ip = size / blockincr
    57  	}
    58  	return size * runes.RuneSize
    59  }
    60  
    61  func (d *Disk) allocBlock(n int) *block {
    62  	var i int
    63  	size := roundSize(n, &i)
    64  	b := d.free[i]
    65  	if b != nil {
    66  		d.free[i] = b.u.next
    67  	} else {
    68  		/* allocate in chunks to reduce malloc overhead */
    69  		if blist == nil {
    70  			bb := make([]block, 100)
    71  			for j := 0; j < 100-1; j++ {
    72  				bb[j].u.next = &bb[j+1]
    73  			}
    74  			blist = &bb[0]
    75  		}
    76  		b = blist
    77  		blist = b.u.next
    78  		b.addr = d.addr
    79  		if d.addr+int64(size) < d.addr {
    80  			util.Fatal("temp file overflow")
    81  		}
    82  		d.addr += int64(size)
    83  	}
    84  	b.u.n = n
    85  	return b
    86  }
    87  
    88  func (d *Disk) freeBlock(b *block) {
    89  	var i int
    90  	roundSize(b.u.n, &i)
    91  	b.u.next = d.free[i]
    92  	d.free[i] = b
    93  }
    94  
    95  func runedata(r []rune) []byte {
    96  	var b []byte
    97  	h := (*reflect.SliceHeader)(unsafe.Pointer(&b))
    98  	h.Data = uintptr(unsafe.Pointer(&r[0]))
    99  	h.Len = runes.RuneSize * len(r)
   100  	h.Cap = runes.RuneSize * cap(r)
   101  	return b
   102  }
   103  
   104  func (d *Disk) write(bp **block, r []rune) {
   105  	n := len(r)
   106  	b := *bp
   107  	size := roundSize(b.u.n, nil)
   108  	nsize := roundSize(n, nil)
   109  	if size != nsize {
   110  		d.freeBlock(b)
   111  		b = d.allocBlock(n)
   112  		*bp = b
   113  	}
   114  	if nw, err := d.fd.WriteAt(runedata(r), b.addr); nw != n*runes.RuneSize || err != nil {
   115  		if err == nil {
   116  			err = io.ErrShortWrite
   117  		}
   118  		util.Fatal(fmt.Sprintf("writing temp file: %v", err))
   119  	}
   120  	b.u.n = n
   121  }
   122  
   123  func (d *Disk) read(b *block, r []rune) {
   124  	n := len(r)
   125  	if n > b.u.n {
   126  		util.Fatal("internal error: diskread")
   127  	}
   128  
   129  	roundSize(b.u.n, nil) /* called only for sanity check on Maxblock */
   130  	if nr, err := d.fd.ReadAt(runedata(r), b.addr); nr != n*runes.RuneSize || err != nil {
   131  		util.Fatal("read error from temp file")
   132  	}
   133  }
   134  
   135  func TempFile() *os.File {
   136  	f, err := ioutil.TempFile("", fmt.Sprintf("acme.%d.*", os.Getpid()))
   137  	if err != nil {
   138  		// TODO rescue()
   139  		log.Fatalf("creating temp file: %v", err)
   140  	}
   141  	return f
   142  }