package mmap_span
import (
+ "fmt"
"io"
+ "sync"
- "launchpad.net/gommap"
+ "github.com/anacrolix/torrent/segments"
)
-type segment struct {
- gommap.MMap
-}
-
-func (me segment) Size() int64 {
- return int64(len(me.MMap))
+type Mmap interface {
+ Flush() error
+ Unmap() error
+ Bytes() []byte
}
type MMapSpan struct {
- span
+ mu sync.RWMutex
+ mMaps []Mmap
+ segmentLocater segments.Index
}
-func (me *MMapSpan) Append(mmap gommap.MMap) {
- me.span = append(me.span, segment{mmap})
+func (ms *MMapSpan) Append(mMap Mmap) {
+ ms.mMaps = append(ms.mMaps, mMap)
}
-func (me MMapSpan) Close() {
- for _, mMap := range me.span {
- mMap.(segment).UnsafeUnmap()
+func (ms *MMapSpan) Flush() (errs []error) {
+ ms.mu.RLock()
+ defer ms.mu.RUnlock()
+ for _, mMap := range ms.mMaps {
+ err := mMap.Flush()
+ if err != nil {
+ errs = append(errs, err)
+ }
}
+ return
}
-func (me MMapSpan) Size() (ret int64) {
- for _, seg := range me.span {
- ret += seg.Size()
+func (ms *MMapSpan) Close() (errs []error) {
+ ms.mu.Lock()
+ defer ms.mu.Unlock()
+ for _, mMap := range ms.mMaps {
+ err := mMap.Unmap()
+ if err != nil {
+ errs = append(errs, err)
+ }
}
+ // This is for issue 211.
+ ms.mMaps = nil
+ ms.InitIndex()
return
}
-func (me MMapSpan) ReadAt(p []byte, off int64) (n int, err error) {
- me.ApplyTo(off, func(intervalOffset int64, interval sizer) (stop bool) {
- _n := copy(p, interval.(segment).MMap[intervalOffset:])
- p = p[_n:]
- n += _n
- return len(p) == 0
+func (me *MMapSpan) InitIndex() {
+ i := 0
+ me.segmentLocater = segments.NewIndex(func() (segments.Length, bool) {
+ if i == len(me.mMaps) {
+ return -1, false
+ }
+ l := int64(len(me.mMaps[i].Bytes()))
+ i++
+ return l, true
})
- if len(p) != 0 {
+ // log.Printf("made mmapspan index: %v", me.segmentLocater)
+}
+
+func (ms *MMapSpan) ReadAt(p []byte, off int64) (n int, err error) {
+ // log.Printf("reading %v bytes at %v", len(p), off)
+ ms.mu.RLock()
+ defer ms.mu.RUnlock()
+ n = ms.locateCopy(func(a, b []byte) (_, _ []byte) { return a, b }, p, off)
+ if n != len(p) {
err = io.EOF
}
return
}
-func (me MMapSpan) WriteSectionTo(w io.Writer, off, n int64) (written int64, err error) {
- me.ApplyTo(off, func(intervalOffset int64, interval sizer) (stop bool) {
- var _n int
- p := interval.(segment).MMap[intervalOffset:]
- if n < int64(len(p)) {
- p = p[:n]
- }
- _n, err = w.Write(p)
- written += int64(_n)
- n -= int64(_n)
- if err != nil {
- return true
- }
- return n == 0
- })
- return
+func copyBytes(dst, src []byte) int {
+ return copy(dst, src)
}
-func (me MMapSpan) WriteAt(p []byte, off int64) (n int, err error) {
- me.ApplyTo(off, func(iOff int64, i sizer) (stop bool) {
- mMap := i.(segment)
- _n := copy(mMap.MMap[iOff:], p)
- // err = mMap.Sync(gommap.MS_ASYNC)
- // if err != nil {
- // return true
- // }
+func (ms *MMapSpan) locateCopy(copyArgs func(remainingArgument, mmapped []byte) (dst, src []byte), p []byte, off int64) (n int) {
+ ms.segmentLocater.Locate(segments.Extent{off, int64(len(p))}, func(i int, e segments.Extent) bool {
+ mMapBytes := ms.mMaps[i].Bytes()[e.Start:]
+ // log.Printf("got segment %v: %v, copying %v, %v", i, e, len(p), len(mMapBytes))
+ _n := copyBytes(copyArgs(p, mMapBytes))
p = p[_n:]
n += _n
- return len(p) == 0
+
+ if segments.Int(_n) != e.Length {
+ panic(fmt.Sprintf("did %d bytes, expected to do %d", _n, e.Length))
+ }
+ return true
})
- if err != nil && len(p) != 0 {
+ return
+}
+
+func (ms *MMapSpan) WriteAt(p []byte, off int64) (n int, err error) {
+ // log.Printf("writing %v bytes at %v", len(p), off)
+ ms.mu.RLock()
+ defer ms.mu.RUnlock()
+ n = ms.locateCopy(func(a, b []byte) (_, _ []byte) { return b, a }, p, off)
+ if n != len(p) {
err = io.ErrShortWrite
}
return