/usr/include/libMems-1.6/libMems/dmSML/buffer.h is in libmems-1.6-dev 1.6.0+4725-4.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | #ifndef _buffer_h_
#define _buffer_h_
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "libMems/dmSML/asyncio.h"
// forward decl for the benefit of iodevice_t
// (can't be avoided)
typedef struct buffer_s buffer_t;
enum {
DEV_FREE,
DEV_BUSY,
};
/*
================
iodevice_t
An IO device represents a physical disk. This is used to make
sure that we're not doing more than one operation on any disk
at a time. The reason is if we are, the OS threads that do the
asynchronous IO will contend for the disk and we'll start seeking.
Seeking is bad.
================
*/
typedef struct iodevice_s {
int op; // an operation number used to enforce serial operation on each device.
int state; // either DEV_FREE or DEV_BUSY as above.
buffer_t *buf; // if we're DEV_BUSY, the buffer we're operating on.
} iodevice_t;
/*
================
buffer_state_t
Buffers are used for IO, and the IO is asynchronous. Buffers
have a bit of state to indicate what their current IO status is.
Ordinarily, buffers are in an OP_NONE state, to indicate no
operation is being performed. When an operation is initiated,
the buffer transitions to OP_PENDING and changes to a valid operation
code when the operation actually starts. When the operation completes,
the buffer is transitioned to the OP_FINISHED state, so that the
program may determine when buffers have completed their operations.
Then the app should transition the state back to OP_NONE.
================
*/
enum {
OP_PENDING = -2,
OP_FINISHED = -1,
OP_NONE = 0,
};
#define CompareKeyPtrs( a, b ) \
((int)(a)->key[0]-(int)(b)->key[0] ? (int)(a)->key[0]-(int)(b)->key[0] : \
(int)(a)->key[1]-(int)(b)->key[1] ? (int)(a)->key[1]-(int)(b)->key[1] : \
(int)(a)->key[2]-(int)(b)->key[2] ? (int)(a)->key[2]-(int)(b)->key[2] : \
(int)(a)->key[3]-(int)(b)->key[3] ? (int)(a)->key[3]-(int)(b)->key[3] : \
(int)(a)->key[4]-(int)(b)->key[4] ? (int)(a)->key[4]-(int)(b)->key[4] : \
(int)(a)->key[5]-(int)(b)->key[5] ? (int)(a)->key[5]-(int)(b)->key[5] : \
(int)(a)->key[6]-(int)(b)->key[6] ? (int)(a)->key[6]-(int)(b)->key[6] : \
(int)(a)->key[7]-(int)(b)->key[7] ? (int)(a)->key[7]-(int)(b)->key[7] : \
(int)(a)->key[8]-(int)(b)->key[8] ? (int)(a)->key[8]-(int)(b)->key[8] : \
(int)(a)->key[9]-(int)(b)->key[9] ? (int)(a)->key[9]-(int)(b)->key[9] : 0)
#define COMPARE_KEYS( a, b ) \
((a).key[0]!=(b).key[0] ? (a).key[0]-(b).key[0] : \
(a).key[1]!=(b).key[1] ? (a).key[1]-(b).key[1] : \
(a).key[2]!=(b).key[2] ? (a).key[2]-(b).key[2] : \
(a).key[3]!=(b).key[3] ? (a).key[3]-(b).key[3] : \
(a).key[4]!=(b).key[4] ? (a).key[4]-(b).key[4] : \
(a).key[5]!=(b).key[5] ? (a).key[5]-(b).key[5] : \
(a).key[6]!=(b).key[6] ? (a).key[6]-(b).key[6] : \
(a).key[7]!=(b).key[7] ? (a).key[7]-(b).key[7] : \
(a).key[8]!=(b).key[8] ? (a).key[8]-(b).key[8] : \
(a).key[9]!=(b).key[9] ? (a).key[9]-(b).key[9] : 0)
// this is the record as in the files to be sorted
typedef struct record_s {
unsigned char key[10];
unsigned char num[1];
unsigned char payload[1];
} record_t;
int CompareKeys_qsort_wrapper( const void *r1, const void *r2 );
int CompareKeys( const record_t *r1, const record_t *r2 );
/*
================
buffer_t
This is the unit of information most commonly dealt with.
We read into these, and write these out, and use these for
binning. A single Working Set of these buffers should be
used for the duration of the program, and they should be
managed with the buffer lists below.
sizeof( buffer_t ) == 32, so the overhead isn't bad.
================
*/
struct buffer_s {
aFILE *file; // the file this buffer is attached to for IO ops
iodevice_t *device; // which IO device this is on (for scheduling IO)
int operation; // either OP_NONE, OP_FINISHED, or the op #.
offset_t numrecs; // the number of valid records in this buffer.
offset_t totalrecs; // number of real records in recs
int fileop; // operation number on device to ensure serialized ops.
record_t *recs; // actual record storage
struct buffer_s *next; // for chaining lists together.
struct buffer_s *last;
offset_t io_size; // amount of data for i/o, need not be equal to numrecs
long long input_pos; // the sequence offset that this data was read from, only valid during binning phase
offset_t io_pos; // the file offset for I/O, set to CURRENT_POS to use the current file seek pointer
};
/*
================
buffer_list_t
Buffer lists are used to manage pools, like the free list,
the reading list, the to process list, and a list for each bin.
We use circular lists because they're simpler.
================
*/
typedef struct buffer_list_s {
int nitems;
buffer_t * head;
} buffer_list_t;
/*
================
working_set_t
Working sets are collections of buffers. They are useful so that
you can use a fixed amount of memory to deal with things. The
problem then becomes internal working set management.
================
*/
typedef struct working_set_s {
offset_t size; // actual size of working set in bytes
int nbufs;
buffer_t *bufs;
} working_set_t;
// Working Set support.
// returns resulting size of the entire structure.
// goalsize is the desired size of the working set in bytes, minbufsize and maxbufsize
// are the minimum and maximum desired number of records in buffers. The buffers will
// be allocated with random sizes in this range until the desired goalsize is reached.
// this will return 0 in the case of a malloc error or if the goalsize is too small to
// have any buffers allocated for it.
int MakeWorkingSet( working_set_t * ws, offset_t goalsize, offset_t minrecs, offset_t maxrecs );
// Working Set support.
// Reorganize the working set with a different distribution of buffers.
void ReorganizeWorkingSet( working_set_t * ws, offset_t minrecs, offset_t maxrecs );
// this updates all the IO on the working set buffers, querying those that
// are not in OP_FINISHED or OP_NONE and putting those that finish into OP_FINISHED
void UpdateWSIOFinishedState( working_set_t * ws );
// this updates the IO on a particular device. this routine and the one above
// should probably be called as this one after that one, and in addtion, this
// one called for every device in the system.
void UpdateDeviceIOExecuteState( working_set_t * ws, iodevice_t * dev );
// buffer list manipulations
// returns argument
buffer_list_t * InitList( buffer_list_t * list );
void PushHead( buffer_list_t * list, buffer_t * item );
void PushTail( buffer_list_t * list, buffer_t * item );
buffer_t * PopHead( buffer_list_t * list );
buffer_t * PopTail( buffer_list_t * list );
// returns second argument
buffer_t * RemoveItem( buffer_list_t * list, buffer_t * item );
// read and write to/from disk.
void ReadBuffer( buffer_t * buffer, offset_t num_recs, iodevice_t * dev );
void WriteBuffer( buffer_t * buffer, offset_t num_recs, iodevice_t * dev );
#endif /* _buffer_h_ */
|