int main(int argc, const char *argv[])
{
    CmdArgParser parser(argc, argv);
    parser.setHeader("File Allocator Tool version "
                     EPICS_VERSION_STRING
                     ", built " __DATE__ ", " __TIME__ "\n\n");
    parser.setArgumentsInfo("<file> <reserved bytes>");
    CmdArgFlag   be_verbose (parser, "verbose", "Verbose mode");
    
    if (! parser.parse())
        return -1;
    if (parser.getArguments().size() != 2)
    {
        parser.usage();
        return -1;
    }
    verbose = be_verbose;

    stdString file_name = parser.getArgument(0);
    long reserved = atol(parser.getArgument(1));
    
    if (verbose)
        printf("Opening '%s'\n", file_name.c_str());
    FILE *f = fopen(file_name.c_str(), "rb");
    if (!f)
    {
        fprintf(stderr, "Cannot open '%s'\n",
                file_name.c_str());
        return -1;
    }
    
    if (verbose)
        printf("Attaching read-only file allocator, %lu reserved bytes\n",
               reserved);
    FileAllocator fa;
    fa.attach(f, reserved, false);
    fa.dump(verbose ? 10 : 0);
    fa.detach();
    fclose(f);
    
    return 0;
}
TEST_CASE file_allocator_create_new_file()
{
    AutoFilePtr f("test/file_allocator.dat", "w+b");
    TEST_MSG(f, "Create new file");

    FileAllocator fa;
    long o1, o2, o3, o4, o5, o6, o7;
    TEST_MSG(fa.attach(f, 1000, true) == true, "attach and initialize");
    TEST_MSG(fa.dump(), "Consistency Check\n");

    TEST_MSG((o1=fa.allocate(1000)), "allocate 1000");
    printf("Got offset %ld\n", o1);
    TEST_MSG(fa.dump(0), "Consistency Check\n");

    TEST_MSG((o2=fa.allocate(2000)), "allocate 2000");
    printf("Got offset %ld\n", o2);
    TEST_MSG(fa.dump(0), "Consistency Check\n");

    TEST_MSG((o3=fa.allocate(3000)), "allocate 3000");
    printf("Got offset %ld\n", o3);
    TEST_MSG(fa.dump(0), "Consistency Check\n");

    TEST_MSG((o4=fa.allocate(4000)), "allocate 4000");
    printf("Got offset %ld\n", o4);
    TEST_MSG(fa.dump(0), "Consistency Check\n");

    TEST_MSG((o5=fa.allocate(5000)), "allocate 5000");
    printf("Got offset %ld\n", o5);
    TEST_MSG(fa.dump(0), "Consistency Check\n");

    TEST_MSG((o6=fa.allocate(6000)), "allocate 6000");
    printf("Got offset %ld\n", o6);
    TEST_MSG(fa.dump(0), "Consistency Check\n");

    TEST_MSG((o7=fa.allocate(7000)), "allocate 7000");
    printf("Got offset %ld\n", o7);
    TEST_MSG(fa.dump(), "Consistency Check\n");

    fa.free(o1);
    TEST_MSG(fa.dump(), "Consistency Check after free 1000 (first block)\n");

    fa.free(o7);
    TEST_MSG(fa.dump(), "Consistency Check after free 7000 (last block)\n");

    fa.free(o5);
    TEST_MSG(fa.dump(), "Consistency Check after free 5000 (embedded block)\n");

    fa.free(o4);
    TEST_MSG(fa.dump(), "Consistency Check after free 4000 (adjacent block, causing merge)\n");

    fa.free(o2);
    TEST_MSG(fa.dump(), "Consistency Check after free 2000 (next to free first block)\n");

    fa.free(o3);
    TEST_MSG(fa.dump(), "Consistency Check after free 3000 (next to free first block)\n");

    TEST_MSG((o1=fa.allocate(4000)), "allocate 4000 (from first free block)");
    printf("Got offset %ld\n", o1);
    TEST_MSG(fa.dump(), "Consistency Check\n");

    TEST_MSG((o2=fa.allocate(500)), "allocate 500 (from first free block)");
    printf("Got offset %ld\n", o2);
    TEST_MSG(fa.dump(), "Consistency Check\n");

    TEST_MSG((o3=fa.allocate(1500)), "allocate 1500 (from first free block)");
    printf("Got offset %ld\n", o3);
    TEST_MSG(fa.dump(), "Consistency Check\n");

    fa.detach();
    TEST_OK;
}
TEST_CASE file_allocator_open_existing()
{
    AutoFilePtr f("test/file_allocator.dat", "r+b");
    TEST_MSG(f, "Re-opened file");

    FileAllocator fa;
    TEST_MSG(fa.attach(f, 1000, false) == false, "re-attach");
    TEST_MSG(fa.dump(), "Consistency Check");

    puts("-- upping file size increment to 50000 --");
    FileAllocator::file_size_increment = 50000;
    long o1, o2, o3;
    TEST_MSG((o1=fa.allocate(10000)), "allocate 10000");
    printf("Got offset %ld\n", o1);
    TEST_MSG(fa.dump(), "Consistency Check\n");

    TEST_MSG((o1=fa.allocate(10000)), "allocate 10000");
    printf("Got offset %ld\n", o1);
    TEST_MSG(fa.dump(), "Consistency Check\n");

    TEST_MSG((o1=fa.allocate(10000)), "allocate 10000");
    printf("Got offset %ld\n", o1);
    TEST_MSG(fa.dump(), "Consistency Check\n");

    TEST_MSG((o2=fa.allocate(5000)), "allocate 5000");
    printf("Got offset %ld\n", o2);
    TEST_MSG(fa.dump(), "Consistency Check\n");

    TEST_MSG((o3=fa.allocate(5000)), "allocate 5000");
    printf("Got offset %ld\n", o3);
    TEST_MSG(fa.dump(), "Consistency Check\n");
    {
        AutoFilePtr file("test/file_allocator.out", "wt");
        TEST_MSG(fa.dump(1, file), "Consistency Check\n");
    }
    TEST_FILEDIFF("test/file_allocator.out", "test/file_allocator.OK");

    TEST("Performing some random tests");
    long o[10];
    memset(o, 0, sizeof(o));
    int i;
    for (i=0; i<500; ++i)
    {   // Randomly free an existing block
        if (o[i%10] && rand()>RAND_MAX/2)
        {
            fa.free(o[i%10]);
            o[i%10] = 0;
        }
        else
        {   // allocate another one
            o[i%10] = fa.allocate(rand() & 0xFFFF);
            if (o[i%10] == 0)
                printf("Alloc failed\n");
        }
        if (! fa.dump(0))
            FAIL("Consistency check failed");
    }
    TEST("If you didn't see any error message, that went fine.");
    fa.detach();

    TEST_OK;
}