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 }