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 }