예제 #1
0
파일: falloc.go 프로젝트: sinfomicien/rkt
// New returns a new File backed by store or an error if any.
// Any existing data in store are discarded.
func New(store storage.Accessor) (f *File, err error) {
	f = &File{f: store}
	return f, storage.Mutate(store, func() (err error) {
		if err = f.f.Truncate(0); err != nil {
			return &ECreate{f.f.Name(), err}
		}

		if _, err = f.Alloc(hdr[1:]); err != nil { //TODO internal panicking versions of the exported fns.
			return
		}

		if _, err = f.Alloc(nil); err != nil { // (empty) root @1
			return
		}

		b := make([]byte, 3856*14)
		for i := 1; i <= 3856; i++ {
			Handle(i).Put(b[(i-1)*14:])
		}
		if _, err = f.Alloc(b); err != nil {
			return
		}

		f.canfree = f.atoms
		return
	})
}
예제 #2
0
파일: falloc.go 프로젝트: sinfomicien/rkt
// Alloc stores b in a newly allocated space and returns its handle and an error if any.
func (f *File) Alloc(b []byte) (handle Handle, err error) {
	err = storage.Mutate(f.Accessor(), func() (err error) {
		rqAtoms := rq2Atoms(len(b))
		if rqAtoms > 3856 {
			return &EBadRequest{f.f.Name(), len(b)}
		}

		for foundsize, foundp := range f.freetab[rqAtoms:] {
			if foundp != 0 {
				// this works only for the current unique sizes list (except the last item!)
				size := int64(foundsize) + rqAtoms
				handle = Handle(foundp)
				if size == 3856 {
					buf := make([]byte, 7)
					f.read(buf, int64(handle)<<4+15)
					(*Handle)(&size).Get(buf)
				}
				f.delFree(int64(handle), size)
				if rqAtoms < size {
					f.addFree(int64(handle)+rqAtoms, size-rqAtoms)
				}
				f.writeUsed(b, int64(handle))
				return
			}
		}

		handle = Handle(f.extend(b))
		return
	})
	return
}
예제 #3
0
파일: falloc.go 프로젝트: sinfomicien/rkt
// Close closes f and returns an error if any.
func (f *File) Close() (err error) {
	return storage.Mutate(f.Accessor(), func() (err error) {
		if err = f.f.Close(); err != nil {
			err = &EClose{f.f.Name(), err}
		}
		return
	})
}
예제 #4
0
파일: falloc.go 프로젝트: sinfomicien/rkt
// Free frees space associated with handle and returns an error if any. Passing an invalid
// handle to Free or reusing handle afterwards will probably corrupt the database or provide
// invalid data on Read. It's like corrupting memory via passing an invalid pointer to C.free()
// or reusing that pointer.
func (f *File) Free(handle Handle) (err error) {
	return storage.Mutate(f.Accessor(), func() (err error) {
		atom := int64(handle)
		atoms, isFree := f.getSize(atom)
		if isFree || atom < f.canfree {
			return &EHandle{f.f.Name(), handle}
		}

		leftFree, rightFree := f.checkLeft(atom), f.checkRight(atom, atoms)
		switch {
		case leftFree != 0 && rightFree != 0:
			f.delFree(atom-leftFree, leftFree)
			f.delFree(atom+atoms, rightFree)
			f.addFree(atom-leftFree, leftFree+atoms+rightFree)
		case leftFree != 0 && rightFree == 0:
			f.delFree(atom-leftFree, leftFree)
			if atom+atoms == f.atoms { // the left free neighbour and this block together are an empy tail
				f.atoms = atom - leftFree
				f.f.Truncate(f.atoms << 4)
				return
			}

			f.addFree(atom-leftFree, leftFree+atoms)
		case leftFree == 0 && rightFree != 0:
			f.delFree(atom+atoms, rightFree)
			f.addFree(atom, atoms+rightFree)
		default: // leftFree == 0 && rightFree == 0
			if atom+atoms < f.atoms { // isolated inner block
				f.addFree(atom, atoms)
				return
			}

			f.f.Truncate(atom << 4) // isolated tail block, shrink file
			f.atoms = atom
		}
		return
	})
}
예제 #5
0
파일: falloc.go 프로젝트: sinfomicien/rkt
// Realloc reallocates space associted with handle to acomodate b, returns the newhandle
// newly associated with b and an error if any. If keepHandle == true then Realloc guarantees
// newhandle == handle even if the new data are larger then the previous content associated
// with handle. If !keepHandle && newhandle != handle then reusing handle will probably corrupt
// the database.
// The above effects are like corrupting memory/data via passing an invalid pointer to C.realloc().
func (f *File) Realloc(handle Handle, b []byte, keepHandle bool) (newhandle Handle, err error) {
	err = storage.Mutate(f.Accessor(), func() (err error) {
		switch handle {
		case 0, 2:
			return &EHandle{f.f.Name(), handle}
		case 1:
			keepHandle = true
		}
		newhandle = handle
		atom, newatoms := int64(handle), rq2Atoms(len(b))
		if newatoms > 3856 {
			return &EBadRequest{f.f.Name(), len(b)}
		}

		typ, oldatoms := f.getInfo(atom)
		switch {
		default:
			return &ECorrupted{f.f.Name(), atom << 4}
		case typ <= 0xfc: // non relocated used block
			switch {
			case newatoms == oldatoms: // in place replace
				f.writeUsed(b, atom)
			case newatoms < oldatoms: // in place shrink
				rightFree := f.checkRight(atom, oldatoms)
				if rightFree > 0 { // right join
					f.delFree(atom+oldatoms, rightFree)
				}
				f.addFree(atom+newatoms, oldatoms+rightFree-newatoms)
				f.writeUsed(b, atom)
			case newatoms > oldatoms:
				if rightFree := f.checkRight(atom, oldatoms); rightFree > 0 && newatoms <= oldatoms+rightFree {
					f.delFree(atom+oldatoms, rightFree)
					if newatoms < oldatoms+rightFree {
						f.addFree(atom+newatoms, oldatoms+rightFree-newatoms)
					}
					f.writeUsed(b, atom)
					return
				}

				if !keepHandle {
					f.Free(Handle(atom))
					newhandle, err = f.Alloc(b)
					return
				}

				// reloc
				newatom, e := f.Alloc(b)
				if e != nil {
					return e
				}

				buf := make([]byte, 16)
				buf[0] = 0xfd
				Handle(newatom).Put(buf[1:])
				f.Realloc(Handle(atom), buf[1:], true)
				f.write(buf[:1], atom<<4)
			}
		case typ == 0xfd: // reloc
			var target Handle
			buf := make([]byte, 7)
			f.read(buf, atom<<4+1)
			target.Get(buf)
			switch {
			case newatoms == 1:
				f.writeUsed(b, atom)
				f.Free(target)
			default:
				if rightFree := f.checkRight(atom, 1); rightFree > 0 && newatoms <= 1+rightFree {
					f.delFree(atom+1, rightFree)
					if newatoms < 1+rightFree {
						f.addFree(atom+newatoms, 1+rightFree-newatoms)
					}
					f.writeUsed(b, atom)
					f.Free(target)
					return
				}

				newtarget, e := f.Realloc(Handle(target), b, false)
				if e != nil {
					return e
				}

				if newtarget != target {
					Handle(newtarget).Put(buf)
					f.write(buf, atom<<4+1)
				}
			}
		}
		return
	})
	return
}
예제 #6
0
파일: map.go 프로젝트: cznic/dns
// Set stores value under partition, key in Store and returns an error if any.
func (s *Store) Set(partition uint32, key, value []byte) (err error) {
	return storage.Mutate(s.accessor, func() (err error) {
		lenK := len(key)
		var h = newFNV1a()
		h.writeUint32(partition)
		h.write(key)
		var ptrbuf = make([]byte, s.PtrBytes)
		hdelta := s.hdelta(h.hash(s.HashWidth))
		if _, err = s.accessor.ReadAt(ptrbuf, hdelta); err != nil {
			return
		}

		handle := s.getHandle(ptrbuf)
		if handle == 0 { // no collision, not set before
			if handle, err = s.Store.New(s.compose(0, partition, key, value)); err != nil {
				return
			}

			return s.setHandle(handle, hdelta)
		}

		// collision or overwrite existing
		var chunk []byte
		for {
			if chunk, err = s.Store.Get(handle); err != nil {
				return
			}

			if len(chunk) < s.PtrBytes+8 {
				return &falloc.ECorrupted{s.accessor.Name(), int64(handle) << 4}
			}

			rdoff := s.PtrBytes
			rdpartition := uint32(chunk[rdoff])<<24 |
				uint32(chunk[rdoff+1])<<16 |
				uint32(chunk[rdoff+2])<<8 |
				uint32(chunk[rdoff+3])
			rdoff += 4
			if rdpartition == partition {
				rdLenK := int(chunk[rdoff])<<8 | int(chunk[rdoff+1])
				rdoff += 4
				if rdLenK == lenK { // chunk key length OK
					if rdoff+lenK > len(chunk) {
						return &falloc.ECorrupted{
							s.accessor.Name(),
							int64(handle) << 4,
						}
					}

					if bytes.Compare(key, chunk[rdoff:rdoff+lenK]) == 0 { // hit, overwrite
						rdoff += lenK
						next := s.getHandle(chunk)
						return s.Store.Set(
							handle,
							s.compose(next, partition, key, value),
						)
					}
				}
			}

			next := s.getHandle(chunk)
			if next == 0 { // collision, not set before
				if next, err = s.Store.New(s.compose(0, partition, key, value)); err != nil {
					return
				}

				s.putHandle(next, chunk)          // link
				return s.Store.Set(handle, chunk) // write back updated chunk
			}

			handle = next
		}

		panic("unreachable")
	})
}