#include #include #include "mdb.h" #include "impl.h" enum { MAXPOOLPAGE = 16, }; int mdb_initpool(MDB *db) { int i; Page *p; for(i = 0; i < MAXPOOLPAGE; i++){ p = malloc(sizeof(*p)); if(p == nil) return -1; mdb_putpage(db, p); } return 0; } /* cannot fail */ Page* mdb_getpage(MDB *db) { Page *p; if(db->pool == nil){ p = mdb_calloc(db, 1, sizeof(*p)); } else { p = db->pool; db->pool = p->next; db->npool--; } assert(p != nil); setmalloctag(p, getcallerpc(&db)); memset(p, 0, sizeof(*p)); p->id = NOPAGE; return p; } void mdb_putpage(MDB *db, Page *p) { if(db->npool == MAXPOOLPAGE){ free(p); return; } p->next = db->pool; db->pool = p; db->npool++; } static Page* mdb_getcache(MDB *db, u32int id) { u32int idx; Page *p; if(0){ fprint(2, "getcache: dump\n"); int i; for(i = 0; i < nelem(db->cache); i++){ p = db->cache[i]; if(p != nil) fprint(2, "buq %d\n", i); for(; p != nil; p = p->next){ fprint(2, "page=%ud type=%ud\n", p->id, p->type); } } } idx = id % nelem(db->cache); for(p = db->cache[idx]; p != nil; p = p->next){ if(p->id == id) return p; } return nil; } /* cannot fail */ static Page* mdb_pagedup(MDB *db, Page *p) { Page *newp; newp = mdb_getpage(db); setmalloctag(newp, getcallerpc(&db)); memmove(newp, p, sizeof(*p)); return newp; } /* cannot fail */ Page* mdb_readpage(MDB *db, u32int id, u32int type) { long rv; u32int idx; uchar buf[BLOCK]; Page *p; //fprint(2, "mdb_readpage: page=%ud expect type=%ud\n", id, type); p = mdb_getcache(db, id); if(p != nil) return mdb_pagedup(db, p); p = mdb_getpage(db); do { rv = pread(db->fd, buf, sizeof(buf), sizeof(buf)*id); } while(rv < 0 && mdb_error_io(db) == 0); if(rv < 0) abort(); if(rv != sizeof(buf)) abort(); p->id = id; mdb_unpack(db, p, buf); if(type != T_ANY && p->type != type) mdb_error_corrupt(db); idx = id % nelem(db->cache); p->next = db->cache[idx]; db->cache[idx] = p; db->ncache++; return mdb_pagedup(db, p); } /* TODO: dont blow the cache on sync */ static void mdb_writepage_uncached(MDB *db, Page *p) { long rv; int sz; uchar buf[BLOCK]; memset(buf, 0, sizeof(buf)); sz = mdb_pack(db, p, buf); assert(sz <= sizeof(buf)); fmtinstall('H', encodefmt); //fprint(2, "mdb_writepage_uncached: write block %ud %d bytes\n%.*H\n", p->id, sz, sz, buf); do { rv = pwrite(db->fd, buf, sizeof(buf), sizeof(buf)*p->id); } while(rv < 0 && mdb_error_io(db) == 0); if(rv < 0) abort(); mdb_putpage(db, p); } void mdb_writepage(MDB *db, Page *p) { u32int idx; Page **pp; if(0){ fprint(2, "===== writepage: before\n"); Page *ppp; int i; for(i = 0; i < nelem(db->cache); i++){ ppp = db->cache[i]; if(ppp != nil) fprint(2, "buq %d\n", i); for(; ppp != nil; ppp = ppp->next){ fprint(2, "page=%ud type=%ud\n", ppp->id, ppp->type); } } } assert(p->type > 0 && p->type < T_MAX); assert(p->id < db->size); idx = p->id % nelem(db->cache); //fprint(2, "mdb_writepage: pid=%ud type=%ud idx=%ud\n", p->id, p->type, idx); for(pp = &db->cache[idx]; *pp != nil; pp = &(*pp)->next){ if((*pp)->id == p->id){ //fprint(2, "mdb_writepage: swap %ud -> %ud\n", (*pp)->type, p->type); //fprint(2, "page id=%ud type=%ud next=%p\n", p->id, p->type, p->next); /* XXX: if pages are the same do nothing */ if(*pp == p) return; p->next = (*pp)->next; mdb_putpage(db, *pp); *pp = p; // goto out; return; } } p->next = db->cache[idx]; db->cache[idx] = p; db->ncache++; out: if(0){ fprint(2, "===== writepage: after\n"); Page *ppp; int i; for(i = 0; i < nelem(db->cache); i++){ ppp = db->cache[i]; if(ppp != nil) fprint(2, "buq %d\n", i); for(; ppp != nil; ppp = ppp->next){ fprint(2, "page=%ud type=%ud\n", ppp->id, ppp->type); } } } } int mdb_sync(MDB *db) { int i; Page *p, *next; fprint(2, "mdb_sync: begin %d blocks. ", db->ncache); for(i = 0; i < nelem(db->cache); i++){ for(p = db->cache[i]; p != nil; p = next){ next = p->next; fprint(2, "%ud.. ", p->id); mdb_writepage_uncached(db, p); } db->cache[i] = nil; } db->ncache = 0; fprint(2, "ok.\n"); return 0; }