root/src/tests/system/kernel/cache/file_map_test.cpp
/*
 * Copyright 2009, Axel Dörfler, axeld@pinc-software.de.
 * Distributed under the terms of the MIT License.
 */


#include <stdio.h>
#include <string.h>

#include <fs_cache.h>
#include <fs_interface.h>

#include <file_cache.h>


#define MAX_VECS        32


class Map {
public:
        Map(const char* name, off_t size);
        ~Map();

        void SetTo(const char* name, off_t size);

        Map& Add(off_t offset, off_t length, off_t diskOffset);
        Map& Clear();
        Map& SetSize(off_t size);
        void Invalidate(off_t start, off_t size);
        void SetMode(uint32 mode);
        void Test();

        status_t GetFileMap(off_t offset, off_t length, file_io_vec* vecs,
                size_t* _vecCount);

private:
        void _Error(const char* format, ...);
        void _Verbose(const char* format, ...);
        int32 _IndexFor(off_t offset);

        const char*     fName;
        uint32          fTest;
        void*           fMap;
        off_t           fOffsets[MAX_VECS];
        file_io_vec     fVecs[MAX_VECS];
        file_io_vec     fTestVecs[MAX_VECS];
        uint32          fCount;
        uint32          fTestCount;
        off_t           fTestOffset;
        off_t           fTestLength;
        off_t           fSize;
};


static bool sVerbose;


Map::Map(const char* name, off_t size)
        :
        fName(NULL),
        fMap(NULL),
        fCount(0),
        fSize(0)
{
        SetTo(name, size);
}


Map::~Map()
{
        file_map_delete(fMap);
}


void
Map::SetTo(const char* name, off_t size)
{
        file_map_delete(fMap);

        fMap = file_map_create((dev_t)this, 0, size);
        if (fMap == NULL)
                _Error("Creating file map failed.");

        fName = name;
        fSize = size;
        fCount = 0;
        fTest = 0;

        printf("Running %s\n", fName);
}


Map&
Map::Add(off_t offset, off_t length, off_t diskOffset)
{
        _Verbose("  Add(): offset %lld, length %lld, diskOffset %lld", offset,
                length, diskOffset);

        if (fCount < MAX_VECS) {
                fOffsets[fCount] = offset;
                fVecs[fCount].offset = diskOffset;
                fVecs[fCount].length = length;
                fCount++;
        }

        return *this;
}


Map&
Map::Clear()
{
        _Verbose("  Clear()");
        fCount = 0;
        return *this;
}


Map&
Map::SetSize(off_t size)
{
        _Verbose("  SetSize(): size %lld", size);
        file_map_set_size(fMap, size);
        fSize = size;
        return *this;
}


void
Map::Invalidate(off_t start, off_t size)
{
        _Verbose("  Invalidate(): start %lld, size %lld", start, size);
        file_map_invalidate(fMap, start, size);
}


void
Map::SetMode(uint32 mode)
{
        file_map_set_mode(fMap, mode);
}


void
Map::Test()
{
        printf("  Test %lu\n", ++fTest);

        for (off_t offset = 0; offset < fSize; offset += 256) {
                fTestOffset = offset;
                fTestLength = 256;
                fTestCount = MAX_VECS;
                status_t status = file_map_translate(fMap, offset, fTestLength,
                        fTestVecs, &fTestCount, 0);
                if (status != B_OK) {
                        _Error("file_map_translate(offset %lld) failed: %s", offset,
                                strerror(status));
                }

                int32 index = _IndexFor(offset);
                if (index < 0)
                        _Error("index for offset %lld not found!", offset);
                
                off_t diff = offset - fOffsets[index];

                if (fTestVecs[0].length > fSize - diff) {
                        _Error("size too large: got %lld, size is %lld",
                                fTestVecs[0].length, fSize);
                }
                if (fTestVecs[0].offset != fVecs[index].offset + diff) {
                        _Error("offset mismatch: got %lld, should be %lld",
                                fTestVecs[0].offset, fVecs[index].offset + diff);
                }
        }

        fTestCount = 0;
}


status_t
Map::GetFileMap(off_t offset, off_t length, file_io_vec* vecs,
        size_t* _vecCount)
{
        int32 index = _IndexFor(offset);
        if (index < 0)
                _Error("No vec for offset %lld\n", offset);

        _Verbose("    GetFileMap(): offset: %lld, length: %lld, index %ld", offset,
                length, index);

        uint32 count = 0;

        while (length > 0) {
                if (count >= *_vecCount)
                        return B_BUFFER_OVERFLOW;
                if ((uint32)index >= fCount)
                        break;

                off_t diff = offset - fOffsets[index];
                vecs[count].offset = fVecs[index].offset + diff;
                vecs[count].length = fVecs[index].length - diff;
                _Verbose("      [%lu] offset %lld, length %lld", count,
                        vecs[count].offset, vecs[count].length);

                length -= vecs[count].length;
                offset += vecs[count].length;
                index++;
                count++;
        }

        *_vecCount = count;
        return B_OK;
}


void
Map::_Error(const char* format, ...)
{
        va_list args;
        va_start(args, format);

        fprintf(stderr, "ERROR %s: ", fName);
        vfprintf(stderr, format, args);
        fputc('\n', stderr);

        va_end(args);
        
        fprintf(stderr, "  size %lld\n", fSize);

        for (uint32 i = 0; i < fCount; i++) {
                fprintf(stderr, "  [%lu] offset %lld, length %lld, disk offset %lld\n",
                        i, fOffsets[i], fVecs[i].length, fVecs[i].offset);
        }

        if (fTestCount > 0) {
                fprintf(stderr, "got for offset %lld, length %lld:\n",
                        fTestOffset, fTestLength);
        }

        for (uint32 i = 0; i < fTestCount; i++) {
                fprintf(stderr, "  [%lu] offset %lld, length %lld\n",
                        i, fTestVecs[i].offset, fTestVecs[i].length);
        }

        fflush(stderr);

        debugger("file map error");
        exit(1);
}


void
Map::_Verbose(const char* format, ...)
{
        if (!sVerbose)
                return;

        va_list args;
        va_start(args, format);

        vprintf(format, args);
        putchar('\n');

        va_end(args);
        fflush(stdout);
}


int32
Map::_IndexFor(off_t offset)
{
        for (uint32 i = 0; i < fCount; i++) {
                if (offset >= fOffsets[i] && offset < fOffsets[i] + fVecs[i].length)
                        return i;
        }

        return -1;
}


//      #pragma mark - VFS support functions


extern "C" status_t
vfs_get_file_map(struct vnode* vnode, off_t offset, uint32 length,
        file_io_vec* vecs, size_t* _vecCount)
{
        Map* map = (Map*)vnode;
        return map->GetFileMap(offset, length, vecs, _vecCount);
}


extern "C" status_t
vfs_lookup_vnode(dev_t mountID, ino_t vnodeID, struct vnode** _vnode)
{
        *_vnode = (struct vnode*)mountID;
        return B_OK;
}


//      #pragma mark -


int
main(int argc, char** argv)
{
        file_map_init();
        sVerbose = true;

        Map map("shrink1", 4096);
        map.Add(0, 1024, 4096).Add(1024, 3072, 8192);
        map.Test();
        map.SetSize(0).Clear();
        map.Test();
        map.Add(0, 8192, 1000).SetSize(7777);
        map.Test();

        map.SetTo("shrink2", 8888);
        map.Add(0, 10000, 3330000);
        map.Test();
        map.SetSize(0);
        map.SetSize(4444);
        map.Clear();
        map.Add(0, 5000, 2220000);
        map.Test();

        map.SetTo("shrink3", 256000);
        map.Add(0, 98304, 188074464);
        map.Add(98304, 38912, 189057024);
        map.Add(137216, 118784, 189177856);
        map.Test();
        map.SetSize(0);
        map.Test();

        return 0;
}