1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <strings.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <sys/mman.h>
7 #include <errno.h>
8 #include "mread.h"
9 
mread_init(int fd)10 mread_handle_t mread_init(int fd)
11 {
12     struct stat s;
13     mread_handle_t h;
14 
15     h=malloc(sizeof(struct mread_ctrl));
16 
17     if (!h)
18     {
19         perror("malloc");
20         exit(1);
21     }
22 
23     bzero(h, sizeof(struct mread_ctrl));
24 
25     h->fd = fd;
26 
27     fstat(fd, &s);
28     h->file_size = s.st_size;
29 
30     return h;
31 }
32 
mread64(mread_handle_t h,void * rec,ssize_t len,off_t offset)33 ssize_t mread64(mread_handle_t h, void *rec, ssize_t len, off_t offset)
34 {
35     /* Idea: have a "cache" of N mmaped regions.  If the offset is
36      * in one of the regions, just copy it.  If not, evict one of the
37      * regions and map the appropriate range.
38      *
39      * Basic algorithm:
40      *  - See if the offset is in one of the regions
41      *    - If not, map it
42      *       - evict an old region
43      *       - map the new region
44      *  - Copy
45      */
46     char * b=NULL;
47     int bind=-1;
48     off_t boffset=0;
49     ssize_t bsize;
50 
51 #define dprintf(x...)
52 //#define dprintf fprintf
53 
54     dprintf(warn, "%s: offset %llx len %d\n", __func__,
55             offset, len);
56     if ( offset > h->file_size )
57     {
58         dprintf(warn, " offset > file size %llx, returning 0\n",
59                 h->file_size);
60         return 0;
61     }
62     if ( offset + len > h->file_size )
63     {
64         dprintf(warn, " offset+len > file size %llx, truncating\n",
65                 h->file_size);
66         len = h->file_size - offset;
67     }
68 
69     /* Try to find the offset in our range */
70     dprintf(warn, " Trying last, %d\n", last);
71     if ( h->map[h->last].buffer
72          && (offset & MREAD_BUF_MASK) == h->map[h->last].start_offset )
73     {
74         bind=h->last;
75         goto copy;
76     }
77 
78     /* Scan to see if it's anywhere else */
79     dprintf(warn, " Scanning\n");
80     for(bind=0; bind<MREAD_MAPS; bind++)
81         if ( h->map[bind].buffer
82              && (offset & MREAD_BUF_MASK) == h->map[bind].start_offset )
83         {
84             dprintf(warn, "  Found, index %d\n", bind);
85             break;
86         }
87 
88     /* If we didn't find it, evict someone and map it */
89     if ( bind == MREAD_MAPS )
90     {
91         dprintf(warn, " Clock\n");
92         while(1)
93         {
94             h->clock++;
95             if(h->clock >= MREAD_MAPS)
96                 h->clock=0;
97             dprintf(warn, "  %d\n", h->clock);
98             if(h->map[h->clock].buffer == NULL)
99             {
100                 dprintf(warn, "  Buffer null, using\n");
101                 break;
102             }
103             if(!h->map[h->clock].accessed)
104             {
105                 dprintf(warn, "  Not accessed, using\n");
106                 break;
107             }
108             h->map[h->clock].accessed=0;
109         }
110         if(h->map[h->clock].buffer)
111         {
112             dprintf(warn, "  Unmapping\n");
113             munmap(h->map[h->clock].buffer, MREAD_BUF_SIZE);
114         }
115         /* FIXME: Try MAP_HUGETLB? */
116         /* FIXME: Make sure this works on large files... */
117         h->map[h->clock].start_offset = offset & MREAD_BUF_MASK;
118         dprintf(warn, "  Mapping %llx from offset %llx\n",
119                 MREAD_BUF_SIZE, h->map[h->clock].start_offset);
120         h->map[h->clock].buffer = mmap(NULL, MREAD_BUF_SIZE, PROT_READ,
121                                   MAP_SHARED,
122                                   h->fd,
123                                   h->map[h->clock].start_offset);
124         dprintf(warn, "   mmap returned %p\n", h->map[h->clock].buffer);
125         if ( h->map[h->clock].buffer == MAP_FAILED )
126         {
127             h->map[h->clock].buffer = NULL;
128             perror("mmap");
129             exit(1);
130         }
131         bind = h->clock;
132     }
133 
134     h->last=bind;
135 copy:
136     h->map[bind].accessed=1;
137     b=h->map[bind].buffer;
138     boffset=offset - h->map[bind].start_offset;
139     if ( boffset + len > MREAD_BUF_SIZE )
140         bsize = MREAD_BUF_SIZE - boffset;
141     else
142         bsize = len;
143     dprintf(warn, " Using index %d, buffer at %p, buffer offset %llx len %d\n",
144             bind, b, boffset, bsize);
145 
146     bcopy(b+boffset, rec, bsize);
147 
148     /* Handle the boundary case; make sure this is after doing anything
149      * with the static variables*/
150     if ( len > bsize )
151     {
152         dprintf(warn, "  Finishing up by reading l %d o %llx\n",
153                 len-bsize, offset+bsize);
154         mread64(h, rec+bsize, len-bsize, offset+bsize);
155     }
156 
157     /* FIXME: ?? */
158     return len;
159 #undef dprintf
160 }
161