364 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			364 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package rpl
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"os"
 | |
| 
 | |
| 	"github.com/edsrzf/mmap-go"
 | |
| 	"github.com/siddontang/go/log"
 | |
| )
 | |
| 
 | |
| //like leveldb or rocksdb file interface, haha!
 | |
| 
 | |
| type writeFile interface {
 | |
| 	Sync() error
 | |
| 	Write(b []byte) (n int, err error)
 | |
| 	Close() error
 | |
| 	ReadAt(buf []byte, offset int64) (int, error)
 | |
| 	Truncate(size int64) error
 | |
| 	SetOffset(o int64)
 | |
| 	Name() string
 | |
| 	Size() int
 | |
| 	Offset() int64
 | |
| }
 | |
| 
 | |
| type readFile interface {
 | |
| 	ReadAt(buf []byte, offset int64) (int, error)
 | |
| 	Close() error
 | |
| 	Size() int
 | |
| 	Name() string
 | |
| }
 | |
| 
 | |
| type rawWriteFile struct {
 | |
| 	writeFile
 | |
| 	f      *os.File
 | |
| 	offset int64
 | |
| 	name   string
 | |
| }
 | |
| 
 | |
| func newRawWriteFile(name string, size int64) (writeFile, error) {
 | |
| 	m := new(rawWriteFile)
 | |
| 	var err error
 | |
| 
 | |
| 	m.name = name
 | |
| 
 | |
| 	m.f, err = os.OpenFile(name, os.O_CREATE|os.O_RDWR, 0644)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return m, nil
 | |
| }
 | |
| 
 | |
| func (m *rawWriteFile) Close() error {
 | |
| 	if err := m.f.Truncate(m.offset); err != nil {
 | |
| 		return fmt.Errorf("close truncate %s error %s", m.name, err.Error())
 | |
| 	}
 | |
| 
 | |
| 	if err := m.f.Close(); err != nil {
 | |
| 		return fmt.Errorf("close %s error %s", m.name, err.Error())
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (m *rawWriteFile) Sync() error {
 | |
| 	return m.f.Sync()
 | |
| }
 | |
| 
 | |
| func (m *rawWriteFile) Write(b []byte) (n int, err error) {
 | |
| 	n, err = m.f.WriteAt(b, m.offset)
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	} else if n != len(b) {
 | |
| 		err = io.ErrShortWrite
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	m.offset += int64(n)
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func (m *rawWriteFile) ReadAt(buf []byte, offset int64) (int, error) {
 | |
| 	return m.f.ReadAt(buf, offset)
 | |
| }
 | |
| 
 | |
| func (m *rawWriteFile) Truncate(size int64) error {
 | |
| 	var err error
 | |
| 	if err = m.f.Truncate(size); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	if m.offset > size {
 | |
| 		m.offset = size
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (m *rawWriteFile) SetOffset(o int64) {
 | |
| 	m.offset = o
 | |
| }
 | |
| 
 | |
| func (m *rawWriteFile) Offset() int64 {
 | |
| 	return m.offset
 | |
| }
 | |
| 
 | |
| func (m *rawWriteFile) Name() string {
 | |
| 	return m.name
 | |
| }
 | |
| 
 | |
| func (m *rawWriteFile) Size() int {
 | |
| 	st, _ := m.f.Stat()
 | |
| 	return int(st.Size())
 | |
| }
 | |
| 
 | |
| type rawReadFile struct {
 | |
| 	readFile
 | |
| 
 | |
| 	f    *os.File
 | |
| 	name string
 | |
| }
 | |
| 
 | |
| func newRawReadFile(name string) (readFile, error) {
 | |
| 	m := new(rawReadFile)
 | |
| 
 | |
| 	var err error
 | |
| 	m.f, err = os.Open(name)
 | |
| 	m.name = name
 | |
| 
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return m, err
 | |
| }
 | |
| 
 | |
| func (m *rawReadFile) Close() error {
 | |
| 	return m.f.Close()
 | |
| }
 | |
| 
 | |
| func (m *rawReadFile) Size() int {
 | |
| 	st, _ := m.f.Stat()
 | |
| 	return int(st.Size())
 | |
| }
 | |
| 
 | |
| func (m *rawReadFile) ReadAt(b []byte, offset int64) (int, error) {
 | |
| 	return m.f.ReadAt(b, offset)
 | |
| }
 | |
| 
 | |
| func (m *rawReadFile) Name() string {
 | |
| 	return m.name
 | |
| }
 | |
| 
 | |
| /////////////////////////////////////////////////
 | |
| 
 | |
| type mmapWriteFile struct {
 | |
| 	writeFile
 | |
| 
 | |
| 	f      *os.File
 | |
| 	m      mmap.MMap
 | |
| 	name   string
 | |
| 	size   int64
 | |
| 	offset int64
 | |
| }
 | |
| 
 | |
| func newMmapWriteFile(name string, size int64) (writeFile, error) {
 | |
| 	m := new(mmapWriteFile)
 | |
| 
 | |
| 	m.name = name
 | |
| 
 | |
| 	var err error
 | |
| 
 | |
| 	m.f, err = os.OpenFile(name, os.O_CREATE|os.O_RDWR, 0644)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	if size == 0 {
 | |
| 		st, _ := m.f.Stat()
 | |
| 		size = st.Size()
 | |
| 	}
 | |
| 
 | |
| 	if err = m.f.Truncate(size); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	if m.m, err = mmap.Map(m.f, mmap.RDWR, 0); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	m.size = size
 | |
| 	m.offset = 0
 | |
| 	return m, nil
 | |
| }
 | |
| 
 | |
| func (m *mmapWriteFile) Size() int {
 | |
| 	return int(m.size)
 | |
| }
 | |
| 
 | |
| func (m *mmapWriteFile) Sync() error {
 | |
| 	return m.m.Flush()
 | |
| }
 | |
| 
 | |
| func (m *mmapWriteFile) Close() error {
 | |
| 	if err := m.m.Unmap(); err != nil {
 | |
| 		return fmt.Errorf("unmap %s error %s", m.name, err.Error())
 | |
| 	}
 | |
| 
 | |
| 	if err := m.f.Truncate(m.offset); err != nil {
 | |
| 		return fmt.Errorf("close truncate %s error %s", m.name, err.Error())
 | |
| 	}
 | |
| 
 | |
| 	if err := m.f.Close(); err != nil {
 | |
| 		return fmt.Errorf("close %s error %s", m.name, err.Error())
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (m *mmapWriteFile) Write(b []byte) (n int, err error) {
 | |
| 	extra := int64(len(b)) - (m.size - m.offset)
 | |
| 	if extra > 0 {
 | |
| 		newSize := m.size + extra + m.size/10
 | |
| 		if err = m.Truncate(newSize); err != nil {
 | |
| 			return
 | |
| 		}
 | |
| 		m.size = newSize
 | |
| 	}
 | |
| 
 | |
| 	n = copy(m.m[m.offset:], b)
 | |
| 	if n != len(b) {
 | |
| 		return 0, io.ErrShortWrite
 | |
| 	}
 | |
| 
 | |
| 	m.offset += int64(len(b))
 | |
| 	return len(b), nil
 | |
| }
 | |
| 
 | |
| func (m *mmapWriteFile) ReadAt(buf []byte, offset int64) (int, error) {
 | |
| 	if offset > m.offset {
 | |
| 		return 0, fmt.Errorf("invalid offset %d", offset)
 | |
| 	}
 | |
| 
 | |
| 	n := copy(buf, m.m[offset:m.offset])
 | |
| 	if n != len(buf) {
 | |
| 		return n, io.ErrUnexpectedEOF
 | |
| 	}
 | |
| 
 | |
| 	return n, nil
 | |
| }
 | |
| 
 | |
| func (m *mmapWriteFile) Truncate(size int64) error {
 | |
| 	var err error
 | |
| 	if err = m.m.Unmap(); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	if err = m.f.Truncate(size); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	if m.m, err = mmap.Map(m.f, mmap.RDWR, 0); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	m.size = size
 | |
| 	if m.offset > m.size {
 | |
| 		m.offset = m.size
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (m *mmapWriteFile) SetOffset(o int64) {
 | |
| 	m.offset = o
 | |
| }
 | |
| 
 | |
| func (m *mmapWriteFile) Offset() int64 {
 | |
| 	return m.offset
 | |
| }
 | |
| 
 | |
| func (m *mmapWriteFile) Name() string {
 | |
| 	return m.name
 | |
| }
 | |
| 
 | |
| type mmapReadFile struct {
 | |
| 	readFile
 | |
| 
 | |
| 	f    *os.File
 | |
| 	m    mmap.MMap
 | |
| 	name string
 | |
| }
 | |
| 
 | |
| func newMmapReadFile(name string) (readFile, error) {
 | |
| 	m := new(mmapReadFile)
 | |
| 
 | |
| 	m.name = name
 | |
| 
 | |
| 	var err error
 | |
| 	m.f, err = os.Open(name)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	m.m, err = mmap.Map(m.f, mmap.RDONLY, 0)
 | |
| 	return m, err
 | |
| }
 | |
| 
 | |
| func (m *mmapReadFile) ReadAt(buf []byte, offset int64) (int, error) {
 | |
| 	if int64(offset) > int64(len(m.m)) {
 | |
| 		return 0, fmt.Errorf("invalid offset %d", offset)
 | |
| 	}
 | |
| 
 | |
| 	n := copy(buf, m.m[offset:])
 | |
| 	if n != len(buf) {
 | |
| 		return n, io.ErrUnexpectedEOF
 | |
| 	}
 | |
| 
 | |
| 	return n, nil
 | |
| }
 | |
| 
 | |
| func (m *mmapReadFile) Close() error {
 | |
| 	if m.m != nil {
 | |
| 		if err := m.m.Unmap(); err != nil {
 | |
| 			log.Errorf("unmap %s error %s", m.name, err.Error())
 | |
| 		}
 | |
| 		m.m = nil
 | |
| 	}
 | |
| 
 | |
| 	if m.f != nil {
 | |
| 		if err := m.f.Close(); err != nil {
 | |
| 			log.Errorf("close %s error %s", m.name, err.Error())
 | |
| 		}
 | |
| 		m.f = nil
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (m *mmapReadFile) Size() int {
 | |
| 	return len(m.m)
 | |
| }
 | |
| 
 | |
| func (m *mmapReadFile) Name() string {
 | |
| 	return m.name
 | |
| }
 | |
| 
 | |
| /////////////////////////////////////
 | |
| 
 | |
| func newWriteFile(useMmap bool, name string, size int64) (writeFile, error) {
 | |
| 	if useMmap {
 | |
| 		return newMmapWriteFile(name, size)
 | |
| 	} else {
 | |
| 		return newRawWriteFile(name, size)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func newReadFile(useMmap bool, name string) (readFile, error) {
 | |
| 	if useMmap {
 | |
| 		return newMmapReadFile(name)
 | |
| 	} else {
 | |
| 		return newRawReadFile(name)
 | |
| 	}
 | |
| }
 |