typedef struct Config           Config;
typedef struct AMap             AMap;
typedef struct AMapN            AMapN;
typedef struct Arena            Arena;
typedef struct ArenaHead        ArenaHead;
typedef struct ArenaPart        ArenaPart;
typedef struct CIBlock          CIBlock;
typedef struct Clump            Clump;
typedef struct ClumpInfo        ClumpInfo;
typedef struct IAddr            IAddr;
typedef struct IBucket          IBucket;
typedef struct ICache           ICache;
typedef struct IEStream         IEStream;
typedef struct IEntry           IEntry;
typedef struct IFile            IFile;
typedef struct ISect            ISect;
typedef struct Index            Index;
typedef struct Lump             Lump;
typedef struct DBlock           DBlock;
typedef struct Part             Part;
typedef struct Stats            Stats;
typedef struct ZBlock           ZBlock;

#define TWID32  ((u32int)~(u32int)0)
#define TWID64  ((u64int)~(u64int)0)
#define TWID8   ((u8int)~(u8int)0)

enum
{
       ABlockLog               = 9,            /* log2(512), the quantum for reading arenas */
       ANameSize               = 64,
       MaxDiskBlock            = 64*1024,      /* max. allowed size for a disk block */
       MaxIoSize               = 64*1024,      /* max. allowed size for a disk io operation */
       PartBlank               = 256*1024,     /* untouched section at beginning of partition */
       HeadSize                = 512,          /* size of a header after PartBlank */
       MinArenaSize            = 1*1024*1024,  /* smallest reasonable arena size */
       IndexBase               = 1024*1024,    /* initial address to use in an index */
       MaxIo                   = 64*1024,      /* max size of a single read or write operation */
       ICacheBits              = 16,           /* default bits for indexing icache */
       ICacheDepth             = 4,            /* default depth of an icache hash chain */
       MaxAMap                 = 2*1024,       /* max. allowed arenas in an address mapping; must be < 32*1024 */

       /*
        * return codes from syncArena
        */
       SyncDataErr     = 1 << 0,               /* problem reading the clump data */
       SyncCIErr       = 1 << 1,               /* found erroneous clump directory entries */
       SyncCIZero      = 1 << 2,               /* found unwritten clump directory entries */
       SyncFixErr      = 1 << 3,               /* error writing fixed data */
       SyncHeader      = 1 << 4,               /* altered header fields */

       /*
        * error severity
        */
       EOk                     = 0,            /* error expected in normal operation */
       EStrange,                               /* strange error that should be logged */
       ECorrupt,                               /* corrupted data found in arenas */
       EICorrupt,                              /* corrupted data found in index */
       EAdmin,                                 /* should be brought to administrators' attention */
       ECrash,                                 /* really bad internal error */
       EBug,                                   /* a limitation which should be fixed */
       EInconsist,                             /* inconsistencies between index and arena */
       EMax,

       /*
        * internal disk formats for the venti archival storage system
        */
       /*
        * magic numbers on disk
        */
       ClumpMagic              = 0xd15cb10c,   /* clump header */
       ClumpFreeMagic          = 0,            /* free clump; terminates active clump log */

       ArenaPartMagic          = 0xa9e4a5e7,   /* arena partition header */
       ArenaMagic              = 0xf2a14ead,   /* arena trailer */
       ArenaHeadMagic          = 0xd15c4ead,   /* arena header */

       ISectMagic              = 0xd15c5ec7,   /* index header */

       ArenaPartVersion        = 3,
       ArenaVersion            = 4,
       IndexVersion            = 1,
       ISectVersion            = 1,

       /*
        * encodings of clumps on disk
        */
       ClumpEErr               = 0,            /* can't happen */
       ClumpENone,                             /* plain */
       ClumpECompress,                         /* compressed */
       ClumpEMax,

       /*
        * marker for corrupted data on disk
        */
       VtTypeCorrupt           = VtPrivateTypes,

       /*
        * sizes in bytes on disk
        */
       U8Size                  = 1,
       U16Size                 = 2,
       U32Size                 = 4,
       U64Size                 = 8,

       ArenaPartSize           = 4 * U32Size,
       ArenaSize               = 2 * U64Size + 6 * U32Size + ANameSize + U8Size,
       ArenaHeadSize           = U64Size + 3 * U32Size + ANameSize,
       ISectSize               = 7 * U32Size + 2 * ANameSize,
       ClumpInfoSize           = U8Size + 2 * U16Size + VtScoreSize,
       ClumpSize               = ClumpInfoSize + U8Size + 3 * U32Size,
       IBucketSize             = U32Size + U16Size,
       IEntrySize              = U64Size + U32Size + 2*U16Size + 2*U8Size + VtScoreSize,
       IEntryTypeOff           = VtScoreSize + U64Size + U32Size + 2 * U16Size,

       MaxClumpBlocks          =  (VtMaxLumpSize + ClumpSize + (1 << ABlockLog) - 1) >> ABlockLog,

       VentiZZZZZZZZ
};

/*
* results of parsing and initializing a config file
*/
struct Config
{
       char            *index;                 /* name of the index to initialize */
       int             naparts;                /* arena partitions initialized */
       ArenaPart       **aparts;
       int             nsects;                 /* index sections initialized */
       ISect           **sects;
};

/*
* a Part is the low level interface to files or disks.
* there are two main types of partitions
*      arena paritions, which some number of arenas, each in a sub-partition.
*      index partition, which only have one subpartition.
*/
struct Part
{
       int             fd;                     /* rock for accessing the disk */
       u64int          size;                   /* size of the partiton */
       u32int          blockSize;              /* block size for reads and writes */
       char            *name;
};

/*
* a cached block from the partition
* yuck -- most of this is internal structure for the cache
* all other routines should only use data
*/
struct DBlock
{
       u8int   *data;

       Part    *part;                  /* partition in which cached */
       u64int  addr;                   /* base address on the partition */
       u16int  size;                   /* amount of data available, not amount allocated; should go away */
       DBlock  *next;                  /* doubly linked hash chains */
       DBlock  *prev;
       u32int  heap;                   /* index in heap table */
       u32int  used;                   /* last reference times */
       u32int  used2;
       u32int  ref;                    /* reference count */
       VtLock  *lock;                  /* for access to data only */
};

/*
* a cached block from the partition
* yuck -- most of this is internal structure for the cache
* all other routines should only use data
* double yuck -- this is mostly the same as a DBlock
*/
struct Lump
{
       Packet  *data;

       Part    *part;                  /* partition in which cached */
       u8int   score[VtScoreSize];     /* score of packet */
       u8int   type;                   /* type of packet */
       u16int  size;                   /* amount of data allocated to hold packet */
       Lump    *next;                  /* doubly linked hash chains */
       Lump    *prev;
       u32int  heap;                   /* index in heap table */
       u32int  used;                   /* last reference times */
       u32int  used2;
       u32int  ref;                    /* reference count */
       VtLock  *lock;                  /* for access to data only */
};

/*
* mapping between names and address ranges
*/
struct AMap
{
       u64int          start;
       u64int          stop;
       char            name[ANameSize];
};

/*
* an AMap along with a length
*/
struct AMapN
{
       int             n;
       AMap            *map;
};

/*
* an ArenaPart is a partition made up of Arenas
* it exists because most os's don't support many partitions,
* and we want to have many different Arenas
*/
struct ArenaPart
{
       Part            *part;
       u64int          size;                   /* size of underlying partition, rounded down to blocks */
       Arena           **arenas;
       u32int          tabBase;                /* base address of arena table on disk */
       u32int          tabSize;                /* max. bytes in arena table */

       /*
        * fields stored on disk
        */
       u32int          version;
       u32int          blockSize;              /* "optimal" block size for reads and writes */
       u32int          arenaBase;              /* base address of first arena */

       /*
        * stored in the arena mapping table on disk
        */
       AMap            *map;
       int             narenas;
};

/*
* info about one block in the clump info cache
*/
struct CIBlock
{
       u32int          block;                  /* blocks in the directory */
       int             offset;                 /* offsets of one clump in the data */
       DBlock          *data;
};

/*
* an Arena is a log of Clumps, preceeded by an ArenaHeader,
* and followed by a Arena, each in one disk block.
* struct on disk is not always up to date, but should be self-consistent.
* to sync after reboot, follow clumps starting at used until ClumpFreeMagic if found.
* <struct name="Arena" type="Arena *">
*      <field name="name" val="s->name" type="AName"/>
*      <field name="version" val="s->version" type="U32int"/>
*      <field name="partition" val="s->part->name" type="AName"/>
*      <field name="blockSize" val="s->blockSize" type="U32int"/>
*      <field name="start" val="s->base" type="U64int"/>
*      <field name="stop" val="s->base+2*s->blockSize" type="U64int"/>
*      <field name="created" val="s->ctime" type="U32int"/>
*      <field name="modified" val="s->wtime" type="U32int"/>
*      <field name="sealed" val="s->sealed" type="Sealed"/>
*      <field name="score" val="s->score" type="Score"/>
*      <field name="clumps" val="s->clumps" type="U32int"/>
*      <field name="compressedClumps" val="s->cclumps" type="U32int"/>
*      <field name="data" val="s->uncsize" type="U64int"/>
*      <field name="compressedData" val="s->used - s->clumps * ClumpSize" type="U64int"/>
*      <field name="storage" val="s->used + s->clumps * ClumpInfoSize" type="U64int"/>
* </struct>
*/
struct Arena
{
       VtLock          *lock;                  /* lock for arena fields, writing to disk */
       Part            *part;                  /* partition in which arena lives */
       int             blockSize;              /* size of block to read or write */
       u64int          base;                   /* base address on disk */
       u64int          size;                   /* total space in the arena */
       u64int          limit;                  /* storage limit for clumps */
       u8int           score[VtScoreSize];     /* score of the entire sealed & summed arena */

       int             clumpMax;               /* ClumpInfos per block */
       CIBlock         cib;                    /* dirty clump directory block */

       /*
        * fields stored on disk
        */
       u32int          version;
       char            name[ANameSize];        /* text label */
       u32int          clumps;                 /* number of allocated clumps */
       u32int          cclumps;                /* clumps which are compressed; informational only */
       u32int          ctime;                  /* first time a block was written */
       u32int          wtime;                  /* last time a block was written */
       u64int          used;                   /* number of bytes currently used */
       u64int          uncsize;                /* total of all clumps's uncsize; informational only */
       u8int           sealed;                 /* arena all filled up? */
};

/*
* redundant storage of some fields at the beginning of each arena
*/
struct ArenaHead
{
       u32int          version;
       char            name[ANameSize];
       u32int          blockSize;
       u64int          size;
};

/*
* most interesting meta information for a clump.
* stored in each clump's header and in the Arena's directory,
* stored in reverse order just prior to the arena trailer
*/
struct ClumpInfo
{
       u8int           type;
       u16int          size;                   /* size of disk data, not including header */
       u16int          uncsize;                /* size of uncompressed data */
       u8int           score[VtScoreSize];     /* score of the uncompressed data only */
};

/*
* header for an immutable clump of data
*/
struct Clump
{
       ClumpInfo       info;
       u8int           encoding;
       u32int          creator;                /* initial client which wrote the block */
       u32int          time;                   /* creation at gmt seconds since 1/1/1970 */
};

/*
* index of all clumps according to their score
* this is just a wrapper to tie together the index sections
* <struct name="Index" type="Index *">
*      <field name="name" val="s->name" type="AName"/>
*      <field name="version" val="s->version" type="U32int"/>
*      <field name="blockSize" val="s->blockSize" type="U32int"/>
*      <field name="tabSize" val="s->tabSize" type="U32int"/>
*      <field name="buckets" val="s->buckets" type="U32int"/>
*      <field name="buckDiv" val="s->div" type="U32int"/>
*      <array name="sect" val="&s->smap[i]" elems="s->nsects" type="Amap"/>
*      <array name="amap" val="&s->amap[i]" elems="s->narenas" type="Amap"/>
*      <array name="arena" val="s->arenas[i]" elems="s->narenas" type="Arena"/>
* </struct>
* <struct name="Amap" type="AMap *">
*      <field name="name" val="s->name" type="AName"/>
*      <field name="start" val="s->start" type="U64int"/>
*      <field name="stop" val="s->stop" type="U64int"/>
* </struct>
*/
struct Index
{
       u32int          div;                    /* divisor for mapping score to bucket */
       u32int          buckets;                /* last bucket used in disk hash table */
       u32int          blockSize;
       u32int          tabSize;                /* max. bytes in index config */
       int             mapAlloc;               /* first arena to check when adding a lump */
       Arena           **arenas;               /* arenas in the mapping */
       ISect           **sects;                /* sections which hold the buckets */

       /*
        * fields stored in config file
        */
       u32int          version;
       char            name[ANameSize];        /* text label */
       int             nsects;
       AMap            *smap;                  /* mapping of buckets to index sections */
       int             narenas;
       AMap            *amap;                  /* mapping from index addesses to arenas */
};

/*
* one part of the bucket storage for an index.
* the index blocks are sequentially allocated
* across all of the sections.
*/
struct ISect
{
       Part            *part;
       int             blockLog;               /* log2(blockSize) */
       int             buckMax;                /* max. entries in a index bucket */
       u32int          tabBase;                /* base address of index config table on disk */
       u32int          tabSize;                /* max. bytes in index config */

       /*
        * fields stored on disk
        */
       u32int          version;
       char            name[ANameSize];        /* text label */
       char            index[ANameSize];       /* index owning the section */
       u32int          blockSize;              /* size of hash buckets in index */
       u32int          blockBase;              /* address of start of on disk index table */
       u32int          blocks;                 /* total blocks on disk; some may be unused */
       u32int          start;                  /* first bucket in this section */
       u32int          stop;                   /* limit of buckets in this section */
};

/*
* externally interesting part of an IEntry
*/
struct IAddr
{
       u64int          addr;
       u16int          size;                   /* uncompressed size */
       u8int           type;                   /* type of block */
       u8int           blocks;                 /* arena io quanta for Clump + data */
};

/*
* entries in the index
* kept in IBuckets in the disk index table,
* cached in the memory ICache.
*/
struct IEntry
{
       u8int           score[VtScoreSize];
       IEntry          *next;                  /* next in hash chain */
       u32int          wtime;                  /* last write time */
       u16int          train;                  /* relative train containing the most recent ref; 0 if no ref, 1 if in same car */
       IAddr           ia;
};

/*
* buckets in the on disk index table
*/
struct IBucket
{
       u16int          n;                      /* number of active indices */
       u32int          next;                   /* overflow bucket */
       u8int           *data;
};

/*
* temporary buffers used by individual threads
*/
struct ZBlock
{
       u32int          len;
       u8int           *data;
};

/*
* simple input buffer for a '\0' terminated text file
*/
struct IFile
{
       char            *name;                          /* name of the file */
       ZBlock          *b;                             /* entire contents of file */
       u32int          pos;                            /* current position in the file */
};

/*
* statistics about the operation of the server
* mainly for performance monitoring and profiling.
*/
struct Stats
{
       VtLock          *lock;
       long            lumpWrites;             /* protocol block writes */
       long            lumpReads;              /* protocol block reads */
       long            lumpHit;                /* lump cache hit */
       long            lumpMiss;               /* lump cache miss */
       long            clumpWrites;            /* clumps to disk */
       vlong           clumpBWrites;           /* clump data bytes to disk */
       vlong           clumpBComp;             /* clump bytes compressed */
       long            clumpReads;             /* clumps from disk */
       vlong           clumpBReads;            /* clump data bytes from disk */
       vlong           clumpBUncomp;           /* clump bytes uncompressed */
       long            ciWrites;               /* clump directory to disk */
       long            ciReads;                /* clump directory from disk */
       long            indexWrites;            /* index to disk */
       long            indexReads;             /* index from disk */
       long            indexWReads;            /* for writing a new entry */
       long            indexAReads;            /* for allocating an overflow block */
       long            diskWrites;             /* total disk writes */
       long            diskReads;              /* total disk reads */
       vlong           diskBWrites;            /* total disk bytes written */
       vlong           diskBReads;             /* total disk bytes read */
       long            pcHit;                  /* partition cache hit */
       long            pcMiss;                 /* partition cache miss */
       long            pcReads;                /* partition cache reads from disk */
       vlong           pcBReads;               /* partition cache bytes read */
       long            icInserts;              /* stores into index cache */
       long            icLookups;              /* index cache lookups */
       long            icHits;                 /* hits in the cache */
       long            icFills;                /* successful fills from index */
};

extern  Index           *mainIndex;
extern  u32int          maxBlockSize;           /* max. block size used by any partition */
extern  int             paranoid;               /* should verify hashes on disk read */
extern  int             queueWrites;            /* put all lump writes on a queue and finish later */
extern  int             readonly;               /* only allowed to read the disk data */
extern  Stats           stats;
extern  u8int           zeroScore[VtScoreSize];