1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is nsDiskCacheBlockFile.cpp, released
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 2001
22 * the Initial Developer. All Rights Reserved.
25 * Gordon Sheridan <gordon@netscape.com>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either the GNU General Public License Version 2 or later (the "GPL"), or
29 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 #include "nsDiskCache.h"
42 #include "nsDiskCacheBlockFile.h"
44 /******************************************************************************
45 * nsDiskCacheBlockFile -
46 *****************************************************************************/
48 const unsigned short kBitMapBytes
= 4096;
49 const unsigned short kBitMapWords
= (kBitMapBytes
/4);
51 /******************************************************************************
53 *****************************************************************************/
55 nsDiskCacheBlockFile::Open( nsILocalFile
* blockFile
, PRUint32 blockSize
)
59 mBlockSize
= blockSize
;
61 // open the file - restricted to user, the data could be confidential
62 nsresult rv
= blockFile
->OpenNSPRFileDesc(PR_RDWR
| PR_CREATE_FILE
, 00600, &mFD
);
63 if (NS_FAILED(rv
)) return rv
; // unable to open or create file
65 // allocate bit map buffer
66 mBitMap
= new PRUint32
[kBitMapWords
];
68 rv
= NS_ERROR_OUT_OF_MEMORY
;
72 // check if we just creating the file
73 fileSize
= PR_Available(mFD
);
75 // XXX an error occurred. We could call PR_GetError(), but how would that help?
76 rv
= NS_ERROR_UNEXPECTED
;
80 // initialize bit map and write it
81 memset(mBitMap
, 0, kBitMapBytes
);
82 PRInt32 bytesWritten
= PR_Write(mFD
, mBitMap
, kBitMapBytes
);
83 if (bytesWritten
< kBitMapBytes
)
86 } else if (fileSize
< kBitMapBytes
) {
87 rv
= NS_ERROR_UNEXPECTED
; // XXX NS_ERROR_CACHE_INVALID;
92 const PRInt32 bytesRead
= PR_Read(mFD
, mBitMap
, kBitMapBytes
);
93 if (bytesRead
< kBitMapBytes
) {
94 rv
= NS_ERROR_UNEXPECTED
;
97 #if defined(IS_LITTLE_ENDIAN)
98 // Swap from network format
99 for (int i
= 0; i
< kBitMapWords
; ++i
)
100 mBitMap
[i
] = ntohl(mBitMap
[i
]);
102 // validate block file size
103 // Because not whole blocks are written, the size may be a
104 // little bit smaller than used blocks times blocksize,
105 // because the last block will generally not be 'whole'.
106 const PRUint32 estimatedSize
= CalcBlockFileSize();
107 if ((PRUint32
)fileSize
+ blockSize
< estimatedSize
) {
108 rv
= NS_ERROR_UNEXPECTED
;
120 /******************************************************************************
122 *****************************************************************************/
124 nsDiskCacheBlockFile::Close(PRBool flush
)
131 PRStatus err
= PR_Close(mFD
);
132 if (NS_SUCCEEDED(rv
) && (err
!= PR_SUCCESS
))
133 rv
= NS_ERROR_UNEXPECTED
;
146 /******************************************************************************
149 * Allocates 1-4 blocks, using a first fit strategy,
150 * so that no group of blocks spans a quad block boundary.
152 * Returns block number of first block allocated or -1 on failure.
154 *****************************************************************************/
156 nsDiskCacheBlockFile::AllocateBlocks(PRInt32 numBlocks
)
158 const int maxPos
= 32 - numBlocks
;
159 const PRUint32 mask
= (0x01 << numBlocks
) - 1;
160 for (int i
= 0; i
< kBitMapWords
; ++i
) {
161 PRUint32 mapWord
= ~mBitMap
[i
]; // flip bits so free bits are 1
162 if (mapWord
) { // At least one free bit
163 // Binary search for first free bit in word
165 if ((mapWord
& 0x0FFFF) == 0) { bit
|= 16; mapWord
>>= 16; }
166 if ((mapWord
& 0x000FF) == 0) { bit
|= 8; mapWord
>>= 8; }
167 if ((mapWord
& 0x0000F) == 0) { bit
|= 4; mapWord
>>= 4; }
168 if ((mapWord
& 0x00003) == 0) { bit
|= 2; mapWord
>>= 2; }
169 if ((mapWord
& 0x00001) == 0) { bit
|= 1; mapWord
>>= 1; }
170 // Find first fit for mask
171 for (; bit
<= maxPos
; ++bit
) {
172 // all bits selected by mask are 1, so free
173 if ((mask
& mapWord
) == mask
) {
174 mBitMap
[i
] |= mask
<< bit
;
175 mBitMapDirty
= PR_TRUE
;
186 /******************************************************************************
188 *****************************************************************************/
190 nsDiskCacheBlockFile::DeallocateBlocks( PRInt32 startBlock
, PRInt32 numBlocks
)
192 if (!mFD
) return NS_ERROR_NOT_AVAILABLE
;
194 if ((startBlock
< 0) || (startBlock
> kBitMapBytes
* 8 - 1) ||
195 (numBlocks
< 1) || (numBlocks
> 4))
196 return NS_ERROR_ILLEGAL_VALUE
;
198 const PRInt32 startWord
= startBlock
>> 5; // Divide by 32
199 const PRUint32 startBit
= startBlock
& 31; // Modulo by 32
201 // make sure requested deallocation doesn't span a word boundary
202 if (startBit
+ numBlocks
> 32) return NS_ERROR_UNEXPECTED
;
203 PRUint32 mask
= ((0x01 << numBlocks
) - 1) << startBit
;
205 // make sure requested deallocation is currently allocated
206 if ((mBitMap
[startWord
] & mask
) != mask
) return NS_ERROR_ABORT
;
208 mBitMap
[startWord
] ^= mask
; // flips the bits off;
209 mBitMapDirty
= PR_TRUE
;
210 // XXX rv = FlushBitMap(); // coherency vs. performance
215 /******************************************************************************
217 *****************************************************************************/
219 nsDiskCacheBlockFile::WriteBlocks( void * buffer
,
222 PRInt32
* startBlock
)
224 // presume buffer != nsnull and startBlock != nsnull
225 NS_ENSURE_TRUE(mFD
, NS_ERROR_NOT_AVAILABLE
);
227 // allocate some blocks in the cache block file
228 *startBlock
= AllocateBlocks(numBlocks
);
229 NS_ENSURE_STATE(*startBlock
>= 0);
231 // seek to block position
232 PRInt32 blockPos
= kBitMapBytes
+ *startBlock
* mBlockSize
;
233 PRInt32 filePos
= PR_Seek(mFD
, blockPos
, PR_SEEK_SET
);
234 NS_ENSURE_STATE(filePos
== blockPos
);
237 PRInt32 bytesWritten
= PR_Write(mFD
, buffer
, size
);
238 NS_ENSURE_STATE(bytesWritten
== size
);
240 // write the bit map and flush the file
241 // XXX except we would take a severe performance hit
242 // XXX rv = FlushBitMap();
247 /******************************************************************************
249 *****************************************************************************/
251 nsDiskCacheBlockFile::ReadBlocks( void * buffer
,
256 // presume buffer != nsnull and bytesRead != bytesRead
258 if (!mFD
) return NS_ERROR_NOT_AVAILABLE
;
259 nsresult rv
= VerifyAllocation(startBlock
, numBlocks
);
260 if (NS_FAILED(rv
)) return rv
;
262 // seek to block position
263 PRInt32 blockPos
= kBitMapBytes
+ startBlock
* mBlockSize
;
264 PRInt32 filePos
= PR_Seek(mFD
, blockPos
, PR_SEEK_SET
);
265 if (filePos
!= blockPos
) return NS_ERROR_UNEXPECTED
;
268 PRInt32 bytesToRead
= *bytesRead
;
269 if ((bytesToRead
<= 0) || ((PRUint32
)bytesToRead
> mBlockSize
* numBlocks
)) {
270 bytesToRead
= mBlockSize
* numBlocks
;
272 *bytesRead
= PR_Read(mFD
, buffer
, bytesToRead
);
278 /******************************************************************************
280 *****************************************************************************/
282 nsDiskCacheBlockFile::FlushBitMap()
284 if (!mBitMapDirty
) return NS_OK
;
287 PRInt32 filePos
= PR_Seek(mFD
, 0, PR_SEEK_SET
);
288 if (filePos
!= 0) return NS_ERROR_UNEXPECTED
;
290 #if defined(IS_LITTLE_ENDIAN)
291 PRUint32 bitmap
[kBitMapWords
];
292 // Copy and swap to network format
293 PRUint32
*p
= bitmap
;
294 for (int i
= 0; i
< kBitMapWords
; ++i
, ++p
)
295 *p
= htonl(mBitMap
[i
]);
297 PRUint32
*bitmap
= mBitMap
;
301 PRInt32 bytesWritten
= PR_Write(mFD
, bitmap
, kBitMapBytes
);
302 if (bytesWritten
< kBitMapBytes
) return NS_ERROR_UNEXPECTED
;
304 PRStatus err
= PR_Sync(mFD
);
305 if (err
!= PR_SUCCESS
) return NS_ERROR_UNEXPECTED
;
307 mBitMapDirty
= PR_FALSE
;
312 /******************************************************************************
316 * NS_OK if all bits are marked allocated
317 * NS_ERROR_ILLEGAL_VALUE if parameters don't obey constraints
318 * NS_ERROR_FAILURE if some or all the bits are marked unallocated
320 *****************************************************************************/
322 nsDiskCacheBlockFile::VerifyAllocation( PRInt32 startBlock
, PRInt32 numBlocks
)
324 if ((startBlock
< 0) || (startBlock
> kBitMapBytes
* 8 - 1) ||
325 (numBlocks
< 1) || (numBlocks
> 4))
326 return NS_ERROR_ILLEGAL_VALUE
;
328 const PRInt32 startWord
= startBlock
>> 5; // Divide by 32
329 const PRUint32 startBit
= startBlock
& 31; // Modulo by 32
331 // make sure requested deallocation doesn't span a word boundary
332 if (startBit
+ numBlocks
> 32) return NS_ERROR_ILLEGAL_VALUE
;
333 PRUint32 mask
= ((0x01 << numBlocks
) - 1) << startBit
;
335 // check if all specified blocks are currently allocated
336 if ((mBitMap
[startWord
] & mask
) != mask
) return NS_ERROR_FAILURE
;
342 /******************************************************************************
345 * Return size of the block file according to the bits set in mBitmap
347 *****************************************************************************/
349 nsDiskCacheBlockFile::CalcBlockFileSize()
351 // search for last byte in mBitMap with allocated bits
352 PRUint32 estimatedSize
= kBitMapBytes
;
353 PRInt32 i
= kBitMapWords
;
355 if (mBitMap
[i
]) break;
359 // binary search to find last allocated bit in byte
360 PRUint32 mapWord
= mBitMap
[i
];
361 PRUint32 lastBit
= 31;
362 if ((mapWord
& 0xFFFF0000) == 0) { lastBit
^= 16; mapWord
<<= 16; }
363 if ((mapWord
& 0xFF000000) == 0) { lastBit
^= 8; mapWord
<<= 8; }
364 if ((mapWord
& 0xF0000000) == 0) { lastBit
^= 4; mapWord
<<= 4; }
365 if ((mapWord
& 0xC0000000) == 0) { lastBit
^= 2; mapWord
<<= 2; }
366 if ((mapWord
& 0x80000000) == 0) { lastBit
^= 1; mapWord
<<= 1; }
367 estimatedSize
+= (i
* 32 + lastBit
+ 1) * mBlockSize
;
370 return estimatedSize
;