]> git.saurik.com Git - apt.git/blame_incremental - apt-pkg/contrib/mmap.cc
[contrib/mmap] implements a theoretical dynamic growing mmap
[apt.git] / apt-pkg / contrib / mmap.cc
... / ...
CommitLineData
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
3// $Id: mmap.cc,v 1.22 2001/05/27 05:19:30 jgg Exp $
4/* ######################################################################
5
6 MMap Class - Provides 'real' mmap or a faked mmap using read().
7
8 MMap cover class.
9
10 Some broken versions of glibc2 (libc6) have a broken definition
11 of mmap that accepts a char * -- all other systems (and libc5) use
12 void *. We can't safely do anything here that would be portable, so
13 libc6 generates warnings -- which should be errors, g++ isn't properly
14 strict.
15
16 ##################################################################### */
17 /*}}}*/
18// Include Files /*{{{*/
19#define _BSD_SOURCE
20#include <apt-pkg/mmap.h>
21#include <apt-pkg/error.h>
22
23#include <apti18n.h>
24
25#include <sys/mman.h>
26#include <sys/stat.h>
27#include <unistd.h>
28#include <fcntl.h>
29
30#include <cstring>
31 /*}}}*/
32
33// MMap::MMap - Constructor /*{{{*/
34// ---------------------------------------------------------------------
35/* */
36MMap::MMap(FileFd &F,unsigned long Flags) : Flags(Flags), iSize(0),
37 Base(0)
38{
39 if ((Flags & NoImmMap) != NoImmMap)
40 Map(F);
41}
42 /*}}}*/
43// MMap::MMap - Constructor /*{{{*/
44// ---------------------------------------------------------------------
45/* */
46MMap::MMap(unsigned long Flags) : Flags(Flags), iSize(0),
47 Base(0)
48{
49}
50 /*}}}*/
51// MMap::~MMap - Destructor /*{{{*/
52// ---------------------------------------------------------------------
53/* */
54MMap::~MMap()
55{
56 Close();
57}
58 /*}}}*/
59// MMap::Map - Perform the mapping /*{{{*/
60// ---------------------------------------------------------------------
61/* */
62bool MMap::Map(FileFd &Fd)
63{
64 iSize = Fd.Size();
65
66 // Set the permissions.
67 int Prot = PROT_READ;
68 int Map = MAP_SHARED;
69 if ((Flags & ReadOnly) != ReadOnly)
70 Prot |= PROT_WRITE;
71 if ((Flags & Public) != Public)
72 Map = MAP_PRIVATE;
73
74 if (iSize == 0)
75 return _error->Error(_("Can't mmap an empty file"));
76
77 // Map it.
78 Base = mmap(0,iSize,Prot,Map,Fd.Fd(),0);
79 if (Base == (void *)-1)
80 return _error->Errno("mmap",_("Couldn't make mmap of %lu bytes"),iSize);
81
82 return true;
83}
84 /*}}}*/
85// MMap::Close - Close the map /*{{{*/
86// ---------------------------------------------------------------------
87/* */
88bool MMap::Close(bool DoSync)
89{
90 if ((Flags & UnMapped) == UnMapped || Base == 0 || iSize == 0)
91 return true;
92
93 if (DoSync == true)
94 Sync();
95
96 if (munmap((char *)Base,iSize) != 0)
97 _error->Warning("Unable to munmap");
98
99 iSize = 0;
100 Base = 0;
101 return true;
102}
103 /*}}}*/
104// MMap::Sync - Syncronize the map with the disk /*{{{*/
105// ---------------------------------------------------------------------
106/* This is done in syncronous mode - the docs indicate that this will
107 not return till all IO is complete */
108bool MMap::Sync()
109{
110 if ((Flags & UnMapped) == UnMapped)
111 return true;
112
113#ifdef _POSIX_SYNCHRONIZED_IO
114 if ((Flags & ReadOnly) != ReadOnly)
115 if (msync((char *)Base,iSize,MS_SYNC) < 0)
116 return _error->Errno("msync","Unable to write mmap");
117#endif
118 return true;
119}
120 /*}}}*/
121// MMap::Sync - Syncronize a section of the file to disk /*{{{*/
122// ---------------------------------------------------------------------
123/* */
124bool MMap::Sync(unsigned long Start,unsigned long Stop)
125{
126 if ((Flags & UnMapped) == UnMapped)
127 return true;
128
129#ifdef _POSIX_SYNCHRONIZED_IO
130 unsigned long PSize = sysconf(_SC_PAGESIZE);
131 if ((Flags & ReadOnly) != ReadOnly)
132 if (msync((char *)Base+(int)(Start/PSize)*PSize,Stop - Start,MS_SYNC) < 0)
133 return _error->Errno("msync","Unable to write mmap");
134#endif
135 return true;
136}
137 /*}}}*/
138
139// DynamicMMap::DynamicMMap - Constructor /*{{{*/
140// ---------------------------------------------------------------------
141/* */
142DynamicMMap::DynamicMMap(FileFd &F,unsigned long Flags,unsigned long WorkSpace) :
143 MMap(F,Flags | NoImmMap), Fd(&F), WorkSpace(WorkSpace)
144{
145 if (_error->PendingError() == true)
146 return;
147
148 unsigned long EndOfFile = Fd->Size();
149 if (EndOfFile > WorkSpace)
150 WorkSpace = EndOfFile;
151 else if(WorkSpace > 0)
152 {
153 Fd->Seek(WorkSpace - 1);
154 char C = 0;
155 Fd->Write(&C,sizeof(C));
156 }
157
158 Map(F);
159 iSize = EndOfFile;
160}
161 /*}}}*/
162// DynamicMMap::DynamicMMap - Constructor for a non-file backed map /*{{{*/
163// ---------------------------------------------------------------------
164/* We try here to use mmap to reserve some space - this is much more
165 cooler than the fallback solution to simply allocate a char array
166 and could come in handy later than we are able to grow such an mmap */
167DynamicMMap::DynamicMMap(unsigned long Flags,unsigned long WorkSpace) :
168 MMap(Flags | NoImmMap | UnMapped), Fd(0), WorkSpace(WorkSpace)
169{
170 if (_error->PendingError() == true)
171 return;
172
173#ifdef _POSIX_MAPPED_FILES
174 // use anonymous mmap() to get the memory
175 Base = (unsigned char*) mmap(0, WorkSpace, PROT_READ|PROT_WRITE,
176 MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
177 if(Base != MAP_FAILED)
178 return;
179#endif
180 // fallback to a static allocated space
181 Base = new unsigned char[WorkSpace];
182 memset(Base,0,WorkSpace);
183 iSize = 0;
184}
185 /*}}}*/
186// DynamicMMap::~DynamicMMap - Destructor /*{{{*/
187// ---------------------------------------------------------------------
188/* We truncate the file to the size of the memory data set */
189DynamicMMap::~DynamicMMap()
190{
191 if (Fd == 0)
192 {
193#ifdef _POSIX_MAPPED_FILES
194 munmap(Base, WorkSpace);
195#else
196 delete [] (unsigned char *)Base;
197#endif
198 return;
199 }
200
201 unsigned long EndOfFile = iSize;
202 iSize = WorkSpace;
203 Close(false);
204 if(ftruncate(Fd->Fd(),EndOfFile) < 0)
205 _error->Errno("ftruncate", _("Failed to truncate file"));
206}
207 /*}}}*/
208// DynamicMMap::RawAllocate - Allocate a raw chunk of unaligned space /*{{{*/
209// ---------------------------------------------------------------------
210/* This allocates a block of memory aligned to the given size */
211unsigned long DynamicMMap::RawAllocate(unsigned long Size,unsigned long Aln)
212{
213 unsigned long Result = iSize;
214 if (Aln != 0)
215 Result += Aln - (iSize%Aln);
216
217 iSize = Result + Size;
218
219 // try to grow the buffer
220 while(Result + Size > WorkSpace)
221 {
222 if(!Grow())
223 {
224 _error->Error(_("Dynamic MMap ran out of room. Please increase the size "
225 "of APT::Cache-Limit. Current value: %lu. (man 5 apt.conf)"), WorkSpace);
226 return 0;
227 }
228 }
229 return Result;
230}
231 /*}}}*/
232// DynamicMMap::Allocate - Pooled aligned allocation /*{{{*/
233// ---------------------------------------------------------------------
234/* This allocates an Item of size ItemSize so that it is aligned to its
235 size in the file. */
236unsigned long DynamicMMap::Allocate(unsigned long ItemSize)
237{
238 // Look for a matching pool entry
239 Pool *I;
240 Pool *Empty = 0;
241 for (I = Pools; I != Pools + PoolCount; I++)
242 {
243 if (I->ItemSize == 0)
244 Empty = I;
245 if (I->ItemSize == ItemSize)
246 break;
247 }
248 // No pool is allocated, use an unallocated one
249 if (I == Pools + PoolCount)
250 {
251 // Woops, we ran out, the calling code should allocate more.
252 if (Empty == 0)
253 {
254 _error->Error("Ran out of allocation pools");
255 return 0;
256 }
257
258 I = Empty;
259 I->ItemSize = ItemSize;
260 I->Count = 0;
261 }
262
263 // Out of space, allocate some more
264 if (I->Count == 0)
265 {
266 I->Count = 20*1024/ItemSize;
267 I->Start = RawAllocate(I->Count*ItemSize,ItemSize);
268 }
269
270 I->Count--;
271 unsigned long Result = I->Start;
272 I->Start += ItemSize;
273 return Result/ItemSize;
274}
275 /*}}}*/
276// DynamicMMap::WriteString - Write a string to the file /*{{{*/
277// ---------------------------------------------------------------------
278/* Strings are not aligned to anything */
279unsigned long DynamicMMap::WriteString(const char *String,
280 unsigned long Len)
281{
282 unsigned long Result = iSize;
283 // try to grow the buffer
284 while(Result + Len > WorkSpace)
285 {
286 if(!Grow())
287 {
288 _error->Error(_("Dynamic MMap ran out of room. Please increase the size "
289 "of APT::Cache-Limit. Current value: %lu. (man 5 apt.conf)"), WorkSpace);
290 return 0;
291 }
292 }
293
294 if (Len == (unsigned long)-1)
295 Len = strlen(String);
296 iSize += Len + 1;
297 memcpy((char *)Base + Result,String,Len);
298 ((char *)Base)[Result + Len] = 0;
299 return Result;
300}
301 /*}}}*/
302// DynamicMMap::Grow - Grow the mmap /*{{{*/
303// ---------------------------------------------------------------------
304/* This method will try to grow the mmap we currently use. This doesn't
305 work most of the time because we can't move the mmap around in the
306 memory for now as this would require to adjust quite a lot of pointers
307 but why we should not at least try to grow it before we give up? */
308bool DynamicMMap::Grow()
309{
310#ifdef _POSIX_MAPPED_FILES
311 unsigned long newSize = WorkSpace + 1024*1024;
312
313 if(Fd != 0)
314 {
315 Fd->Seek(newSize - 1);
316 char C = 0;
317 Fd->Write(&C,sizeof(C));
318 }
319
320 Base = mremap(Base, WorkSpace, newSize, 0);
321 if(Base == MAP_FAILED)
322 return false;
323
324 WorkSpace = newSize;
325 return true;
326#else
327 return false;
328#endif
329}
330 /*}}}*/