/*---------------------------------------------------------------------------*/ static coffee_page_t next_file(coffee_page_t page, struct file_header *hdr) { if(HDR_FREE(*hdr)) { return (page + COFFEE_PAGES_PER_SECTOR) & ~(COFFEE_PAGES_PER_SECTOR - 1); } else if(HDR_ISOLATED(*hdr)) { return page + 1; } return page + hdr->max_pages; }
/*---------------------------------------------------------------------------*/ static coffee_page_t next_file(coffee_page_t page, struct file_header *hdr) { /* * The quick-skip algorithm for finding file extents is the most * essential part of Coffee. The file allocation rules enables this * algorithm to quickly jump over free areas and allocated extents * after reading single headers and determining their status. * * The worst-case performance occurs when we encounter multiple long * sequences of isolated pages, but such sequences are uncommon and * always shorter than a sector. */ if(HDR_FREE(*hdr)) { return (page + COFFEE_PAGES_PER_SECTOR) & ~(COFFEE_PAGES_PER_SECTOR - 1); } else if(HDR_ISOLATED(*hdr)) { return page + 1; } return page + hdr->max_pages; }
/*---------------------------------------------------------------------------*/ static coffee_page_t get_sector_status(uint16_t sector, struct sector_status *stats) { static coffee_page_t skip_pages; static char last_pages_are_active; struct file_header hdr; coffee_page_t active, obsolete, free; coffee_page_t sector_start, sector_end; coffee_page_t page; memset(stats, 0, sizeof(*stats)); active = obsolete = free = 0; /* * get_sector_status() is an iterative function using local static * state. It therefore requires that the caller starts iterating from * sector 0 in order to reset the internal state. */ if(sector == 0) { skip_pages = 0; last_pages_are_active = 0; } sector_start = sector * COFFEE_PAGES_PER_SECTOR; sector_end = sector_start + COFFEE_PAGES_PER_SECTOR; /* * Account for pages belonging to a file starting in a previous * segment that extends into this segment. If the whole segment is * covered, we do not need to continue counting pages in this iteration. */ if(last_pages_are_active) { if(skip_pages >= COFFEE_PAGES_PER_SECTOR) { stats->active = COFFEE_PAGES_PER_SECTOR; skip_pages -= COFFEE_PAGES_PER_SECTOR; return 0; } active = skip_pages; } else { if(skip_pages >= COFFEE_PAGES_PER_SECTOR) { stats->obsolete = COFFEE_PAGES_PER_SECTOR; skip_pages -= COFFEE_PAGES_PER_SECTOR; return skip_pages >= COFFEE_PAGES_PER_SECTOR ? 0 : skip_pages; } obsolete = skip_pages; } /* Determine the amount of pages of each type that have not been accounted for yet in the current sector. */ for(page = sector_start + skip_pages; page < sector_end;) { read_header(&hdr, page); last_pages_are_active = 0; if(HDR_ACTIVE(hdr)) { last_pages_are_active = 1; page += hdr.max_pages; active += hdr.max_pages; } else if(HDR_ISOLATED(hdr)) { page++; obsolete++; } else if(HDR_OBSOLETE(hdr)) { page += hdr.max_pages; obsolete += hdr.max_pages; } else { free = sector_end - page; break; } } /* * Determine the amount of pages in the following sectors that * should be remembered for the next iteration. This is necessary * because no page except the first of a file contains information * about what type of page it is. A side effect of remembering this * amount is that there is no need to read in the headers of each * of these pages from the storage. */ skip_pages = active + obsolete + free - COFFEE_PAGES_PER_SECTOR; if(skip_pages > 0) { if(last_pages_are_active) { active = COFFEE_PAGES_PER_SECTOR - obsolete; } else { obsolete = COFFEE_PAGES_PER_SECTOR - active; } } stats->active = active; stats->obsolete = obsolete; stats->free = free; /* * To avoid unnecessary page isolation, we notify the caller that * "skip_pages" pages should be isolated only if the current file extent * ends in the next sector. If the file extent ends in a more distant * sector, however, the garbage collection can free the next sector * immediately without requiring page isolation. */ return (last_pages_are_active || (skip_pages >= COFFEE_PAGES_PER_SECTOR)) ? 0 : skip_pages; }
/*---------------------------------------------------------------------------*/ static coffee_page_t get_sector_status(uint16_t sector, struct sector_status *stats) { static coffee_page_t skip_pages; static char last_pages_are_active; struct file_header hdr; coffee_page_t active, obsolete, free; coffee_page_t sector_start, sector_end; coffee_page_t page; memset(stats, 0, sizeof(*stats)); active = obsolete = free = 0; if(sector == 0) { skip_pages = 0; last_pages_are_active = 0; } sector_start = sector * COFFEE_PAGES_PER_SECTOR; sector_end = sector_start + COFFEE_PAGES_PER_SECTOR; if(last_pages_are_active) { if(skip_pages >= COFFEE_PAGES_PER_SECTOR) { stats->active = COFFEE_PAGES_PER_SECTOR; skip_pages -= COFFEE_PAGES_PER_SECTOR; return 0; } active = skip_pages; } else { if(skip_pages >= COFFEE_PAGES_PER_SECTOR) { stats->obsolete = COFFEE_PAGES_PER_SECTOR; skip_pages -= COFFEE_PAGES_PER_SECTOR; return skip_pages >= COFFEE_PAGES_PER_SECTOR ? 0 : skip_pages; } obsolete = skip_pages; } for(page = sector_start + skip_pages; page < sector_end;) { read_header(&hdr, page); last_pages_are_active = 0; if(HDR_ACTIVE(hdr)) { last_pages_are_active = 1; page += hdr.max_pages; active += hdr.max_pages; } else if(HDR_ISOLATED(hdr)) { page++; obsolete++; } else if(HDR_OBSOLETE(hdr)) { page += hdr.max_pages; obsolete += hdr.max_pages; } else { free = sector_end - page; break; } } skip_pages = active + obsolete + free - COFFEE_PAGES_PER_SECTOR; if(skip_pages > 0) { if(last_pages_are_active) { active = COFFEE_PAGES_PER_SECTOR - obsolete; } else { obsolete = COFFEE_PAGES_PER_SECTOR - active; } } stats->active = active; stats->obsolete = obsolete; stats->free = free; return (last_pages_are_active || (skip_pages >= COFFEE_PAGES_PER_SECTOR)) ? 0 : skip_pages; }