github.com/balzaczyy/golucene@v0.0.0-20151210033525-d0be9ee89713/core/store/simplefs.go (about)

     1  package store
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"sync"
     9  )
    10  
    11  // store/SimpleFSLockFactory.java
    12  
    13  type SimpleFSLock struct {
    14  	*LockImpl
    15  	file, dir string
    16  }
    17  
    18  func newSimpleFSLock(lockDir, lockFileName string) *SimpleFSLock {
    19  	ans := &SimpleFSLock{
    20  		dir:  lockDir,
    21  		file: filepath.Join(lockDir, lockFileName),
    22  	}
    23  	ans.LockImpl = NewLockImpl(ans)
    24  	return ans
    25  }
    26  
    27  func (lock *SimpleFSLock) Obtain() (ok bool, err error) {
    28  	// Ensure that lockDir exists and is a directory:
    29  	var fi os.FileInfo
    30  	fi, err = os.Stat(lock.dir)
    31  	if err == nil { // exists
    32  		if !fi.IsDir() {
    33  			err = errors.New(fmt.Sprintf("Found regular file where directory expected: %v", lock.dir))
    34  			return
    35  		}
    36  	} else if os.IsNotExist(err) {
    37  		err = os.Mkdir(lock.dir, 0755)
    38  		if err != nil { // IO error
    39  			return
    40  		}
    41  	} else { // IO error
    42  		return
    43  	}
    44  	var f *os.File
    45  	if f, err = os.Create(lock.file); err == nil {
    46  		fmt.Printf("File '%v' is created.\n", f.Name())
    47  		ok = true
    48  		defer f.Close()
    49  	}
    50  	return
    51  
    52  }
    53  
    54  func (lock *SimpleFSLock) Close() error {
    55  	return os.Remove(lock.file)
    56  }
    57  
    58  func (lock *SimpleFSLock) IsLocked() bool {
    59  	f, err := os.Open(lock.file)
    60  	if err == nil {
    61  		defer f.Close()
    62  	}
    63  	return err == nil || os.IsExist(err)
    64  }
    65  
    66  func (lock *SimpleFSLock) String() string {
    67  	return fmt.Sprintf("SimpleFSLock@%v", lock.file)
    68  }
    69  
    70  /*
    71  Implements LockFactory using os.Create().
    72  
    73  NOTE: This API may has the same issue as the one in Lucene Java that
    74  the write lock may not be released when Go program exists abnormally.
    75  
    76  When this happens, an error is returned when trying to create a
    77  writer, in which case you need to explicitly clear the lock file
    78  first. You can either manually remove the file, or use
    79  UnlockDirectory() API. But, first be certain that no writer is in
    80  fact writing to the index otherwise you can easily corrupt your index.
    81  
    82  If you suspect that this or any other LockFactory is not working
    83  properly in your environment, you can easily test it by using
    84  VerifyingLockFactory, LockVerifyServer and LockStressTest.
    85  */
    86  type SimpleFSLockFactory struct {
    87  	*FSLockFactory
    88  }
    89  
    90  func NewSimpleFSLockFactory(path string) *SimpleFSLockFactory {
    91  	ans := &SimpleFSLockFactory{}
    92  	ans.FSLockFactory = newFSLockFactory()
    93  	ans.setLockDir(path)
    94  	return ans
    95  }
    96  
    97  func (f *SimpleFSLockFactory) Make(name string) Lock {
    98  	if f.lockPrefix != "" {
    99  		name = fmt.Sprintf("%v-%v", f.lockPrefix, name)
   100  	}
   101  	return newSimpleFSLock(f.lockDir, name)
   102  }
   103  
   104  func (f *SimpleFSLockFactory) Clear(name string) error {
   105  	if f.lockPrefix != "" {
   106  		name = fmt.Sprintf("%v-%v", f.lockPrefix, name)
   107  	}
   108  	return os.Remove(filepath.Join(f.lockDir, name))
   109  }
   110  
   111  type SimpleFSDirectory struct {
   112  	*FSDirectory
   113  }
   114  
   115  func NewSimpleFSDirectory(path string) (d *SimpleFSDirectory, err error) {
   116  	d = &SimpleFSDirectory{}
   117  	d.FSDirectory, err = newFSDirectory(d, path)
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  	return
   122  }
   123  
   124  func (d *SimpleFSDirectory) OpenInput(name string, context IOContext) (IndexInput, error) {
   125  	d.EnsureOpen()
   126  	fpath := filepath.Join(d.path, name)
   127  	// fmt.Printf("Opening %v...\n", fpath)
   128  	return newSimpleFSIndexInput(fmt.Sprintf("SimpleFSIndexInput(path='%v')", fpath), fpath, context)
   129  }
   130  
   131  // func (d *SimpleFSDirectory) CreateSlicer(name string, ctx IOContext) (slicer IndexInputSlicer, err error) {
   132  // 	d.EnsureOpen()
   133  // 	f, err := os.Open(filepath.Join(d.path, name))
   134  // 	if err != nil {
   135  // 		return nil, err
   136  // 	}
   137  // 	return &fileIndexInputSlicer{f, ctx, d.chunkSize}, nil
   138  // }
   139  
   140  // type fileIndexInputSlicer struct {
   141  // 	file      *os.File
   142  // 	ctx       IOContext
   143  // 	chunkSize int
   144  // }
   145  
   146  // func (s *fileIndexInputSlicer) Close() error {
   147  // 	err := s.file.Close()
   148  // 	if err != nil {
   149  // 		fmt.Printf("Closing %v failed: %v\n", s.file.Name(), err)
   150  // 	}
   151  // 	return err
   152  // }
   153  
   154  // func (s *fileIndexInputSlicer) OpenSlice(desc string, offset, length int64) IndexInput {
   155  // 	return newSimpleFSIndexInputFromFileSlice(fmt.Sprintf("SimpleFSIndexInput(%v in path='%v' slice=%v:%v)",
   156  // 		desc, s.file.Name(), offset, offset+length),
   157  // 		s.file, offset, length, bufferSize(s.ctx), s.chunkSize)
   158  // }
   159  
   160  // func (s *fileIndexInputSlicer) OpenFullSlice() IndexInput {
   161  // 	fi, err := s.file.Stat()
   162  // 	if err != nil {
   163  // 		panic(err)
   164  // 	}
   165  // 	return s.OpenSlice("full-slice", 0, fi.Size())
   166  // }
   167  
   168  /*
   169  The maximum chunk size is 8192 bytes, becaues Java RamdomAccessFile
   170  mallocs a native buffer outside of stack if the read buffer size is
   171  larger. GoLucene takes the same default value.
   172  TODO: test larger value here
   173  */
   174  const CHUNK_SIZE = 8192
   175  
   176  type SimpleFSIndexInput struct {
   177  	*BufferedIndexInput
   178  	fileLock sync.Locker
   179  	// the file channel we will read from
   180  	file *os.File
   181  	// is this instance a clone and hence does not own the file to close it
   182  	isClone bool
   183  	// start offset: non-zero in the slice case
   184  	off int64
   185  	// end offset (start+length)
   186  	end int64
   187  }
   188  
   189  func newSimpleFSIndexInput(desc, path string, ctx IOContext) (*SimpleFSIndexInput, error) {
   190  	f, err := os.Open(path)
   191  	if err != nil {
   192  		return nil, err
   193  	}
   194  	fstat, err := f.Stat()
   195  	if err != nil {
   196  		return nil, err
   197  	}
   198  	ans := new(SimpleFSIndexInput)
   199  	ans.BufferedIndexInput = newBufferedIndexInput(ans, desc, ctx)
   200  	ans.file = f
   201  	ans.off = 0
   202  	ans.end = fstat.Size()
   203  	ans.fileLock = &sync.Mutex{}
   204  	return ans, nil
   205  }
   206  
   207  func newSimpleFSIndexInputFromFileSlice(desc string, file *os.File, off, length int64, bufferSize int) *SimpleFSIndexInput {
   208  	ans := new(SimpleFSIndexInput)
   209  	ans.BufferedIndexInput = newBufferedIndexInputBySize(ans, desc, bufferSize)
   210  	ans.file = file
   211  	ans.off = off
   212  	ans.end = off + length
   213  	ans.isClone = true
   214  	return ans
   215  }
   216  
   217  func (in *SimpleFSIndexInput) Close() error {
   218  	if !in.isClone {
   219  		return in.file.Close()
   220  	}
   221  	return nil
   222  }
   223  
   224  func (in *SimpleFSIndexInput) Clone() IndexInput {
   225  	ans := &SimpleFSIndexInput{
   226  		in.BufferedIndexInput.Clone(),
   227  		in.fileLock,
   228  		in.file,
   229  		true,
   230  		in.off,
   231  		in.end,
   232  	}
   233  	ans.spi = ans
   234  	return ans
   235  }
   236  
   237  func (in *SimpleFSIndexInput) Slice(desc string, offset, length int64) (IndexInput, error) {
   238  	assert2(offset >= 0 && length >= 0 && offset+length <= in.Length(),
   239  		"slice() %v out of bounds: %v", desc, in)
   240  	ans := newSimpleFSIndexInputFromFileSlice(desc, in.file, in.off+offset, length, in.bufferSize)
   241  	ans.fileLock = in.fileLock // share same file lock
   242  	return ans, nil
   243  }
   244  
   245  func (in *SimpleFSIndexInput) Length() int64 {
   246  	return in.end - in.off
   247  }
   248  
   249  func (in *SimpleFSIndexInput) readInternal(buf []byte) error {
   250  	length := len(buf)
   251  	in.fileLock.Lock()
   252  	defer in.fileLock.Unlock()
   253  
   254  	// TODO make use of Go's relative Seek or ReadAt function
   255  	position := in.off + in.FilePointer()
   256  	_, err := in.file.Seek(position, 0)
   257  	if err != nil {
   258  		return err
   259  	}
   260  
   261  	if position+int64(length) > in.end {
   262  		return errors.New(fmt.Sprintf("read past EOF: %v", in))
   263  	}
   264  
   265  	total := 0
   266  	for {
   267  		readLength := length - total
   268  		if CHUNK_SIZE < readLength {
   269  			readLength = CHUNK_SIZE
   270  		}
   271  		// FIXME verify slice is working
   272  		i, err := in.file.Read(buf[total : total+readLength])
   273  		if err != nil {
   274  			return errors.New(fmt.Sprintf("%v: %v", err, in))
   275  		}
   276  		total += i
   277  		if total >= length {
   278  			break
   279  		}
   280  	}
   281  	return nil
   282  }
   283  
   284  func (in *SimpleFSIndexInput) seekInternal(pos int64) error { return nil }