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 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
|
/*
* Jeffrey Friedl
* Omron Corporation ʳ
* Nagaokakyoshi, Japan 617Ĺ
*
* jfriedl@nff.ncl.omron.co.jp
*
* This work is placed under the terms of the GNU General Purpose License
* (the "GNU Copyleft").
*
* July 1996
*
* Routine to access a file with virtualmemory-like access.
*/
#include "config.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include "output.h"
#include "xmalloc.h"
#include "strsave.h"
#include "virtfile.h"
#define PAGE_VALID(p) ((p)->text)
/*
* Given a filename, return the length of the file.
*/
long int filesize(const char *filename)
{
struct stat statbuf;
if (stat(filename, &statbuf) < 0)
return -1L;
else
return statbuf.st_size;
}
/*
* PAGE_SIZE -- size of pages loaded. The page size limits the
* length of the longest line.
*/
static unsigned PAGE_SIZE = 0x2000;
/*
* Number of pages to keep in memory at once, for all files.
*/
static unsigned PAGE_COUNT = 40;
/*
* pointer to list of pages....
*/
static Page *common = 0;
static unsigned default_blast = 0;
/*
* Init routine -- fills 'common'. Can change PAGE_COUNT beforehand....
*/
static void init_common(void)
{
int i;
if (common)
return;
common = xmalloc(sizeof(Page) * PAGE_COUNT);
bzero(common, sizeof(Page) * PAGE_COUNT);
}
VirtFile *
OpenVertFile(const char *filename)
{
VirtFile *v;
int fd = open(filename, 0);
if (fd < 0)
return NULL;
if (!common)
init_common();
v = xmalloc(sizeof *v);
v->fd = fd;
v->filename = strsave(filename);
v->length = filesize(filename);
return v;
}
static Page *
LoadPage(VirtFile *v, fileloc start)
{
int i;
Page *empty = 0, *p = 0;
/* See if a page holding the location is already available */
for (i = 0; i < PAGE_COUNT; i++)
{
if (!empty && !PAGE_VALID(&common[i]))
empty = &common[i];
if (common[i].owner == v && common[i].start < start) {
p = &common[i];
break;
}
}
if (!p)
{
/* if not, either pick an empty one, or blast a currently-used one */
if (empty)
p = empty;
else {
p = &common[default_blast];
default_blast = (default_blast + 1) % PAGE_COUNT;
}
}
/*
* If page has no text yet, grab some memory */
if (!PAGE_VALID(p))
p->text = xmalloc(PAGE_SIZE);
/* go to appropriate place in file and read data */
if (lseek(v->fd, start, SEEK_SET) < 0)
die("bad lseek to %ld of %s: %n\n", (long)start);
if (i = read(v->fd, p->text, PAGE_SIZE), i < 0)
die("bad read of %ld bytes starting at %ld of %s: %n\n",
(long)PAGE_SIZE, (long)start, v->filename);
/* fill in other page stuff */
p->start = start;
p->end = start + i;
p->owner = v;
return p;
}
/*
* Given a file an a location, return a page that holds the location
* somewhere.
*/
static Page *
EnsurePositionInMemory(VirtFile *v, fileloc pos)
{
Page *p = 0;
int i;
/* see if page is already there */
for (i = 0; i < PAGE_COUNT; i++)
{
/* must be valid and owned by me */
if (!PAGE_VALID(&common[i]) || (common[i].owner != v))
continue;
/* must have a position in range [start .. end) */
if (pos < common[i].start || pos > common[i].end)
continue;
/* if we don't have a pointer yet, or if this page has the
desired location earlier in the page than the one we already
found, use this new page instead */
if (!p || (pos - p->start) > (pos - common[i].start))
p = &common[i];
}
/* if it's not already here, get a new page */
if (p)
return p;
else
return LoadPage(v, pos);
}
/*
* Given a page and an index into the file (which the given page MUST
* represent), return the length of the line.
*/
struct ScanInfo {
const unsigned char *str_start; /* pointer in memory where line starts */
unsigned short length; /* length of line */
char no_end; /* true if line extends after page */
};
static __inline__ struct ScanInfo *
ScanStrInPage(Page *p, fileloc start)
{
static struct ScanInfo result;
/* PTR is string we'll scan, END is end of the page */
const unsigned char *ptr = &p->text[start - p->start];
const unsigned char *end = &p->text[(int)(p->end - p->start)];
result.str_start = ptr;
/* scan string looking for newline */
while ((ptr < end) && (*ptr != '\n'))
ptr++;
result.length = ptr - result.str_start;
/*
* Past end of page, and... page is full size (i.e. not last page of file)
*/
result.no_end = (ptr >= end && (p->end - p->start >= PAGE_SIZE));
return &result;
}
/*
* Given a file and a location, return a pointer to memory holding the
* string starting at (or spanning) the given location.
* Fill *pCount with the length of the string if not NULL.
*/
const unsigned char *
VirtPos2Str(VirtFile *v, fileloc start, unsigned *pCount)
{
struct ScanInfo *info;
/* some small sanity check */
if (start < 0 || start > v->length)
die("oops: %ld vs. length=%ld.\n", start, v->length);
/* Get the page with the string, and get the string */
info = ScanStrInPage(EnsurePositionInMemory(v, start), start);
/*
* If we hit the end of the page (and the page is a full page,
* and not the last one in the file where a newline might not end
* things), we need to re-load the page such that the target line
* is at the beginning of the page. This will ensure the whole line
* is in memory. Of course, if the line is longer than PAGE_LENGTH
* then life is tough and we return a chopped line.
*/
if (info->no_end)
info = ScanStrInPage(LoadPage(v, start), start);
if (pCount)
*pCount = info->length;
return info->str_start;
}
|