Exemplo n.º 1
0
/* allocate some space from the free list. The offset returned points
   to a unconnected tdb1_record within the database with room for at
   least length bytes of total data

   0 is returned if the space could not be allocated
 */
tdb1_off_t tdb1_allocate(struct tdb_context *tdb, tdb1_len_t length, struct tdb1_record *rec)
{
	tdb1_off_t rec_ptr, last_ptr, newrec_ptr;
	struct {
		tdb1_off_t rec_ptr, last_ptr;
		tdb1_len_t rec_len;
	} bestfit;
	float multiplier = 1.0;

	if (tdb1_lock(tdb, -1, F_WRLCK) == -1)
		return 0;

	/* over-allocate to reduce fragmentation */
	length *= 1.25;

	/* Extra bytes required for tailer */
	length += sizeof(tdb1_off_t);
	length = TDB1_ALIGN(length, TDB1_ALIGNMENT);

 again:
	last_ptr = TDB1_FREELIST_TOP;

	/* read in the freelist top */
	if (tdb1_ofs_read(tdb, TDB1_FREELIST_TOP, &rec_ptr) == -1)
		goto fail;

	bestfit.rec_ptr = 0;
	bestfit.last_ptr = 0;
	bestfit.rec_len = 0;

	/*
	   this is a best fit allocation strategy. Originally we used
	   a first fit strategy, but it suffered from massive fragmentation
	   issues when faced with a slowly increasing record size.
	 */
	while (rec_ptr) {
		if (tdb1_rec_free_read(tdb, rec_ptr, rec) == -1) {
			goto fail;
		}

		if (rec->rec_len >= length) {
			if (bestfit.rec_ptr == 0 ||
			    rec->rec_len < bestfit.rec_len) {
				bestfit.rec_len = rec->rec_len;
				bestfit.rec_ptr = rec_ptr;
				bestfit.last_ptr = last_ptr;
			}
		}

		/* move to the next record */
		last_ptr = rec_ptr;
		rec_ptr = rec->next;

		/* if we've found a record that is big enough, then
		   stop searching if its also not too big. The
		   definition of 'too big' changes as we scan
		   through */
		if (bestfit.rec_len > 0 &&
		    bestfit.rec_len < length * multiplier) {
			break;
		}

		/* this multiplier means we only extremely rarely
		   search more than 50 or so records. At 50 records we
		   accept records up to 11 times larger than what we
		   want */
		multiplier *= 1.05;
	}

	if (bestfit.rec_ptr != 0) {
		if (tdb1_rec_free_read(tdb, bestfit.rec_ptr, rec) == -1) {
			goto fail;
		}

		newrec_ptr = tdb1_allocate_ofs(tdb, length, bestfit.rec_ptr,
					      rec, bestfit.last_ptr);
		tdb1_unlock(tdb, -1, F_WRLCK);
		return newrec_ptr;
	}

	/* we didn't find enough space. See if we can expand the
	   database and if we can then try again */
	if (tdb1_expand(tdb, length + sizeof(*rec)) == 0)
		goto again;
 fail:
	tdb1_unlock(tdb, -1, F_WRLCK);
	return 0;
}