file-cache.c revision 4498761e9ee18c96a19ce3029258a6390efeb092
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen/* Copyright (c) 2004 Timo Sirainen */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen#include "lib.h"
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen#include "buffer.h"
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen#include "mmap-util.h"
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen#include "file-cache.h"
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen#include <sys/stat.h>
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainenstruct file_cache {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen int fd;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen buffer_t *page_bitmask;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen void *mmap_base;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen size_t mmap_length;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen size_t read_highwater;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen};
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainenstruct file_cache *file_cache_new(int fd)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen{
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen struct file_cache *cache;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen cache = i_new(struct file_cache, 1);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen cache->fd = fd;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen cache->page_bitmask = buffer_create_dynamic(default_pool, 128);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen return cache;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen}
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainenvoid file_cache_free(struct file_cache *cache)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen{
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (cache->mmap_base != NULL) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (munmap_anon(cache->mmap_base, cache->mmap_length) < 0)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen i_error("munmap_anon() failed: %m");
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen buffer_free(cache->page_bitmask);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen i_free(cache);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen}
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainenvoid file_cache_set_fd(struct file_cache *cache, int fd)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen{
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen cache->fd = fd;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen file_cache_invalidate(cache, 0, cache->mmap_length);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen}
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainenssize_t file_cache_read(struct file_cache *cache, uoff_t offset, size_t size)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen{
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen size_t page_size = mmap_get_page_size();
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen size_t poffset, psize, mmap_needed, dest_offset, dest_size;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen unsigned char *bits, *dest;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen ssize_t ret;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
f83a33d8ddb275984cd89835ee8afe0ff584fe91Timo Sirainen if (size > SSIZE_T_MAX) {
f83a33d8ddb275984cd89835ee8afe0ff584fe91Timo Sirainen /* make sure our calculations won't overflow. most likely
f83a33d8ddb275984cd89835ee8afe0ff584fe91Timo Sirainen we'll be reading less data, but allow it anyway so caller
f83a33d8ddb275984cd89835ee8afe0ff584fe91Timo Sirainen doesn't have to deal with any extra checks. */
f83a33d8ddb275984cd89835ee8afe0ff584fe91Timo Sirainen size = SSIZE_T_MAX;
f83a33d8ddb275984cd89835ee8afe0ff584fe91Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (offset + size > cache->mmap_length &&
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen offset + size - cache->mmap_length > 1024*1024) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* growing more than a megabyte, make sure that the
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen file is large enough so we don't allocate memory
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen more than needed */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen struct stat st;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (fstat(cache->fd, &st) < 0) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen i_error("fstat(file_cache) failed: %m");
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen return -1;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (offset + size > (uoff_t)st.st_size) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (offset >= (uoff_t)st.st_size)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen return 0;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen size = (uoff_t)st.st_size - offset;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen poffset = offset / page_size;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen psize = (offset + size + page_size-1) / page_size - poffset;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen i_assert(psize > 0);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen mmap_needed = (poffset + psize) * page_size;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (mmap_needed > cache->mmap_length) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* grow mmaping */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (cache->mmap_base == NULL) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen cache->mmap_base = mmap_anon(mmap_needed);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (cache->mmap_base == MAP_FAILED) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen i_error("mmap_anon(%"PRIuSIZE_T") failed: %m",
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen mmap_needed);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen return -1;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen } else {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen cache->mmap_base = mremap_anon(cache->mmap_base,
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen cache->mmap_length,
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen mmap_needed,
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen MREMAP_MAYMOVE);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (cache->mmap_base == MAP_FAILED) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen i_error("mremap_anon(%"PRIuSIZE_T") failed: %m",
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen mmap_needed);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen return -1;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen cache->mmap_length = mmap_needed;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
77717f1e9e8442fd2cdbca87cbc45c03d0b5fcaeTimo Sirainen bits = buffer_get_space_unsafe(cache->page_bitmask, 0,
77717f1e9e8442fd2cdbca87cbc45c03d0b5fcaeTimo Sirainen poffset / CHAR_BIT +
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen (psize + CHAR_BIT - 1) / CHAR_BIT);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen dest_offset = poffset * page_size;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen dest = PTR_OFFSET(cache->mmap_base, dest_offset);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen dest_size = page_size;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen while (psize > 0) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (bits[poffset / CHAR_BIT] & (1 << (poffset % CHAR_BIT))) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* page is already in cache */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen psize--; poffset++;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen dest += page_size;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen dest_offset += page_size;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen continue;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen ret = pread(cache->fd, dest, dest_size, dest_offset);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (ret <= 0) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (ret < 0)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen return -1;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* EOF */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* FIXME: we should mark the last block cached and
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen invalidate it only when trying to read past the
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen file */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen return dest_offset <= offset ? 0 :
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen dest_offset - offset < size ?
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen dest_offset - offset : size;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen dest += ret;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen dest_offset += ret;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (cache->read_highwater < dest_offset)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen cache->read_highwater = dest_offset;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if ((size_t)ret != dest_size) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* partial read - probably EOF but make sure. */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen dest_size -= ret;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen continue;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen bits[poffset / CHAR_BIT] |= 1 << (poffset % CHAR_BIT);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen dest_size = page_size;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen psize--; poffset++;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen return size;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen}
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainenconst void *file_cache_get_map(struct file_cache *cache, size_t *size_r)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen{
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen *size_r = cache->read_highwater;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen return cache->mmap_base;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen}
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainenvoid file_cache_write(struct file_cache *cache, const void *data, size_t size,
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen uoff_t offset)
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen{
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen size_t page_size = mmap_get_page_size();
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen size_t max_size;
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen unsigned char *bits;
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen unsigned int first_page, last_page;
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen if (offset >= cache->mmap_length)
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen return;
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen max_size = cache->mmap_length - offset;
4498761e9ee18c96a19ce3029258a6390efeb092Timo Sirainen if (max_size > size)
4498761e9ee18c96a19ce3029258a6390efeb092Timo Sirainen size = max_size;
4498761e9ee18c96a19ce3029258a6390efeb092Timo Sirainen memcpy(PTR_OFFSET(cache->mmap_base, offset), data, size);
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen /* mark fully written pages cached */
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen if (size >= page_size) {
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen first_page = offset / page_size;
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen last_page = (offset + size) / page_size;
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen if ((offset % page_size) != 0)
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen first_page++;
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen bits = buffer_get_space_unsafe(cache->page_bitmask, 0,
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen last_page / CHAR_BIT + 1);
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen for (; first_page < last_page; first_page++) {
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen bits[first_page / CHAR_BIT] |=
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen 1 << (first_page % CHAR_BIT);
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen }
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen }
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen}
b22f3db16d33b8e509b7331e46108c313e7f3b94Timo Sirainen
74eff344f67ca582eec8c38dfdc6f5f6faa21fd8Timo Sirainenvoid file_cache_invalidate(struct file_cache *cache, uoff_t offset, uoff_t size)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen{
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen size_t page_size = mmap_get_page_size();
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen unsigned char *bits, mask;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen unsigned int i;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (offset >= cache->read_highwater)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen return;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (size > cache->read_highwater - offset)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen size = cache->read_highwater - offset;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen size = (offset + size + page_size-1) / page_size;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen offset /= page_size;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen size -= offset;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
72767afd6cd99439a4d93fcba1621bfcac5e791cTimo Sirainen if (size != 1) {
72767afd6cd99439a4d93fcba1621bfcac5e791cTimo Sirainen /* tell operating system that we don't need the memory anymore
72767afd6cd99439a4d93fcba1621bfcac5e791cTimo Sirainen and it may free it. don't bother to do it for single pages,
72767afd6cd99439a4d93fcba1621bfcac5e791cTimo Sirainen there's a good chance that they get re-read back
72767afd6cd99439a4d93fcba1621bfcac5e791cTimo Sirainen immediately. */
72767afd6cd99439a4d93fcba1621bfcac5e791cTimo Sirainen (void)madvise(PTR_OFFSET(cache->mmap_base, offset * page_size),
72767afd6cd99439a4d93fcba1621bfcac5e791cTimo Sirainen size * page_size, MADV_DONTNEED);
72767afd6cd99439a4d93fcba1621bfcac5e791cTimo Sirainen }
72767afd6cd99439a4d93fcba1621bfcac5e791cTimo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen bits = buffer_get_space_unsafe(cache->page_bitmask, offset / CHAR_BIT,
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen (size + CHAR_BIT - 1) / CHAR_BIT);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* set the first byte */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen for (i = offset % CHAR_BIT, mask = 0; i < CHAR_BIT && size > 0; i++) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen mask |= 1 << i;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen size--;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen *bits++ &= ~mask;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* set the middle bytes */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen memset(bits, 0, size / CHAR_BIT);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen bits += size / CHAR_BIT;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen size %= CHAR_BIT;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* set the last byte */
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (size > 0) {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen mask = 0;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen for (i = 0, mask = 0; i < size; i++)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen mask |= 1 << i;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen *bits &= ~mask;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen}