/* nailboard.c: NAILBOARD IMPLEMENTATION * * $Id$ * Copyright (c) 2014 Ravenbrook Limited. See end of file for license. * * .sources: . */ #include "bt.h" #include "check.h" #include "mpm.h" #include "nailboard.h" SRCID(nailboard, "$Id$"); Bool NailboardCheck(Nailboard board) { Index i; CHECKS(Nailboard, board); CHECKL(RangeCheck(&board->range)); CHECKL(0 < board->levelShift); for (i = 0; i < board->levels; ++i) { /* weak check for BTs @@@@ */ CHECKL(board->level[i] != NULL); } CHECKL(BoolCheck(board->newNails)); return TRUE; } /* nailboardStructSize -- return the size of the nailboard structure, * including its array of pointers to levels. */ static Size nailboardStructSize(Count levels) { return sizeof(NailboardStruct) + sizeof(BT *) * (levels - 1); } /* nailboardLevelsSize -- return the total size of the bit tables for * all the levels in a nailboard with the given number of paramaters. */ static Size nailboardLevelsSize(Count nails, Count levels, Shift shift) { Index i; Size size; AVER(nails >> ((levels - 1) * shift) != 0); AVER(nails >> (levels * shift) <= 1); size = 0; for (i = 0; i < levels; ++i) { size += BTSize(nails >> (i * shift)); } return size; } /* NailboardCreate -- allocate a nailboard * * Allocate a nailboard in the control pool for arena, to cover the * range of addresses from base to limit (which must be non-empty). If * successful, set *boardReturn to point to the nailboard and return * ResOK. Otherwise, return a result code to indicate failure. * * alignment specifies the granularity of the nails: that is, the * number of bytes covered by each nail. */ Res NailboardCreate(Nailboard *boardReturn, Arena arena, Align alignment, Addr base, Addr limit) { void *p; Nailboard board; Count nails, levels; Index i; Size structSize, levelsSize; Res res; Shift levelShift = MPS_WORD_SHIFT; AVER(boardReturn != NULL); AVERT(Arena, arena); AVERT(Align, alignment); AVER(base < limit); AVER(AddrIsAligned(base, alignment)); AVER(AddrIsAligned(limit, alignment)); nails = AddrOffset(base, limit) / alignment; levels = SizeRoundUp(SizeLog2(nails) + 1, levelShift) / levelShift; AVER((nails >> ((levels - 1) * levelShift)) > 0); AVER((nails >> (levels * levelShift)) == 0); structSize = nailboardStructSize(levels); levelsSize = nailboardLevelsSize(nails, levels, levelShift); res = ControlAlloc(&p, arena, structSize + levelsSize, FALSE); if (res != ResOK) return res; board = p; board->newNails = FALSE; board->levels = levels; board->alignShift = SizeLog2(alignment); board->levelShift = levelShift; RangeInit(&board->range, base, limit); AVER(nails == RangeSize(&board->range) >> board->alignShift); p = AddrAdd(p, structSize); for (i = 0; i < levels; ++i) { Count levelNails = nails >> (i * levelShift); AVER(levelNails > 0); board->level[i] = p; BTResRange(board->level[i], 0, levelNails); p = AddrAdd(p, BTSize(levelNails)); } board->sig = NailboardSig; AVERT(Nailboard, board); *boardReturn = board; return ResOK; } /* NailboardDestroy -- destroy a nailboard */ void NailboardDestroy(Nailboard board, Arena arena) { Count nails; Size structSize, levelsSize; AVERT(Nailboard, board); AVERT(Arena, arena); nails = RangeSize(&board->range) >> board->alignShift; structSize = nailboardStructSize(board->levels); levelsSize = nailboardLevelsSize(nails, board->levels, board->levelShift); board->sig = SigInvalid; ControlFree(arena, board, structSize + levelsSize); } /* NailboardClearNewNails -- clear the "new nails" flag */ void NailboardClearNewNails(Nailboard board) { board->newNails = FALSE; } /* NailboardNewNails -- return the "new nails" flag * * Return TRUE if any new nails have been set in the nailboard since * the last call to NailboardClearNewNails (or since the nailboard was * created, if there have never been any such calls), FALSE otherwise. */ Bool NailboardNewNails(Nailboard board) { return board->newNails; } /* nailboardIndex -- return the index of the nail corresponding to * addr in the given level. */ static Index nailboardIndex(Nailboard board, Index level, Addr addr) { return AddrOffset(RangeBase(&board->range), addr) >> (board->alignShift + level * board->levelShift); } /* nailboardAddr -- return the address corresponding to the index in * the given level. */ static Addr nailboardAddr(Nailboard board, Index level, Index index) { return AddrAdd(RangeBase(&board->range), index << (board->alignShift + level * board->levelShift)); } /* nailboardIndexRange -- update *ibaseReturn and *ilimitReturn to be * the indexes of the nail corresponding to base and limit * respectively, in the given level. */ static void nailboardIndexRange(Index *ibaseReturn, Index *ilimitReturn, Nailboard board, Index level, Addr base, Addr limit) { *ibaseReturn = nailboardIndex(board, level, base); *ilimitReturn = nailboardIndex(board, level, AddrSub(limit, 1)) + 1; } /* NailboardGet -- return nail corresponding to address * * Return the nail in the nailboard corresponding to the address addr. * It is an error if addr does not lie in the range of addresses * covered by the nailboard. */ Bool NailboardGet(Nailboard board, Addr addr) { AVERT(Nailboard, board); AVER(RangeContains(&board->range, addr)); return BTGet(board->level[0], nailboardIndex(board, 0, addr)); } /* NailboardSet -- set nail corresponding to address * * Set the nail in the nailboard corresponding to the address addr. * Return the old nail at that position. It is an error if addr does * not lie in the range of addresses covered by the nailboard. */ Bool NailboardSet(Nailboard board, Addr addr) { Bool isNew = FALSE; Index i; AVERT_CRITICAL(Nailboard, board); AVER_CRITICAL(RangeContains(&board->range, addr)); for (i = 0; i < board->levels; ++i) { Index j = nailboardIndex(board, i, addr); if (BTGet(board->level[i], j)) { break; } BTSet(board->level[i], j); isNew = TRUE; } if (isNew) { board->newNails = TRUE; return FALSE; } return TRUE; } /* NailboardSetRange -- set all nails in range * * Set all nails in the nailboard corresponding to the range between * base and limit. It is an error if any part of the range is not * covered by the nailboard, or if any nail in the range is set. */ void NailboardSetRange(Nailboard board, Addr base, Addr limit) { Index i, ibase, ilimit; nailboardIndexRange(&ibase, &ilimit, board, 0, base, limit); AVER(BTIsResRange(board->level[0], ibase, ilimit)); BTSetRange(board->level[0], ibase, ilimit); for (i = 1; i < board->levels; ++i) { nailboardIndexRange(&ibase, &ilimit, board, i, base, limit); BTSetRange(board->level[i], ibase, ilimit); } } /* NailboardIsSetRange -- test if all nails are set in a range * * Return TRUE if all nails are set in the range between base and * limit, or FALSE if any nail is unset. It is an error if any part of * the range is not covered by the nailboard. */ Bool NailboardIsSetRange(Nailboard board, Addr base, Addr limit) { Index ibase, ilimit; AVERT(Nailboard, board); nailboardIndexRange(&ibase, &ilimit, board, 0, base, limit); return BTIsSetRange(board->level[0], ibase, ilimit); } /* NailboardIsResRange -- test if all nails are reset in a range * * Return TRUE if no nails are set in the range between base and * limit, or FALSE if any nail is set. It is an error if any part of * the range is not covered by the nailboard. */ Bool NailboardIsResRange(Nailboard board, Addr base, Addr limit) { Index i, ibase, ilimit; Index j, jbase, jlimit; Addr leftLimit, rightBase; AVERT_CRITICAL(Nailboard, board); i = board->levels - 1; for (;;) { nailboardIndexRange(&ibase, &ilimit, board, i, base, limit); if (BTIsResRange(board->level[i], ibase, ilimit)) return TRUE; if (ibase + 1 < ilimit - 1) { if (BTIsResRange(board->level[i], ibase + 1, ilimit - 1)) break; else return FALSE; } if (i == 0) return TRUE; -- i; } /* Left splinter */ for (j = i, jbase = ibase;;) { if (j == 0) break; leftLimit = nailboardAddr(board, j, jbase + 1); AVER_CRITICAL(base < leftLimit); AVER_CRITICAL(leftLimit < limit); -- j; nailboardIndexRange(&jbase, &jlimit, board, j, base, leftLimit); if (jbase + 1 < jlimit && !BTIsResRange(board->level[j], jbase + 1, jlimit)) return FALSE; if (!BTGet(board->level[j], jbase)) break; } /* Right splinter */ for (j = i, jlimit = ilimit;;) { if (j == 0) break; rightBase = nailboardAddr(board, j, jlimit - 1); AVER_CRITICAL(base < rightBase); AVER_CRITICAL(rightBase < limit); -- j; nailboardIndexRange(&jbase, &jlimit, board, j, rightBase, limit); if (jbase < jlimit - 1 && !BTIsResRange(board->level[j], jbase, jlimit - 1)) return FALSE; if (!BTGet(board->level[j], jlimit - 1)) break; } return TRUE; } /* NailboardIsResClientRange -- test if all nails are reset in a range * * As NailboardIsResRange, except that the addresses are client * addresses for objects with the given header size. */ Bool NailboardIsResClientRange(Nailboard board, Size headerSize, Addr base, Addr limit) { return NailboardIsResRange(board, AddrSub(base, headerSize), AddrSub(limit, headerSize)); } Res NailboardDescribe(Nailboard board, mps_lib_FILE *stream) { Count nails; Index i, j; if(!TESTT(Nailboard, board)) return ResFAIL; if(stream == NULL) return ResFAIL; nails = RangeSize(&board->range) >> board->alignShift; for(i = 0; i < board->levels; ++i) { Count levelNails = nails >> (i * board->levelShift); Res res; res = WriteF(stream, " Level $U: ", i, NULL); if(res != ResOK) return res; for (j = 0; j < levelNails; ++j) { char c = BTGet(board->level[i], j) ? '*' : '.'; res = WriteF(stream, "$C", c, NULL); if(res != ResOK) return res; } res = WriteF(stream, "\n", NULL); if(res != ResOK) return res; } return ResOK; } /* C. COPYRIGHT AND LICENSE * * Copyright (C) 2014 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Redistributions in any form must be accompanied by information on how * to obtain complete source code for this software and any accompanying * software that uses this software. The source code must either be * included in the distribution or be available for no more than the cost * of distribution plus a nominal fee, and must be freely redistributable * under reasonable conditions. For an executable file, complete source * code means the source code for all modules it contains. It does not * include source code for modules or files that typically accompany the * major components of the operating system on which the executable file * runs. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */