[contrib/mmap] implements a theoretical dynamic growing mmap
[ntk/apt.git] / apt-pkg / contrib / mmap.cc
CommitLineData
578bfd0a
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
0db4a45b 3// $Id: mmap.cc,v 1.22 2001/05/27 05:19:30 jgg Exp $
578bfd0a
AL
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
578bfd0a
AL
16 ##################################################################### */
17 /*}}}*/
18// Include Files /*{{{*/
19#define _BSD_SOURCE
094a497d
AL
20#include <apt-pkg/mmap.h>
21#include <apt-pkg/error.h>
578bfd0a 22
b2e465d6
AL
23#include <apti18n.h>
24
578bfd0a
AL
25#include <sys/mman.h>
26#include <sys/stat.h>
578bfd0a
AL
27#include <unistd.h>
28#include <fcntl.h>
4f333a8b
MV
29
30#include <cstring>
578bfd0a
AL
31 /*}}}*/
32
33// MMap::MMap - Constructor /*{{{*/
34// ---------------------------------------------------------------------
35/* */
2d11135a 36MMap::MMap(FileFd &F,unsigned long Flags) : Flags(Flags), iSize(0),
578bfd0a
AL
37 Base(0)
38{
39 if ((Flags & NoImmMap) != NoImmMap)
2d11135a
AL
40 Map(F);
41}
42 /*}}}*/
43// MMap::MMap - Constructor /*{{{*/
44// ---------------------------------------------------------------------
45/* */
46MMap::MMap(unsigned long Flags) : Flags(Flags), iSize(0),
47 Base(0)
48{
578bfd0a
AL
49}
50 /*}}}*/
51// MMap::~MMap - Destructor /*{{{*/
52// ---------------------------------------------------------------------
53/* */
54MMap::~MMap()
55{
2d11135a 56 Close();
578bfd0a
AL
57}
58 /*}}}*/
59// MMap::Map - Perform the mapping /*{{{*/
60// ---------------------------------------------------------------------
61/* */
2d11135a 62bool MMap::Map(FileFd &Fd)
578bfd0a
AL
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
b35d2f5f 74 if (iSize == 0)
b2e465d6 75 return _error->Error(_("Can't mmap an empty file"));
b35d2f5f 76
578bfd0a
AL
77 // Map it.
78 Base = mmap(0,iSize,Prot,Map,Fd.Fd(),0);
79 if (Base == (void *)-1)
b2e465d6 80 return _error->Errno("mmap",_("Couldn't make mmap of %lu bytes"),iSize);
578bfd0a
AL
81
82 return true;
83}
84 /*}}}*/
85// MMap::Close - Close the map /*{{{*/
86// ---------------------------------------------------------------------
87/* */
2d11135a 88bool MMap::Close(bool DoSync)
578bfd0a 89{
2d11135a 90 if ((Flags & UnMapped) == UnMapped || Base == 0 || iSize == 0)
578bfd0a 91 return true;
2d11135a 92
1164783d
AL
93 if (DoSync == true)
94 Sync();
578bfd0a
AL
95
96 if (munmap((char *)Base,iSize) != 0)
97 _error->Warning("Unable to munmap");
98
99 iSize = 0;
b2e465d6 100 Base = 0;
578bfd0a
AL
101 return true;
102}
103 /*}}}*/
104// MMap::Sync - Syncronize the map with the disk /*{{{*/
105// ---------------------------------------------------------------------
0149949b
AL
106/* This is done in syncronous mode - the docs indicate that this will
107 not return till all IO is complete */
578bfd0a
AL
108bool MMap::Sync()
109{
2d11135a
AL
110 if ((Flags & UnMapped) == UnMapped)
111 return true;
112
de3c15ea 113#ifdef _POSIX_SYNCHRONIZED_IO
1164783d 114 if ((Flags & ReadOnly) != ReadOnly)
e9fce64b 115 if (msync((char *)Base,iSize,MS_SYNC) < 0)
4b1b89c5 116 return _error->Errno("msync","Unable to write mmap");
de3c15ea 117#endif
578bfd0a
AL
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{
2d11135a
AL
126 if ((Flags & UnMapped) == UnMapped)
127 return true;
128
35c22def
AL
129#ifdef _POSIX_SYNCHRONIZED_IO
130 unsigned long PSize = sysconf(_SC_PAGESIZE);
1164783d 131 if ((Flags & ReadOnly) != ReadOnly)
e9fce64b 132 if (msync((char *)Base+(int)(Start/PSize)*PSize,Stop - Start,MS_SYNC) < 0)
4b1b89c5 133 return _error->Errno("msync","Unable to write mmap");
de3c15ea 134#endif
578bfd0a
AL
135 return true;
136}
137 /*}}}*/
138
139// DynamicMMap::DynamicMMap - Constructor /*{{{*/
140// ---------------------------------------------------------------------
141/* */
8e06abb2 142DynamicMMap::DynamicMMap(FileFd &F,unsigned long Flags,unsigned long WorkSpace) :
2d11135a 143 MMap(F,Flags | NoImmMap), Fd(&F), WorkSpace(WorkSpace)
578bfd0a 144{
d38b7b3d
AL
145 if (_error->PendingError() == true)
146 return;
147
2d11135a 148 unsigned long EndOfFile = Fd->Size();
b2e465d6
AL
149 if (EndOfFile > WorkSpace)
150 WorkSpace = EndOfFile;
ea92d036 151 else if(WorkSpace > 0)
b2e465d6 152 {
ea92d036 153 Fd->Seek(WorkSpace - 1);
b2e465d6
AL
154 char C = 0;
155 Fd->Write(&C,sizeof(C));
156 }
157
2d11135a 158 Map(F);
578bfd0a
AL
159 iSize = EndOfFile;
160}
161 /*}}}*/
2d11135a
AL
162// DynamicMMap::DynamicMMap - Constructor for a non-file backed map /*{{{*/
163// ---------------------------------------------------------------------
f1c6a8ca
DK
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 */
2d11135a
AL
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;
f1c6a8ca
DK
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
2d11135a 181 Base = new unsigned char[WorkSpace];
0db4a45b 182 memset(Base,0,WorkSpace);
2d11135a
AL
183 iSize = 0;
184}
185 /*}}}*/
578bfd0a
AL
186// DynamicMMap::~DynamicMMap - Destructor /*{{{*/
187// ---------------------------------------------------------------------
188/* We truncate the file to the size of the memory data set */
189DynamicMMap::~DynamicMMap()
190{
2d11135a
AL
191 if (Fd == 0)
192 {
f1c6a8ca
DK
193#ifdef _POSIX_MAPPED_FILES
194 munmap(Base, WorkSpace);
195#else
2d11135a 196 delete [] (unsigned char *)Base;
f1c6a8ca 197#endif
2d11135a
AL
198 return;
199 }
200
578bfd0a 201 unsigned long EndOfFile = iSize;
1164783d 202 iSize = WorkSpace;
2d11135a 203 Close(false);
3c8cda8b
MV
204 if(ftruncate(Fd->Fd(),EndOfFile) < 0)
205 _error->Errno("ftruncate", _("Failed to truncate file"));
578bfd0a
AL
206}
207 /*}}}*/
208// DynamicMMap::RawAllocate - Allocate a raw chunk of unaligned space /*{{{*/
209// ---------------------------------------------------------------------
f55a958f
AL
210/* This allocates a block of memory aligned to the given size */
211unsigned long DynamicMMap::RawAllocate(unsigned long Size,unsigned long Aln)
578bfd0a
AL
212{
213 unsigned long Result = iSize;
f55a958f
AL
214 if (Aln != 0)
215 Result += Aln - (iSize%Aln);
216
217 iSize = Result + Size;
578bfd0a 218
f1c6a8ca
DK
219 // try to grow the buffer
220 while(Result + Size > WorkSpace)
578bfd0a 221 {
f1c6a8ca
DK
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 }
578bfd0a 228 }
578bfd0a
AL
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 }
578bfd0a
AL
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;
f55a958f 267 I->Start = RawAllocate(I->Count*ItemSize,ItemSize);
578bfd0a 268 }
f55a958f 269
578bfd0a
AL
270 I->Count--;
271 unsigned long Result = I->Start;
470773ca 272 I->Start += ItemSize;
578bfd0a
AL
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;
f1c6a8ca
DK
283 // try to grow the buffer
284 while(Result + Len > WorkSpace)
578bfd0a 285 {
f1c6a8ca
DK
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
6e52073f 294 if (Len == (unsigned long)-1)
578bfd0a
AL
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 /*}}}*/
f1c6a8ca
DK
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 /*}}}*/