// check next meta whether free, if free, then do quick merge // replace_next == 0 means meta will replace next in free link // return 0 means do quick merge success int _slab_free_quick_merge(meta_t* meta, int replace_next) { meta_t* next, *prev; page_t* page = META_PAGE(meta); int shift = META_SHIFT(meta) + META_SIZE + meta->size; if (shift < getpagesize()) { next = META(meta, shift); if (!(next->color & META_COLOR_ALLOC)) { prev = next->free_prev > 0 ? PAGE_META(page, next->free_prev) : NULL; // quick merge with next meta->size += next->size + META_SIZE; if (replace_next == 0) { _slab_free_replace(next, meta); if (META_SHIFT(next) == page->free) { page->free = META_SHIFT(meta); } } // quick merge with prev if (prev && META_SHIFT(prev) + META_SIZE + prev->size == META_SHIFT(meta)) { prev->size += META_SIZE + meta->size; prev->free_next = meta->free_next; next = meta->free_next > 0 ? PAGE_META(page, meta->free_next) : NULL; if (next) { next->free_prev = META_SHIFT(prev); } } return 0; } } return -1; }
void* _slab_alloc(page_t* page, list_head_t* head, size_t sz) { int16_t shift; meta_t* meta, *next, *split; // no free memory if (page->free <= 0 || page->remain < (int)sz + (int)META_SIZE) { return NULL; } // loop free memory shift = page->free; while (shift > 0) { meta = PAGE_META(page, shift); shift = meta->free_next; // not enough, ignore if (meta->size < (int)sz) { meta = NULL; continue; } // just fit, erase from free list else if (meta->size <= (int)(sz + META_SIZE)) { next = _slab_free_erase(meta); // erase free-link head if (META_SHIFT(meta) == page->free) { page->free = next ? META_SHIFT(next) : -1; } meta->color |= META_COLOR_ALLOC; break; } // do split else { split = _slab_free_split(meta, sz); _slab_free_replace(meta, split); if (META_SHIFT(meta) == page->free) { page->free = META_SHIFT(split); } meta->color |= META_COLOR_ALLOC; break; } } if (meta) { page->remain -= (META_SIZE + meta->size); _slab_page_erase(page, head); return META_MEM(meta); } return NULL; }
/* * Called when bufp's page contains a partial key (index should be 1) * * All pages in the big key/data pair except bufp are freed. We cannot * free bufp because the page pointing to it is lost and we can't get rid * of its pointer. * * Returns: * 0 => OK *-1 => ERROR */ int __big_delete(HTAB *hashp, BUFHEAD *bufp) { BUFHEAD *last_bfp, *rbufp; uint16_t *bp, pageno; int key_done, n; size_t temp; rbufp = bufp; last_bfp = NULL; bp = (uint16_t *)(void *)bufp->page; pageno = 0; key_done = 0; while (!key_done || (bp[2] != FULL_KEY_DATA)) { if (bp[2] == FULL_KEY || bp[2] == FULL_KEY_DATA) key_done = 1; /* * If there is freespace left on a FULL_KEY_DATA page, then * the data is short and fits entirely on this page, and this * is the last page. */ if (bp[2] == FULL_KEY_DATA && FREESPACE(bp)) break; pageno = bp[bp[0] - 1]; rbufp->flags |= BUF_MOD; rbufp = __get_buf(hashp, (uint32_t)pageno, rbufp, 0); if (last_bfp) __free_ovflpage(hashp, last_bfp); last_bfp = rbufp; if (!rbufp) return (-1); /* Error. */ bp = (uint16_t *)(void *)rbufp->page; } /* * If we get here then rbufp points to the last page of the big * key/data pair. Bufp points to the first one -- it should now be * empty pointing to the next page after this pair. Can't free it * because we don't have the page pointing to it. */ /* This is information from the last page of the pair. */ n = bp[0]; pageno = bp[n - 1]; /* Now, bp is the first page of the pair. */ bp = (uint16_t *)(void *)bufp->page; if (n > 2) { /* There is an overflow page. */ bp[1] = pageno; bp[2] = OVFLPAGE; bufp->ovfl = rbufp->ovfl; } else /* This is the last page. */ bufp->ovfl = NULL; n -= 2; bp[0] = n; temp = hashp->BSIZE - PAGE_META(n); _DBFIT(temp, uint16_t); FREESPACE(bp) = (uint16_t)temp; OFFSET(bp) = hashp->BSIZE; bufp->flags |= BUF_MOD; if (rbufp) __free_ovflpage(hashp, rbufp); if (last_bfp && last_bfp != rbufp) __free_ovflpage(hashp, last_bfp); hashp->NKEYS--; return (0); }
/* * Big_insert * * You need to do an insert and the key/data pair is too big * * Returns: * 0 ==> OK *-1 ==> ERROR */ int __big_insert(HTAB *hashp, BUFHEAD *bufp, const DBT *key, const DBT *val) { uint16_t *p, n; size_t key_size, val_size; uint16_t space, move_bytes, off; char *cp, *key_data, *val_data; size_t temp; cp = bufp->page; /* Character pointer of p. */ p = (uint16_t *)(void *)cp; key_data = (char *)key->data; _DBFIT(key->size, int); key_size = key->size; val_data = (char *)val->data; _DBFIT(val->size, int); val_size = val->size; /* First move the Key */ temp = FREESPACE(p) - BIGOVERHEAD; _DBFIT(temp, uint16_t); space = (uint16_t)temp; while (key_size) { move_bytes = MIN(space, key_size); off = OFFSET(p) - move_bytes; memmove(cp + off, key_data, (size_t)move_bytes); key_size -= move_bytes; key_data += move_bytes; n = p[0]; p[++n] = off; p[0] = ++n; temp = off - PAGE_META(n); _DBFIT(temp, uint16_t); FREESPACE(p) = (uint16_t)temp; OFFSET(p) = off; p[n] = PARTIAL_KEY; bufp = __add_ovflpage(hashp, bufp); if (!bufp) return (-1); n = p[0]; if (!key_size) { space = FREESPACE(p); if (space) { move_bytes = MIN(space, val_size); /* * If the data would fit exactly in the * remaining space, we must overflow it to the * next page; otherwise the invariant that the * data must end on a page with FREESPACE * non-zero would fail. */ if (space == val_size && val_size == val->size) goto toolarge; off = OFFSET(p) - move_bytes; memmove(cp + off, val_data, (size_t)move_bytes); val_data += move_bytes; val_size -= move_bytes; p[n] = off; p[n - 2] = FULL_KEY_DATA; FREESPACE(p) = FREESPACE(p) - move_bytes; OFFSET(p) = off; } else { toolarge: p[n - 2] = FULL_KEY; } } p = (uint16_t *)(void *)bufp->page; cp = bufp->page; bufp->flags |= BUF_MOD; temp = FREESPACE(p) - BIGOVERHEAD; _DBFIT(temp, uint16_t); space = (uint16_t)temp; } /* Now move the data */ temp = FREESPACE(p) - BIGOVERHEAD; _DBFIT(temp, uint16_t); space = (uint16_t)temp; while (val_size) { move_bytes = MIN(space, val_size); /* * Here's the hack to make sure that if the data ends on the * same page as the key ends, FREESPACE is at least one. */ if (space == val_size && val_size == val->size) move_bytes--; off = OFFSET(p) - move_bytes; memmove(cp + off, val_data, (size_t)move_bytes); val_size -= move_bytes; val_data += move_bytes; n = p[0]; p[++n] = off; p[0] = ++n; temp = off - PAGE_META(n); _DBFIT(temp, uint16_t); FREESPACE(p) = (uint16_t)temp; OFFSET(p) = off; if (val_size) { p[n] = FULL_KEY; bufp = __add_ovflpage(hashp, bufp); if (!bufp) return (-1); cp = bufp->page; p = (uint16_t *)(void *)cp; } else p[n] = FULL_KEY_DATA; bufp->flags |= BUF_MOD; temp = FREESPACE(p) - BIGOVERHEAD; _DBFIT(temp, uint16_t); space = (uint16_t)temp; } return (0); }
/* * Big_insert * * You need to do an insert and the key/data pair is too big * * Returns: * 0 ==> OK *-1 ==> ERROR */ extern int dbm_big_insert(HTAB *hashp, BUFHEAD *bufp, const DBT *key, const DBT *val) { register uint16 *p; uint key_size, n, val_size; uint16 space, move_bytes, off; char *cp, *key_data, *val_data; cp = bufp->page; /* Character pointer of p. */ p = (uint16 *)cp; key_data = (char *)key->data; key_size = key->size; val_data = (char *)val->data; val_size = val->size; /* First move the Key */ for (space = FREESPACE(p) - BIGOVERHEAD; key_size; space = FREESPACE(p) - BIGOVERHEAD) { move_bytes = PR_MIN(space, key_size); off = OFFSET(p) - move_bytes; memmove(cp + off, key_data, move_bytes); key_size -= move_bytes; key_data += move_bytes; n = p[0]; p[++n] = off; p[0] = ++n; FREESPACE(p) = off - PAGE_META(n); OFFSET(p) = off; p[n] = PARTIAL_KEY; bufp = dbm_add_ovflpage(hashp, bufp); if (!bufp) return (-1); n = p[0]; if (!key_size) { if (FREESPACE(p)) { move_bytes = PR_MIN(FREESPACE(p), val_size); off = OFFSET(p) - move_bytes; p[n] = off; memmove(cp + off, val_data, move_bytes); val_data += move_bytes; val_size -= move_bytes; p[n - 2] = FULL_KEY_DATA; FREESPACE(p) = FREESPACE(p) - move_bytes; OFFSET(p) = off; } else p[n - 2] = FULL_KEY; } p = (uint16 *)bufp->page; cp = bufp->page; bufp->flags |= BUF_MOD; } /* Now move the data */ for (space = FREESPACE(p) - BIGOVERHEAD; val_size; space = FREESPACE(p) - BIGOVERHEAD) { move_bytes = PR_MIN(space, val_size); /* * Here's the hack to make sure that if the data ends on the * same page as the key ends, FREESPACE is at least one. */ if (space == val_size && val_size == val->size) move_bytes--; off = OFFSET(p) - move_bytes; memmove(cp + off, val_data, move_bytes); val_size -= move_bytes; val_data += move_bytes; n = p[0]; p[++n] = off; p[0] = ++n; FREESPACE(p) = off - PAGE_META(n); OFFSET(p) = off; if (val_size) { p[n] = FULL_KEY; bufp = dbm_add_ovflpage(hashp, bufp); if (!bufp) return (-1); cp = bufp->page; p = (uint16 *)cp; } else p[n] = FULL_KEY_DATA; bufp->flags |= BUF_MOD; } return (0); }