github.com/scottcagno/storage@v1.8.0/pkg/datafile/datafile.go (about)

     1  package datafile
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"log"
     7  	"math/bits"
     8  	"os"
     9  	"path/filepath"
    10  	"strconv"
    11  )
    12  
    13  type kind uint8
    14  
    15  const (
    16  	blockSize   = 4096
    17  	chunkSize   = 16 * blockSize
    18  	extentSize  = 16 * chunkSize
    19  	segmentSize = 16 * extentSize
    20  
    21  	blockSizeMask   = blockSize - 1
    22  	chunkSizeMask   = chunkSize - 1
    23  	extentSizeMask  = extentSize - 1
    24  	segmentSizeMask = segmentSize - 1
    25  
    26  	fullBlock   = kind(1)
    27  	firstBlock  = kind(2)
    28  	middleBlock = kind(3)
    29  	lastBlock   = kind(4)
    30  )
    31  
    32  var (
    33  	ErrOutOfBounds = errors.New("out of bounds")
    34  )
    35  
    36  // align aligns sizes
    37  func align(n int, mask int) int {
    38  	return ((n + 2) + mask) &^ mask
    39  }
    40  
    41  // malloc allocates heap memory and copies d into it
    42  func malloc(d []byte, sizeMask int) ([]byte, error) {
    43  	if len(d) > sizeMask {
    44  		return nil, ErrOutOfBounds
    45  	}
    46  	heap := make([]byte, align(0, sizeMask))
    47  	copy(heap[0:], d)
    48  	return heap, nil
    49  }
    50  
    51  // free releases memory to be cleaned up
    52  func free(d *[]byte) {
    53  	*d = nil
    54  }
    55  
    56  func wordSize(t interface{}) int {
    57  	switch t.(type) {
    58  	case uint8:
    59  		return bits.OnesCount(uint(^uint8(0)))
    60  	case uint16:
    61  		return bits.OnesCount(uint(^uint16(0)))
    62  	case uint32:
    63  		return bits.OnesCount(uint(^uint32(0)))
    64  	case uint64:
    65  		return bits.OnesCount(uint(^uint64(0)))
    66  	}
    67  	return -1
    68  }
    69  
    70  type bitsetU16 uint16
    71  
    72  func (bs *bitsetU16) has(i uint) bool {
    73  	return *bs&(1<<(i&(15))) != 0
    74  }
    75  
    76  func (bs *bitsetU16) set(i uint) {
    77  	*bs |= 1 << (i & (15))
    78  }
    79  
    80  func (bs *bitsetU16) unset(i uint) {
    81  	*bs &^= 1 << (i & (15))
    82  }
    83  
    84  func (bs bitsetU16) String() string {
    85  	// print binary value of bitset
    86  	//var res string = "16" // set this to the "bit resolution" you'd like to see
    87  	var res = strconv.Itoa(16)
    88  	return fmt.Sprintf("%."+res+"b (%s bits)", bs, res)
    89  }
    90  
    91  // header represents a block header
    92  type header struct {
    93  	stat uint8  // stat is the block status
    94  	kind uint8  // kind is the type of block
    95  	used uint16 // used is the length of the data
    96  	free uint16 // free is the free bytes at the end
    97  }
    98  
    99  // block is a contiguous set of bytes 4kb in size
   100  type block struct {
   101  	*header
   102  	data []byte
   103  }
   104  
   105  // Write is the write method for a block
   106  func (b *block) Write(d []byte) (int, error) {
   107  	// allocate
   108  	data, err := malloc(d, blockSizeMask)
   109  	if err != nil {
   110  		return -1, err
   111  	}
   112  	b.data = data
   113  	return -1, nil
   114  }
   115  
   116  // chunk is a contiguous set of 16 blocks
   117  type chunk struct {
   118  	free bitsetU16 // bitmap of free blocks in chunk
   119  	data [chunkSize]byte
   120  }
   121  
   122  // Write is the write method for a chunk
   123  func (b *chunk) Write(d []byte) (int, error) {
   124  	// placeholder
   125  	return -1, nil
   126  }
   127  
   128  // extent is a contiguous set of 16 chunks
   129  type extent struct {
   130  	free bitsetU16 // bitmap of free blocks in extent
   131  	data [extentSize]byte
   132  }
   133  
   134  // Write is the write method for an extent
   135  func (b *extent) Write(d []byte) (int, error) {
   136  	// placeholder
   137  	return -1, nil
   138  }
   139  
   140  // segment is a contiguous set of 16 extents
   141  type segment struct {
   142  	free bitsetU16 // bitmap of free extents
   143  	data [segmentSize]byte
   144  }
   145  
   146  // Write is the write method for a segment
   147  func (b *segment) Write(d []byte) (int, error) {
   148  	// placeholder
   149  	return -1, nil
   150  }
   151  
   152  // datafile is a file containing one or more segments
   153  type datafile struct {
   154  	fp   *os.File
   155  	data []*segment
   156  }
   157  
   158  func openDataFile(path string) (*datafile, error) {
   159  	// sanitize path
   160  	path, err := filepath.Abs(path)
   161  	if err != nil {
   162  		return nil, err
   163  	}
   164  	// split path
   165  	dir, name := filepath.Split(filepath.ToSlash(path))
   166  	// init file and dirs
   167  	var fp *os.File
   168  	_, err = os.Stat(path)
   169  	if os.IsNotExist(err) {
   170  		// create dir
   171  		err = os.MkdirAll(dir, os.ModeDir)
   172  		if err != nil {
   173  			return nil, err
   174  		}
   175  		// create file
   176  		fp, err = os.Create(filepath.Join(dir, name))
   177  		if err != nil {
   178  			return nil, err
   179  		}
   180  		// truncate to size
   181  		err = fp.Truncate(segmentSize)
   182  		if err != nil {
   183  			return nil, err
   184  		}
   185  		// close file
   186  		err = fp.Close()
   187  		if err != nil {
   188  			return nil, err
   189  		}
   190  	}
   191  	// open existing file
   192  	fp, err = os.OpenFile(path, os.O_RDWR, 0666)
   193  	if err != nil {
   194  		return nil, err
   195  	}
   196  	// create data file
   197  	df := &datafile{
   198  		fp:   fp,
   199  		data: make([]*segment, 0),
   200  	}
   201  	// call load
   202  	err = df.load()
   203  	if err != nil {
   204  		return nil, err
   205  	}
   206  	// return data file
   207  	return df, nil
   208  }
   209  
   210  func (df *datafile) load() error {
   211  	return nil
   212  }
   213  
   214  func (df *datafile) Sync() error {
   215  	err := df.fp.Sync()
   216  	if err != nil {
   217  		return err
   218  	}
   219  	err = df.fp.Close()
   220  	if err != nil {
   221  		return err
   222  	}
   223  	return nil
   224  }
   225  
   226  func (df *datafile) Close() error {
   227  	err := df.fp.Close()
   228  	if err != nil {
   229  		return err
   230  	}
   231  	return nil
   232  }
   233  
   234  func OpenFile(path string) (*os.File, error) {
   235  	_, err := os.Stat(path)
   236  	if os.IsNotExist(err) {
   237  		dir, file := filepath.Split(path)
   238  		err = os.MkdirAll(dir, os.ModeDir)
   239  		if err != nil {
   240  			return nil, err
   241  		}
   242  		fd, err := os.Create(dir + file)
   243  		if err != nil {
   244  			return nil, err
   245  		}
   246  		err = fd.Close()
   247  		if err != nil {
   248  			return fd, err
   249  		}
   250  	}
   251  	fd, err := os.OpenFile(path, os.O_RDWR, 0666) // os.ModeSticky
   252  	if err != nil {
   253  		return nil, err
   254  	}
   255  	return fd, nil
   256  }
   257  
   258  func CreateFileSize(path string, size int64) error {
   259  	_, err := os.Stat(path)
   260  	if os.IsNotExist(err) {
   261  		dir, file := filepath.Split(path)
   262  		err = os.MkdirAll(dir, os.ModeDir)
   263  		if err != nil {
   264  			return err
   265  		}
   266  		fd, err := os.Create(dir + file)
   267  		if err != nil {
   268  			return err
   269  		}
   270  		err = fd.Truncate(size)
   271  		if err != nil {
   272  			return err
   273  		}
   274  		err = fd.Close()
   275  		if err != nil {
   276  			return err
   277  		}
   278  	}
   279  	return nil
   280  }
   281  
   282  func TruncateFile(fd *os.File, size int64) error {
   283  	err := fd.Truncate(size)
   284  	if err != nil {
   285  		return err
   286  	}
   287  	return nil
   288  }
   289  
   290  func CleanPath(path string) (string, string) {
   291  	path, err := filepath.Abs(path)
   292  	if err != nil {
   293  		log.Panicf("cleaning path: %v\n", err)
   294  	}
   295  	return filepath.Split(filepath.ToSlash(path))
   296  }