next up previous contents index
Next: PersistentStorage.cc Up: The Sources Previous: The Sources

PersistentStorage.h

// PersistentStorage.h
// Copyright (C) 1997,98 Mori Tetsuya
// 12/16/97
// 12/17/97 
// 12/20/97 map()
// 12/21/97 grow()
// 12/24/97 initializeObjectTable(), splitTopChunk(), ...
// 12/26/97 destroy(), findChunk(), split()
// 12/28/97 removeRedundantWindow(), -O3 optimization bug avoidance
// 12/29/97 map() with validation, minimumChunkSize()
// 12/30/97 cleanUp()
// 1/2/98   setObjectFlag(), getEntryPoint(), stabilized
// 1/3/98   PointerPair, PersistentObject

#ifndef __PERSISTENT_STORAGE_H_
#define __PERSISTENT_STORAGE_H_
#pragma interface "PersistentStorage.h"


#include <stdio.h>
#include <iostream.h>
#include <time.h>
#include "FileStat.h"
#include "SortedTable.h"
#include "MMap.h"

void breakpoint();
#ifdef DEBUG
#define STATUS(str) (cerr << str, breakpoint())
#else
#define STATUS(str) (cerr << str)
#endif

class PersistentStorage;
class Arena;
class ArenaInitialBlock;
class Window;
class Chunk;
class OffsetChunkPair;
class PersistentOffset;
class Entry;
class PersistentPointer;
class PersistentObject;
class PointerPair;

#ifndef __BASIC_TYPES__
#define __BASIC_TYPES__
typedef int Integer32;
typedef Integer32 SizeType;
typedef Integer32 OffsetType;
typedef Integer32 CounterType;
typedef Integer32 ArenaID;
typedef Integer32 EntryID;
typedef Integer32 BoolType;
enum { True = 1, False = 0 };
typedef Integer32 ComparisonType;
enum { GreaterThan = 1, EqualTo = 0, LessThan = -1 };
typedef void *PointerType;

#endif // __BASIC_TYPES__

typedef Integer32 ObjectID;
typedef PersistentObject *P;

extern PersistentStorage *ps; // the unique PersistentStorage object

/* 
   Note: The order of declaration is coordinated so that 
         any declaration of classes used for template may precede 
         instantiations of template classes as class members.
 */

/*
  PersistentOffset --  An element object for objectTable in Window.

  A PersistentOffset object corresponds to a Chunk in the Window.
  The object contains 
  1) the offset of the Chunk
  2) the flag for being in use
  3) the flag for being grabbed
  4) the flag for being fragmented
  5) the counter for readonly grabbing
 */
class PersistentOffset { // SortableObject
  friend class Arena;
  friend class Window;
  friend class OffsetChunkPair;
  friend class SortedObjectTable<PersistentOffset, OffsetType>;
protected:
  OffsetType  offset; // the offset of the Chunk
  CounterType grabCountReadOnly; // the counter for readonly grabbing 
  // the member 'offset' contains 3 flags in its least significant 3 bits
  static const OffsetType offsetMask = ~0x00000007;
  static const OffsetType inUseMask = 0x00000001;
  static const OffsetType fragmentedMask = 0x00000002;
  static const OffsetType grabbedMask = 0x00000004;
protected:
  // constructors
  // constructor for nullObject
  PersistentOffset(): offset(0), grabCountReadOnly(0) {}
  // regular constructor 
  PersistentOffset(OffsetType off, BoolType inUse,
                   BoolType fragmented = False, 
                   BoolType grabbed = False) :
    offset(off & offsetMask), grabCountReadOnly(0) {
      if (inUse) 
        offset |= inUseMask;
      if (fragmented)
        offset |= fragmentedMask;
      if (grabbed) 
        offset |= grabbedMask;
  }

  // accessors
  // get the offset of the Chunk
  inline OffsetType getOffset() {
    return offset & offsetMask;
  }
  // probe the in use flag
  inline BoolType isInUse() {
    return offset & inUseMask ? True : False;
  }
  // probe the fragmented flag
  inline BoolType isFragmented() {
    return offset & fragmentedMask ? True : False;
  }
  // probe the grabbed flag
  inline BoolType isGrabbed() {
    return offset & grabbedMask ? True : False;
  }
  // get the counter for the readonly grabbing
  inline CounterType getGrabCountReadOnly() {
    return grabCountReadOnly;
  }
  
  // operations for the counter
  // increment the counter by 1 without overflow checking
  inline CounterType incrementGrabCountReadOnly() {
    return ++grabCountReadOnly;
  }
  // decrement the counter by 1 with underflow checking
  // returns -1 when failed
  inline CounterType decrementGrabCountReadOnly() {
    if (grabCountReadOnly > 0) {
      return --grabCountReadOnly;
    } else {
      return (CounterType)-1;
    }
  }

  // operations for the flags
  // set the in use flag
  inline void setInUse() {
    offset |= inUseMask;
  }
  // clear the in use flag
  inline void clearInUse() {
    offset &= ~inUseMask;
  }
  // set the fragmented flag
  inline void setFragmented() {
    offset |= fragmentedMask;
  }
  // clear the fragmented flag
  inline void clearFragmented() {
    offset &= ~fragmentedMask;
  }
  // set the grabbed flag
  inline void setGrabbed() {
    offset |= grabbedMask;
  }
  // clear the grabbed flag
  inline void clearGrabbed() {
    offset &= ~grabbedMask;
  }

  // methods as a SortableObject
  // compare with a PersistentOffset
  inline ComparisonType compare(PersistentOffset &po) {
    return getOffset() > po.getOffset() ? GreaterThan : 
      (getOffset() < po.getOffset() ? LessThan : EqualTo);
  }
  // compare with an offset
  inline ComparisonType compare(OffsetType off) {
    return getOffset() > (off & offsetMask) ? GreaterThan :
      (getOffset() < (off & offsetMask) ? LessThan : EqualTo);
  }
  // get the key, which is the offset
  inline OffsetType getKey() {
    return offset & offsetMask;
  }
  // probe if the object is null
  inline BoolType isNull() {
    return (offset & offsetMask) == 0;
  }
  // the null object 
  static PersistentOffset nullObject;
}; // PersistentOffset

/*
  Window -- an element object for windowTable in Arena

  Window is derived from MMap, which wraps the mmap() system call.
  A Window object manipulates a memory mapping of an Arena.
  The object contains
  1) the counter for the total number of grabbings 
     of the objects in the Window
  2) the counter for the total number of readonly grabbings 
     of the objects in the Window
  3) the sorted table of the objects in the Window, 
     which are represented by PersistentOffset objects
 */
class Window : protected MMap {
  friend class Arena;
  friend class Chunk;
  friend class PersistentPointer;
  friend class OffsetChunkPair;
  friend class SortedPointerTable<Window, OffsetType>;
  friend class PersistentStorage;
protected:
  BoolType    dirty;
  CounterType totalGrabCount; // the total number of grabbings
  CounterType totalGrabCountReadOnly; // the total number of readonly grabbings
  // the sorted table of objects in the Window
  SortedObjectTable<PersistentOffset, OffsetType> objectTable;
protected:
  // constructors

  /* 
     constructor for masterWindow, which is never mapped

     parameter:
     const char *file -- the file name of the Arena storage
   */
  Window(const char *file): 
    MMap(file), // associates the file with the Window
    totalGrabCount(-1), // invalid count
    totalGrabCountReadOnly(-1) {} // invalid count

  /* 
     constructor for general Window's, which maps and initializes objectTable
     
     parameters:
     Window     &w -- the masterWindow object, which gives the file descriptor
     OffsetType off -- the offset of the beginning of the Window
     SizeType   len -- the size of the Window in bytes
     OffsetType obj -- the offset of the object 
                       that is requested to be grabbed, 
                       also used as a hint for construction of objectTable
     Window *before -- the previous Window
     Window  *after -- the next Window
     Arena   *arena -- the parent Window
   */
  Window(Window &w, 
         OffsetType off, 
         SizeType len, 
         OffsetType obj = 0,
         Window *before = 0,
         Window *after = 0,
         Arena *arena = 0,
         SizeType minimumChunkSize = 16);


  // destructor
  ~Window();

  void printStatus();

  /*
    initializeObjectTable -- constructs objectTable
    
    parameter:
    OffsetType obj -- used as a hint for tracing the Chunk list
    Arena   *arena -- the parent of this Window

    return value:
     BoolType -- True if successful
   */
  BoolType initializeObjectTable(OffsetType obj, 
                                 Window *before = 0, 
                                 Window *after = 0,
                                 Arena *arena = 0);

  /*
    isWithinRange -- check if the offset is within the Window

    parameter:
    OffsetType o -- the offset to be checked

    return value:
    BoolType -- whether the offset is within the window or not
   */
  inline BoolType isWithinRange(OffsetType o) {
    return getAddr() && 
      getOffset() <= o && 
      o < getOffset() + (OffsetType)getLength();
  }

  /*
    isValid -- check if the offset points a valid Chunk

    parameter:
    OffsetType o -- the offset to be checked

    return value:
    BoolType -- whether the offset points a valid Chunk or not
   */
  inline BoolType isValid(OffsetType o) {
    return isWithinRange(o) && !objectTable[o].isNull();
  }

  /*
    isAllocated -- check if the offset points an ojbect in use

    parameter:
    OffsetType o -- the offset to be checked

    return value:
    BoolType -- whether the offset points an object in use or not
   */
  inline BoolType isAllocated(OffsetType o) {
    return isValid(o) && objectTable[o].isInUse();
  }

  /*
    isGrabbed -- check if the offset points a grabbed ojbect

    parameter:
    OffsetType o -- the offset to be checked

    return value:
    BoolType -- whether the offset points a grabbed object or not
   */
  inline BoolType isGrabbed(OffsetType o) {
    return isValid(o) && objectTable[o].isGrabbed();
  }

  /*
    isFragmented -- check if the offset points a fragmented Chunk

    parameter:
    OffsetType o -- the offset to be checked

    return value:
    BoolType -- whether the offset points a fragmented Chunk or not
   */
  inline BoolType isFragmented(OffsetType o) {
    return isValid(o) && objectTable[o].isFragmented();
  }

  /*
    map -- convert an offset to the pointer to the corresponding Chunk
    
    parameter:
    OffsetType o -- the offset to be converted

    return value:
    Chunk * -- the pointer to the corresponding Chunk,
               null if not within the range
   */
  inline Chunk *map(OffsetType o) { // irrelevant to MMap::map()
    if (isWithinRange(o)) {
      return (Chunk *)((PointerType)getAddr() + (o - getOffset()));
    } else {
      return (Chunk *)0;
    }
  }

  /*
    grab -- part of the internal implementation of 
            one of the primitive operations to a PersistentPointer,
            grab an object for manipulation,
            no two pointers can grab one object at the same time

    parameter:
    OffsetType o -- the offset of the Chunk

    return value:
    Chunk * -- the pointer to the grabbed Chunk
   */
  Chunk *grab(OffsetType o) {
    if (o & PersistentOffset::fragmentedMask) {
      o &= PersistentOffset::offsetMask;
      if (isValid(o)) {
        PersistentOffset &po = objectTable[o];
        if (!po.isGrabbed()) {
          po.setGrabbed();
          totalGrabCount++;
          return (Chunk *)((PointerType)getAddr() + (o - getOffset()));
        }
      } 
    } else {
      o &= PersistentOffset::offsetMask;
      if (isValid(o)) {
        PersistentOffset &po = objectTable[o];
        if (!po.isFragmented() && 
            po.isInUse() && 
            !po.isGrabbed()) {
          po.setGrabbed();
          totalGrabCount++;
          dirty = True;
          return (Chunk *)((PointerType)getAddr() + (o - getOffset()));
        } else {
#ifdef DEBUG
          cerr << "Window::grab " 
               << (po.isFragmented() ? "Frag " : "Comp ")
               << (po.isInUse() ? "Used " : "Free ")
               << (po.isGrabbed() ? "Grabbed " : "Released ")
               << endl;
#endif
          return 0;
        }
      } else {
#ifdef DEBUG
        cerr << "Window::grab not valid " << o << endl; 
#endif
      }
    }
    return (Chunk *)0;
  }

  /*
    release -- part of the internal implementation of 
               one of the primitive operations to a PersistentPointer,
               release an object to notify the end of its manipulation,
               only grabbed objects can be released

    parameter:
    OffsetType o -- the offset of the Chunk

    return value:
    BoolType -- True if successful
   */
  BoolType release(OffsetType o) {
    if (isValid(o)) {
      PersistentOffset &po = objectTable[o];
      if (totalGrabCount > 0 && 
          po.isGrabbed()) {
        po.clearGrabbed();
        totalGrabCount--;
        return True;
      } else {
#ifdef DEBUG
        cerr << "Window::release totalGrabCount " 
             << totalGrabCount << " "
             << (po.isGrabbed() ? "Grabbed " : "Released ")
             << endl; 
#endif
      }
    } else {
#ifdef DEBUG
        cerr << "Window::release not valid " << o << endl; 
#endif
    }
    return False;
  }

  /*
    grabReadOnly -- part of the internal implementation of 
                    one of the primitive operations to a PersistentPointer,
                    grab an object for reference,
                    more than one pointer can grab at the same time

    parameter:
    OffsetType o -- the offset of the Chunk

    return value:
    Chunk * -- the pointer to the grabbed Chunk
   */
  Chunk *grabReadOnly(OffsetType o) {
    if (isValid(o)) {
      PersistentOffset &po = objectTable[o];
      if (!po.isFragmented() &&
          po.isInUse()) {
        if (po.incrementGrabCountReadOnly() >= 0) {
          totalGrabCountReadOnly++;
          return (Chunk *)((PointerType)getAddr() + (o - getOffset()));
        }
      }
    }
    return (Chunk *)0;
  }

  /*
    releaseReadOnly -- part of the internal implementation of 
                       one of the primitive operations to a PersistentPointer,
                       release an object to notify the end of its reference,
                       only grabbed-for-reference objects can be released

    parameter:
    OffsetType o -- the offset of the Chunk

    return value:
    BoolType -- True if successful
   */
  BoolType releaseReadOnly(OffsetType o) {
    if (isValid(o)) {
      PersistentOffset &po = objectTable[o];
      if (!po.isFragmented() && 
          totalGrabCountReadOnly > 0 &&
          po.getGrabCountReadOnly() > 0) {
        if (po.decrementGrabCountReadOnly() >= 0) {
          totalGrabCountReadOnly--;
          return True;
        }
      }
    }
    return False;
  }

  // accessors

  // get the total number of the grabbed objects in the Window
  inline CounterType getTotalGrabCount() {
    return totalGrabCount;
  }

  // get the total number of the pointers that point 
  //     the grabbed-for-reference objects in the Window
  inline CounterType getTotalGrabCountReadOnly() {
    return totalGrabCountReadOnly;
  }

  inline BoolType isCompletelyReleased() {
    return getTotalGrabCount() == 0 && 
      getTotalGrabCountReadOnly() == 0;
  }

  inline BoolType isDirty() {
    return dirty;
  }

  inline void setDirty() {
    dirty = True;
  }

  inline void clearDirty() {
    dirty = False;
  }

  // get the number of the grabbed-for-reference pointers 
  //     that point the object with the offset o
  // return -1 if the offset o is invalid
  inline CounterType getGrabCountReadOnly(OffsetType o) {
    if (isValid(o)) {
      PersistentOffset &po = objectTable[o];
      if (!po.isFragmented() && 
          po.isInUse()) {
        return po.getGrabCountReadOnly();
      }
    }
    return (CounterType)-1;
  }
  
  // methods as a SortableObject

  // compare with a Window
  // the beginning offset is used for comparison
  inline ComparisonType compare(Window &w) {
    OffsetType o1 = getOffset();
    OffsetType o2 = w.getOffset();
    return o1 > o2 ? 
      GreaterThan : 
      (o1 < o2 ? LessThan : 
       (getLength() < w.getLength() ? 
        LessThan :
        (getLength() > w.getLength() ? 
         GreaterThan : EqualTo)));
  }

  /* 
     compare with an offset

     The policy for comparison is somewhat complicated:
       There are two different schemes for comparison.
       One scheme just checks whether the offset is within the Window.
       This scheme is specified by a fragmentedMask in the offset.
       Another scheme assumes that the offset points to a valid Chunk.
       The conditions for equality are:
       1) The offset points to a valid Chunk within the Window.
          Therefore, there should be an element with the offset in objectTable.
       2) The Chunk must be contained completely in the Window, i.e.,
          the chunk is not fragmented.
       Name the zone where the above condition are fulfilled 
       as the equality zone.
       The window is 'less' than the offset 
       if the offset is after the equality zone.
       The window is 'greater' than the offset
       if the offset is before the equality zone.
       In short, the offset within the equality zone is the one that can be
       referenced or manipulated through the Window.
   */
  inline ComparisonType compare(OffsetType o) {
    if (o & PersistentOffset::fragmentedMask) {
      // The first scheme
      if (isWithinRange(o & PersistentOffset::offsetMask)) {
        return EqualTo;
      } else if ((o & PersistentOffset::offsetMask) < getOffset()) {
        return GreaterThan;
      } else {
        return LessThan;
      }
    } else {
      // The object validation scheme
      if (isWithinRange(o)) {
        if (isValid(o)) {
          if (!isFragmented(o)) {
            return EqualTo;
          } else {
            CounterType c = objectTable.getSize();
            if (c <= 0) { // no objects ??
              return LessThan;
            }
            PersistentOffset ofirst = objectTable(0);
            if (!ofirst.isFragmented()) { 
              return LessThan;
            } else {
              CounterType i;
              for (i = 1; i < c; i++) {
                ofirst = objectTable(i);
                if (!ofirst.isFragmented())
                  break;
              }
              if (ofirst.isFragmented()) { // all objects are fragmented
                return LessThan;
              }
              // ofirst is the first valid and non-fragmented object
              if (ofirst.compare(o) < 0) {
                return LessThan;
              } else {
                return GreaterThan;
              }
            }
          }
        } else { // within range but not valid
          return EqualTo; // switch to the first scheme
        }
      } else { // out of range
        if (getOffset() > o) {
          return GreaterThan;
        } else {
          return LessThan;
        }
      }
    }
  }

  // get the key, which is the beginning offset of the Window 
  inline OffsetType getKey() {
    return (OffsetType)getOffset();
  }

  /*
    isContainedBy -- check if 'this' Window is completely contained by
                     another Window w
   */
  inline BoolType isContainedBy(Window &w) {
    if (getOffset() >= w.getOffset() &&
        getOffset() + getLength() <= w.getOffset() + w.getLength()) {
      return True;
    } else {
      return False;
    }
  }

  /*
    isSeparatedFrom -- check if 'this' Window is not overlapped with
                       another Window w
   */
  inline BoolType isSeparatedFrom(Window &w) {
    if (getOffset() >= w.getOffset()) {
      if (getOffset() >= (w.getOffset() + (OffsetType)w.getLength())) {
        return True;
      } else {
        return False;
      }
    } else {
      if ((getOffset() + (OffsetType)getLength()) <= w.getOffset()) {
        return True;
      } else {
        return False;
      }
    }
  }
}; // Window

/* 
   Chunk -- an element of a doubly linked list which is located at
            the head of a used/free block in Arena

   The Chunk object is a unit of object allocation in persistent storage,
   and marks a storage block, i.e., it has one-to-one correspondence with
   an object in a persistent storage.
   The object contains:
   1) the size of the previous Chunk in bytes
   2) the size of the Chunk in bytes
   3) the offset of the previous FREE Chunk when the Chunk is free
   4) the offset of the next FREE Chunk when the Chunk is free
   5) the flag that shows whether the PREVIOUS Chunk is in use
   6) the flag that shows whether the Chunk is a dummy one 
      for non-perisitent storage
   7) the head of the memory block for the object when the Chunk is in use
 */
class Chunk {
  friend class Arena;
  friend class Window;
  friend class OffsetChunkPair;
  friend class PersistentStorage;
  friend class PersistentPointer;
protected:
  SizeType prevSize; // the size of the previous Chunk in bytes
  SizeType size; // the size of the Chunk in bytes
  OffsetType backward; // the offset of the previous FREE Chunk
  OffsetType forward; // the offset of the next FREE Chunk
protected:
  // the member 'size' contains two flags at its least significant 3 bits
  static const SizeType sizeMask = ~0x00000007;
  static const SizeType prevInUseMask = 0x00000001;
  static const SizeType dummyChunkMask = 0x00000002;
  // the size of the Chunk in use 
  static const SizeType inUseChunkSize = sizeof(SizeType) * 2;
protected:
  /* 
     constructor

     parameters:
     SizeType prev -- the size of the previous Chunk in bytes
     SizeType sz -- the size of this Chunk in bytes 
     BoolType pInUse -- whether the previous Chunk is in use or not
     OffsetType bk -- the offset of the previous free Chunk
     OffsetType fd -- the offset of the next free Chunk
     BoolType dummy -- whether this Chunk is dummy
   */
  Chunk(SizeType prev, SizeType sz, 
        BoolType pInUse, 
        OffsetType bk = 0, 
        OffsetType fd = 0, 
        BoolType dummy = False): 
    prevSize(prev & sizeMask), 
    size(sz & sizeMask), 
    backward(bk), 
    forward(fd) {
      if (pInUse) 
        size |= prevInUseMask;
      if (dummy)
        size |= dummyChunkMask;
  }

  // destructor
  ~Chunk() {}

  // accessors

  // get the size of this Chunk
  inline SizeType getSize() {
    return size & sizeMask;
  }
  
  // get the size of the previous Chunk
  inline SizeType getPrevSize() {
    return prevSize & sizeMask;
  }
  
  // operations
  
  // set the size of this Chunk, preserving flags
  inline SizeType setSize(SizeType sz) {
    return size = (sz & sizeMask) | (size & ~sizeMask);
  }

  // set the size of the previous Chunk
  inline SizeType setPrevSize(SizeType sz) {
    return prevSize = (sz & sizeMask) | (prevSize & ~sizeMask);
  }

  // set the previous-in-use flag
  inline void setPrevInUse() {
    size |= prevInUseMask;
  }

  // clear the previous-in-use flag
  inline void clearPrevInUse() {
    size &= ~prevInUseMask;
  }
  
  // set the dummy flag
  inline void setDummy() {
    size |= dummyChunkMask;
  }

  // clear the dummy flag
  inline void clearDummy() {
    size &= ~dummyChunkMask;
  }

  // probe the previous-in-use flag
  inline BoolType isPrevInUse() {
    return size & prevInUseMask ? True : False;
  }

  // probe the dummy flag
  inline BoolType isDummy() {
    return size & dummyChunkMask ? True : False;
  }

  // get the memory block of this Chunk
  inline PointerType getMemBlock() {
    return ((PointerType)this) + inUseChunkSize;
  }

  // a static member function that generates 
  // the pointer to the Chunk from a pointer to an object
  static Chunk *getChunk(PointerType p) {
    return (Chunk *)(p - inUseChunkSize);
  }
}; // Chunk

/*
  PersistentPointer -- a persistent pointer object that points to 
                       a persistent object in PersistentStorage

  The PersistentPointer object represents a unique location in
  the PersistentStorage.  The object has internal states for grabbing.
  Therefore, in order to grab the pointed object, a PersistentPointer 
  object must be copied to another object when located in the 
  PersistentStorage.
  The object contains:
  1) the integer identifier for specifying Arena
  2) the integer offset for specifying a location in the Arena
 */
class PersistentPointer {
  friend class PersistentStorage;
protected:
  ArenaID     arenaID; // the identifier for Arena
  OffsetType  offset; // the offset in the Arena
  // the member 'offset' contains flags at its least significant 3 bits
  static const OffsetType offsetMask = ~0x00000007;
  static const OffsetType grabbedMask = 0x00000004;
  static const OffsetType grabbedReadOnlyMask = 0x00000002;
public:
  // constructors

  // constructor for copying
  // flags are cleared
  PersistentPointer(PersistentPointer &pp): 
    arenaID(pp.arenaID), offset(pp.offset & offsetMask) {}

  // constructor for null pointer
  PersistentPointer(): arenaID(0), offset(0) {}

protected:
  /*
    protected constructor for allocator

    
   */
  PersistentPointer(ArenaID aid, OffsetType off,
                    BoolType grabbed = False,
                    BoolType grabbedReadOnly = False):
    arenaID(aid), offset(off & offsetMask) {
      if (grabbed) 
        offset |= grabbedMask;
      if (grabbedReadOnly)
        offset |= grabbedReadOnlyMask;
  }

  // protected accessors

  // get the offset
  inline OffsetType getOffset() {
    return offset & offsetMask;
  }
  
  // get the Arena ID
  inline ArenaID getArenaID() {
    return arenaID;
  }

  // set the grabbed flag
  inline void setGrabbed() {
    offset |= grabbedMask;
  }

  // clear the grabbed flag
  inline void clearGrabbed() {
    offset &= ~grabbedMask;
  }

  // set the grabbed-read-only flag
  inline void setGrabbedReadOnly() {
    offset |= grabbedReadOnlyMask;
  }

  // clear the grabbed-read-only flag
  inline void clearGrabbedReadOnly() {
    offset &= ~grabbedReadOnlyMask;
  }

public:
  // destructor
  ~PersistentPointer() {}

  // assignment operators

  // assignment with another PersistentPointer
  // flags are cleared
  inline PersistentPointer &operator=(PersistentPointer pp) {
    arenaID = pp.arenaID;
    offset = pp.offset & offsetMask;
    return *this;
  }

  // assignment with 0 to generate null pointer
  inline PersistentPointer &operator=(OffsetType off) {
    // argument off is discarded
    off = 0;
    arenaID = 0;
    offset = 0;
    return *this;
  }

  inline BoolType operator==(OffsetType off) {
    off = 0;
    return offset == 0;
  }

  inline BoolType operator!=(OffsetType off) {
    off = 0;
    return offset != 0;
  }
    
  inline BoolType operator==(PersistentPointer &pp) {
    return (this->arenaID == pp.arenaID) && 
      (this->offset & offsetMask) == (pp.offset & offsetMask);
  }

  inline BoolType operator!=(PersistentPointer &pp) {
    return (this->arenaID != pp.arenaID) || 
      (this->offset & offsetMask) != (pp.offset & offsetMask);
  }

  // accessors

  // probe the grabbed flag
  inline BoolType isGrabbed() {
    return (offset & grabbedMask) ? True : False;
  }

  // probe the grabbedReadOnly flag
  inline BoolType isGrabbedReadOnly() {
    return (offset & grabbedReadOnlyMask) ? True : False;
  }
  
  // probe if persistent
  inline BoolType isPersistent() { 
    return arenaID != 0;
  }

  // probe if neither grabbed nor grabbedReadOnly
  inline BoolType isReleased() {
    return !isGrabbed() && !isGrabbedReadOnly();
  }

  // probe if null
  inline BoolType isNull() {
    return (offset & offsetMask) == 0;
  }

  /*
    grab -- one of the primitive operations to a PersistentPointer,
            grab an object for manipulation,
            no two pointers can grab one object at the same time

    parameter: none

    return value:
    PointerType -- the pointer to the grabbed object, 0 if failed
   */
  inline PointerType grab();

  /*
    release -- one of the primitive operations to a PersistentPointer,
               release an object to notify the end of its manipulation,
               only grabbed objects can be released

    parameter: none

    return value:
    BoolType -- True if successful
   */
  inline BoolType release();

  /*
    grabReadOnly -- one of the primitive operations to a PersistentPointer,
                    grab an object for reference,
                    more than one pointer can grab at the same time

    parameter: none

    return value:
    PointerType -- the pointer to the grabbed object, 0 if failed
   */
  inline PointerType grabReadOnly();

  /*
    releaseReadOnly -- one of the primitive operations to a PersistentPointer,
                       release an object to notify the end of its reference,
                       only grabbed-for-reference objects can be released

    parameter: none

    return value:
    BoolType -- True if successful
   */
  inline BoolType releaseReadOnly();
}; // PersistentPointer

/*
  ArenaInitialBlock -- The capsule for basic information on an Arena storage.

  This object is located at the initial block of every Arena.
 */
class ArenaInitialBlock {
  friend class Arena;
  friend class Window;
  friend class Chunk;
protected:
  char version[128];
  char copyright[128];
  char timestamp[128];
  
  ArenaID    arenaID;
  SizeType   arenaSize;
  OffsetType freeList;
  OffsetType topChunk;

protected:
  static const char Version[] = 
  "PersistentStorage version 0.2.5 " __DATE__ " " __TIME__;
  static const char Copyright[] = 
  "Copyright(C) 1997 Mori Tetsuya / n971270@yamata.icu.ac.jp";
  static const char TimeStampFormat[] = "ArenaID %08x, created %s";
protected:
  ArenaInitialBlock(ArenaID aid) : 
    arenaID(aid),
    arenaSize(0),
    freeList(0),
    topChunk(0) {
    strcpy(version, Version);
    strcpy(copyright, Copyright);
    time_t t = time(0);
    sprintf(timestamp, TimeStampFormat, aid, ctime(&t));
  }
};

/* 
   OffsetChunkPair -- the pair of OffsetType offset and Chunk *chunk
                      used during the allocation process
 */
typedef Chunk *PChunk;
class OffsetChunkPair {
  friend class Arena;
  friend class Chunk;
  friend class Window;
protected:
  OffsetType offset;
  Chunk *chunk;
public:
  OffsetChunkPair(OffsetType off = 0, Chunk *ch = 0) : offset(off), chunk(ch) {
  }
  inline OffsetChunkPair &operator = (OffsetType o) {
    chunk = 0;
    offset = o;
    return *this;
  }
  inline BoolType operator == (OffsetType o) {
    return offset == o;
  }
  inline BoolType operator != (OffsetType o) {
    return offset != o;
  }
  inline BoolType operator == (OffsetChunkPair p) {
    return offset == p.offset;
  }
  inline BoolType operator != (OffsetChunkPair p) {
    return offset != p.offset;
  }
  inline operator OffsetType () {
    return offset;
  }
  inline operator PChunk () {
    return chunk;
  }
  inline OffsetType getPrevChunk(Arena *arena, BoolType fragmented = True);
  inline OffsetType getNextChunk(Arena *arena, BoolType fragmented = True);
  inline Chunk *map(Arena *arena, 
                    BoolType fragmented = True,
                    BoolType anywhere = False);
  inline Chunk *map(Window *window);
};

/*
  Arena -- an element of PersistentStorage 

  An Arena object corresponds to a file that is used as 
  a persistent storage space.  An offset from the beginning 
  of the file is used for pointing a unique location in
  the storage.  An Arena is manipulated through Window 
  objects, which map a region of a file on the main storage.
  Allocation and destruction of any object in an Arena are
  accomplished by manipulation of Chunk objects.  A Chunk
  can be in two states, in-use and free.  An in-use Chunk
  is followed by a memory block where there is a single 
  persistent object.  A free Chunk is followed by a free
  memory block.  Chunks are doubly-linked in two parallel
  ways in order to trace Chunk lists.  An Arena manipulates
  such lists to provide reusable and free sized storage 
  space.
  The object contains:
  1) the integer identifier for Arena
  2) the size of Arena storage space in bytes
  3) the master Window object for mapping
  4) the sorted table of Window objects
  5) the offset of the free Chunk list
  6) the offset of the top free Chunk

  A desirable scheme for better performance:
  An arena ID can contain more than just the ID of the arena.
  To improve performance by reduction of fragmentation,
  An arena have the minimum size of free chunks.
  The 6 MSBs represent the size.
  
 */
class Arena {
  friend class PersistentStorage;
  friend class PersistentPointer;
  friend class Chunk;
  friend class OffsetChunkPair;
  friend class Window;
  friend class SortedPointerTable<Arena, ArenaID>;
protected:
  ArenaID      arenaID; // the identifier for this Arena
  static const CounterType minimumChunkBitShifts = 24;
  static const ArenaID minimumChunkMask = 0x1f000000;
  SizeType     arenaSize; // the size of thie Arena in bytes
  Window       masterWindow; // the master Window object, never mapped
  SortedPointerTable<Window, OffsetType> windowTable; // the table of Windows
  OffsetType freeList; // the offset of the free Chunk list
  OffsetType topChunk; // the offset of the top free Chunk
  SizeType   maximumFreeChunk;
protected:
  /* 
     constructor 
     
     parameters:
     ArenaID aid -- the arena ID
     const char *storageFile -- the file name for storage
   */
  Arena(ArenaID aid, const char *storageFile);

  // destructor
  ~Arena();

  void printStatus();

  BoolType objectTableCheck();

  // primitive operations

  /*
    allocate -- part of an internal implementation of one of the 
                primitive operations on the PersistentStorage.
         
    This operation finds an appropriate free Chunk, splits it, and
    cut out a new Chunk which is bigger than the specified size. 
    
    parameter:
    SizeType s -- the size of the requested Chunk size, including Chunk
    BoolType persistent -- True if request persistent storage

    return value:
    OffsetType -- the offset of the allocated Chunk, 0 if failed
   */
  OffsetType allocate(SizeType s);

  /*
    destroy -- part of an internal implementation of one of the
               primitive operations on the PersistentStorage.
               
    This operation destroys the in-use Chunk at the specified offset
    and restores it to the free Chunk list.
    parameter:
    OffsetType o -- the offset of the in-use Chunk to be destroyed
    
    return value:
    BoolType -- True if successful
   */
  BoolType destroy(OffsetType o);

  /*
    isMapped -- probe whether the offset o is mapped in a Window
   */
  inline BoolType isMapped(OffsetType o) {
    Window *window = windowTable[o];
    if (window != 0) {
      return window->isWithinRange(o);
    } else {
      return False;
    }
  }

  /*
    isValid -- probe whether the offset o is a valid offset to a Chunk
    
    This operation maps the offset if not mapped.
   */
  inline BoolType isValid(OffsetType o) {
    Window *window = windowTable[o];
    if (window != 0) { // Window found
      return window->isValid(o);
    } else { // Window not found
      if (map(o) != 0) { // new Window must be created to map the offset
        window = windowTable[o]; 
        if (window != 0) {
          return window->isValid(o);
        } else {
          return False;
        }
      } else {
        return False;
      }
    }
  }

  /*
    isAllocated -- probe whether the offset o points to an allocated Chunk

    This operation maps the offset if not mapped.
   */
  inline BoolType isAllocated(OffsetType o) {
    Window *window = windowTable[o];
    if (window != 0) { // Window found
      return window->isAllocated(o);
    } else { // Window not found
      if (map(o) != 0) { // new Window must be created to map the offset
        window = windowTable[o]; 
        if (window != 0) {
          return window->isAllocated(o);
        } else {
          return False;
        }
      } else {
        return False;
      }
    }
  }


  /*
    map -- map the Chunk which begins at the offset o
   */
  Chunk *map(OffsetType o);

  Window *mapWindow(OffsetType o);

  /*
    grab -- part of the internal implementation of one of the primitive
            operations on PersistentStorage.
            
    This operation grabs an object for manipulation.
    No two pointers can grab one object at the same time.

    parameter:
    OffsetType o -- the offset of the Chunk

    return value:
    Chunk * -- the pointer to the grabbed Chunk
   */
  inline Chunk *grab(OffsetType o) {
    if (o & PersistentOffset::fragmentedMask) {
      o &= PersistentOffset::offsetMask;
      if (!isGrabbed(o | PersistentOffset::fragmentedMask)) { 
        Window *window = windowTable[o];
        if (window == 0)
          window = windowTable[o | PersistentOffset::fragmentedMask];
        return window->grab(o | PersistentOffset::fragmentedMask);
      } else {
        return 0;
      }
    } else {
      if (isAllocated(o) &&
          !isGrabbed(o)) { 
        Window *window = windowTable[o];
        return window->grab(o);
      } else {
#ifdef DEBUG
        cerr << "Arena::grab " << o << " "
             << (isAllocated(o) ? "Used " : "Free ")
             << (isGrabbed(o) ? "Grabbed " : "Released ")
             << endl;
        objectTableCheck();
#endif
        return 0;
      }
    }
  }

  /*
    release -- part of the internal implementation of one of the primitive
               operations on PersistentStorage.
            
    This operation releases an object to notify the end of 
    its manipulation.  Only grabbed objects can be released.

    parameter: 
    OffsetType o -- the offset of the grabbed Chunk

    return value:
    BoolType -- True if successful
   */
  inline BoolType release(OffsetType o) {
    if (o & PersistentOffset::fragmentedMask) {
      o &= PersistentOffset::offsetMask;
      if (isGrabbed(o | PersistentOffset::fragmentedMask)) {
        Window *window = windowTable[o];
        if (window == 0) 
          window = windowTable[o | PersistentOffset::fragmentedMask];
        return window->release(o);
      } else {
        return False;
      }
    } else {
      if (isAllocated(o) &&
          isGrabbed(o)) {
        Window *window = windowTable[o];
        return window->release(o);
      } else {
#ifdef DEBUG
        cerr << "Arena::release " << o << " "
             << (isAllocated(o) ? "Used " : "Free ")
             << (isGrabbed(o) ? "Grabbed " : "Released ")
             << endl;
#endif
        return False;
      }
    }
  }

  /*
    grabReadOnly -- part of the internal implementation of 
                    one of the primitive operations to a PersistentPointer,
                    grab an object for reference,
                    more than one pointer can grab at the same time

    parameter:
    OffsetType o -- the offset of the Chunk

    return value:
    Chunk * -- the pointer to the grabbed Chunk
   */
  inline Chunk *grabReadOnly(OffsetType o) {
    if (isAllocated(o)) {
      Window *window = windowTable[o];
      return window->grabReadOnly(o);
    } else {
      return 0;
    }
  }

  /*
    releaseReadOnly -- part of the internal implementation of 
                       one of the primitive operations to a PersistentPointer,
                       release an object to notify the end of its reference,
                       only grabbed-for-reference objects can be released

    parameter:
    OffsetType o -- the offset of the Chunk

    return value:
    BoolType -- True if successful
   */
  inline BoolType releaseReadOnly(OffsetType o) {
    if (isAllocated(o)) {
      Window *window = windowTable[o];
      return window->releaseReadOnly(o);
    } else {
      return False;
    }
  }

  /*
    isGrabbed -- probe whether the Chunk which begins at the offset o
                 is grabbed.
   */
  inline BoolType isGrabbed(OffsetType o) {
    if (o & PersistentOffset::fragmentedMask) {
      o &= PersistentOffset::offsetMask;
      Window *window = windowTable[o];
      if (window == 0)
        window = windowTable[o | PersistentOffset::fragmentedMask];
      if (window != 0) { // Window found
        return window->isGrabbed(o);
      } else { // Window not found
        return False;
      }
    } else {
      Window *window = windowTable[o];
      if (window != 0) { // Window found
        return window->isGrabbed(o);
      } else { // Window not found
        return False;
      }
    }
  }

  /*
    getGrabCountReadOnly -- get the read-only-grabbing count of the Chunk
                 which begins at the offset o
   */
  CounterType getGrabCountReadOnly(OffsetType o) {
    Window *window = windowTable[o];
    if (window != 0) {
      return window->getGrabCountReadOnly(o);
    } else {
      return 0;
    }
  }

  /*
    synchronize -- synchronize the Window that maps the offset o

    parameter:
    OffsetType o -- the offset of the object to synchronize,
                    synchronize all Window objects when o = 0

    return value:
    BoolType -- True if successful
   */
  BoolType synchronize(OffsetType o = 0);


  SizeType cleanUp(BoolType completely = False);

  // methods as SortableObject

  // compare with another Arena
  // Its arenaID is the key.
  inline ComparisonType compare(Arena &a) {
    return arenaID > a.arenaID ? GreaterThan : 
      (arenaID < a.arenaID ? LessThan : EqualTo);
  }

  // compare with ArenaID
  inline ComparisonType compare(ArenaID aid) {
    return arenaID > aid ? GreaterThan :
      (arenaID < aid ? LessThan : EqualTo);
  }
  
  // get the key, which is arenaID
  inline ArenaID getKey() {
    return arenaID;
  }

private:
  inline SizeType getMinimumChunkSize() {
    return 1 << (4 + ((arenaID & minimumChunkMask) >> minimumChunkBitShifts));
  }

  inline static ArenaID calculateMinimumChunkSizeBits(SizeType size) {
    register SizeType s = alignChunkSize(size) >> 4; 
    register SizeType log = 0;
    // calculate log2 s
    while ((s >>= 1) > 0) 
      log++;
    return (ArenaID)(log * (1 << minimumChunkBitShifts));
  }
  /*
    alignChunkSize -- align the requested memory block size to
                      its appropriate Chunk size

    16 bytes is the smallest Chunk size.
    parameter:
    SizeType s -- the size of the requested memory block size

    return value:
    SizeType -- the size of the appropriate Chunk
   */
  inline static SizeType alignChunkSize(SizeType s) {
    return ((s + Chunk::inUseChunkSize) & ~(sizeof(Chunk) - 1)) 
      + sizeof(Chunk);
  }

  OffsetChunkPair findChunk(SizeType size);

  OffsetChunkPair splitTopChunk(SizeType size);

  OffsetChunkPair split(OffsetChunkPair fChunk, SizeType size);

  BoolType consolidateIntoTopChunk();

  BoolType destroyChunk(OffsetType o);

  BoolType insertObject(OffsetType offset, BoolType inUse = True);

  // also remove the fragmented object
  BoolType removeObject(OffsetType offset);

  BoolType setObjectFlag(OffsetType offset, 
                         BoolType inUse, 
                         SizeType size = 0);

  BoolType isValidFreeChunk(OffsetType offset);

  OffsetType findNearestFreeChunk(OffsetType hint);

  PersistentOffset &getObject(OffsetType offset, 
                              BoolType fragmented = False);

  Window *getWindow(OffsetType offset, 
                    BoolType fragmented = False);

  BoolType updateFragmentedFlags(SizeType windowIndex);

  BoolType isRedundant(SizeType windowIndex);

  BoolType removeRedundantWindow(OffsetType offset);

  BoolType updateInitialBlock(BoolType sync = False);

  BoolType grow();
}; // Arena

/*
  Entry -- an element object for implementing entry points for objects.

  An Entry object is a combination of an EntryID and a PersistentPointer.
  The PersistentPointer can work as an entry point for a structured
  object.  A table of Entry objects is in the PersistentStorage object.
  The table with EntryID as the key can work as indexed entry points.
  The object contains:
  1) the integer identifier for the entry point
  2) the entry point
 */
class Entry {
  friend class PersistentStorage;
  friend class SortedObjectTable<Entry, EntryID>;
protected:
  EntryID            entryID;  // the identifier for the entry point
  PersistentPointer  entryPoint; // the entry point
  static Entry       nullObject; // the null object
protected:
  // constructors
  
  // copy constructor
  Entry(Entry &e): entryID(e.entryID), entryPoint(e.entryPoint) {}

  // general constructor
  Entry(EntryID eid, PersistentPointer &pp): entryID(eid), entryPoint(pp) {}

public:
  // default constructor for generation of null object
  Entry(): entryID(0), entryPoint() {}

  // destructor
  ~Entry() {}

protected:
  // assignment operator
  inline Entry &operator=(Entry &e) {
    entryID = e.entryID;
    entryPoint = e.entryPoint;
    return *this;
  }

  inline operator PersistentPointer() {
    return entryPoint;
  }

  // methods as SortableObject

  // compare with another Entry
  inline ComparisonType compare(Entry &e) {
    return entryID > e.entryID ? GreaterThan : 
      (entryID < e.entryID ? LessThan : EqualTo);
  }

  // compare with EntryID
  inline ComparisonType compare(EntryID eid) {
    return entryID > eid ? GreaterThan :
      (entryID < eid ? LessThan : EqualTo);
  }

  // get the key, which is the entryID
  inline EntryID getKey() {
    return entryID;
  }

  // probe whether it is null
  inline BoolType isNull() {
    return entryID == 0;
  }
}; // Entry

/*
  PersistentStorage --
 */
class PersistentStorage {
  friend class PersistentPointer;
  friend class Window;
  friend class Arena;
protected:
  SortedPointerTable<Arena, ArenaID>  arenaTable;
  SortedObjectTable<Entry, EntryID>   entryTable;
  char *baseDir;
  SizeType totalWindowSize;
public:
  // implementation constants
  // The page size of the operating system's virtual memery 
  static const SizeType pageSize = 4096; 
  static const SizeType maxArenaSize = 1024 * 1024 * 1024; // 1GB
  static const SizeType maxWindowSize = 4 * 1024 * 1024; // 4MB
  static const SizeType maxObjectSize = 2 * 1024 * 1024; // 2MB
  static const SizeType maxTotalWindowSize = 32 * 1024 * 1024; // 32MB
  static const SizeType standardWindowSize = 8 * 1024; // 16KB
  static const SizeType standardWindowSizeNonPersistent = 1024 * 1024; // 1MB
  static const SizeType growingChunkSize = 4 * 1024 * 1024; // 4MB
  static const char arenaFilenameFormat[] = "Arena%08x.psa";
  static const char arenaFilenameWildcard[] = 
  "Arena[0-9a-f][0-9a-f][0-9a-f][0-9a-f]"
  "[0-9a-f][0-9a-f][0-9a-f][0-9a-f]\\.psa"; // "Arena[0-9a-f]{8}\.psa"
  static const char entryPointTableFilename[] = "EntryPoint.psa";
public:
  PersistentStorage(const char *dir);
  ~PersistentStorage();
  PersistentPointer allocate(SizeType size, BoolType persistent = True);
  BoolType destroy(PersistentPointer &pp);
  PersistentPointer getEntryPoint(EntryID eid);
  BoolType setEntryPoint(EntryID eid, PersistentPointer &pp);
  BoolType synchronize();

  void printStatus() {
    ostream &os = cerr;
    SizeType size;
    os << "PersistentStorage {\n  baseDir " << baseDir
       << "\n  arenaTable {\n  size " << (size = arenaTable.getSize())
       << "\n";
    Arena *arena;
    SizeType index;
    for (index = 0; index < size; index++) {
      arena = arenaTable(index);
      arena->printStatus();
    }
    os << "} // PersistentStorage\n";
  }
protected:
  inline Arena *getArena(ArenaID aid) {
    return arenaTable[aid];
  }
  PointerType grab(PersistentPointer &pp);
  BoolType release(PersistentPointer &pp);
  PointerType grabReadOnly(PersistentPointer &pp);
  BoolType releaseReadOnly(PersistentPointer &pp);

  inline BoolType notifyMapping(SizeType size, 
                                Window *window = 0,
                                Arena *arena = 0) {
    window = 0;
    arena = 0;
    totalWindowSize += size;
    return True;
  }

  inline BoolType notifyUnmapping(SizeType size, 
                                  Window *window = 0,
                                  Arena *arena = 0) {
    window = 0;
    arena = 0;
    totalWindowSize -= size;
    return True;
  }

  SizeType cleanUp(BoolType completely = False);

  BoolType initializeArenaTable();
  BoolType initializeEntryTable();

  inline static PersistentStorage *getThePS();
}; // PersistentStorage


class PersistentObject {
protected:
  ObjectID objectID;
public:
  PersistentObject(int id = 0): objectID(id) {}
  inline ObjectID getID() { return objectID; }
};

class PointerPair: public PersistentPointer {
protected:
  PointerType obj;
public:
  PointerPair(PointerPair &p): PersistentPointer(p), obj(0) {}
  PointerPair(PersistentPointer p): PersistentPointer(p), obj(0) {}
  PointerPair(PointerType o = 0): PersistentPointer(), obj(o = 0) {}
  ~PointerPair() {
    if (isGrabbed())
      PersistentPointer::release();
    if (isGrabbedReadOnly())
      PersistentPointer::releaseReadOnly();
  }
  inline operator PointerType() {
    return obj;
  }
  inline operator PersistentObject *() {
    return (PersistentObject *)obj;
  }
  inline PointerType grab() {
    return obj = PersistentPointer::grab();
  }
  inline BoolType release() {
    if (PersistentPointer::release()) {
      obj = 0; 
      return True;
    } else {
      return False;
    }
  }
  inline PointerType grabReadOnly() {
    return obj = PersistentPointer::grabReadOnly();
  }
  inline BoolType releaseReadOnly() {
    if (PersistentPointer::releaseReadOnly()) {
      obj = 0; 
      return True;
    } else {
      return False;
    }
  }
}; // PointerPair

#endif // __PERSISTENT_STORAGE_H_



Mori Tetsuya / t2y3141592@gmail.com