6
MAR
2011

Memory Mapped Files

One of the difficult parts of writing games is memory management. This is especially true for games that run on mobile devices. One useful "trick" is to map files into the virtual memory space, instead of actually loading the file.

When you map a file into the virtual memory space, you get a memory pointer to the start of the file and you can use that pointer as if the file had been loaded straight into memory. When you access different parts of the file those parts will be loaded into physical memory on demand.

When the system is running out of memory, it will throw out the parts of memory mapped files that have been loaded into the physical memory before it starts complaining about being short on memory. When the app tries to access the unloaded part of the mapped memory again, it will be loaded back into physical memory.

Obviously, accessing the mapped memory is not as fast as just accessing physical memory when you access a part of the file that is not currently in physical memory, but it's almost like free extra memory for your app, so for code that does not need to be super fast it is sufficient (Note that this is only true for files that are mapped into memory as read-only).

On the iPhone, you can memory map files in at least two ways. One is to use the mmap() system call and the other is to use NSData with the +(id)dataWithContentsOfMappedFile: initializer method.

For my game engine, I want to use Objective-C objects for memory mapping files, but as there is no documentation on exactly how NSData is implemented, I have chosen to implement my own class for memory mapped files that simply wraps the mmap() system call. When it comes to memory management, it's usually a good idea to be somewhat paranoid and make sure you know exactly what is going on behind the scenes.

As always, you can do whatever you want with code that I put on this blog, but I take no responsibility for it or what you do with it. :-)

Header file (MemoryMappedFile.h):

#import <Foundation/Foundation.h>
 
 
@interface MemoryMappedFile : NSObject
{
@private
    NSString *path;
    void *baseAddress;
    NSUInteger size;
}
 
// The path of the file to map into memory.
@property (nonatomic, readonly) NSString *path;
 
// The memory address where the file is mapped.
// NULL when the file is not mapped into memory.
@property (nonatomic, readonly) void *baseAddress;
 
// Total size of the file.
@property (nonatomic, readonly) NSUInteger size;
 
// Returns YES when the file is mapped into memory.
@property (nonatomic, readonly) BOOL isMapped;
 
// Prepares to map the specified file, but does not
// actually map the file into memory.
- (id)initWithPath:(NSString *)pathToFile;
 
// Maps the file into memory.
// Returns pointer to start of file or NULL if unsuccessful.
- (void *)map;
 
// Unmaps the file from memory.
// The pointer returned by map is no longer valid.
- (void)unmap;
 
@end

Implementation file (MemoryMappedFile.m):

#import "MemoryMappedFile.h"
#import <fcntl.h>
#import <unistd.h>
#import <sys/stat.h>
#import <sys/mman.h>
 
@implementation MemoryMappedFile
 
@synthesize path;
@synthesize baseAddress;
@synthesize size;
 
- (id)initWithPath:(NSString *)pathToFile
{
    if (self = [super init])
    {
        path = [pathToFile copyWithZone:nil];
    }
 
    return self;
}
 
- (void)dealloc
{
    if ([self isMapped])
    {
        [self unmap];
    }
 
    [path release];
    [super dealloc];
}
 
- (void *)map
{
    if ([self isMapped])
    {
        return baseAddress;
    }
 
    // This will be released when "path" is released.
    const char *cPath = [path
            cStringUsingEncoding:NSISOLatin1StringEncoding];
 
    // The file must be opened so we can pass 
    // the file descriptor to mmap().
    int file = open(cPath, O_RDONLY);
    if (file == -1)
    {
        perror("open");
        return NULL;
    }
 
    // Get info about file, we need the file size.
    struct stat buffer;
    if (fstat(file, &buffer) == -1)
    {
        perror("fstat");
        close(file);
        return NULL;
    }
 
    // Map the file as read only pages.
    baseAddress = mmap(NULL, buffer.st_size, PROT_READ,
                        MAP_SHARED, file, 0);
    if (baseAddress == MAP_FAILED)
    {
        perror("mmap");
        close(file);
        return NULL;
    }
 
    // Store the size, we need it when we unmap the file.
    size = (NSUInteger)buffer.st_size;
 
    // It's ok to close() after mmap().
    if (close(file) == -1)
    {
        perror("close");
        [self unmap];
        return NULL;
    }
 
    return baseAddress;
}
 
- (void)unmap
{
    // Only unmap the file if it is actually mapped.
    if ([self isMapped])
    {
        if (munmap(baseAddress, size) == -1)
        {
            perror("munmap");
            // There's not much we can do if munmap() fails.
        }
 
        baseAddress = NULL;
        size = 0;
    }
}
 
- (BOOL)isMapped
{
    return baseAddress != NULL;
}
 
@end


14
FEB
2010

My Thoughts on the iPad

It seems like a lot of people were disappointed or simply underwhelmed by the iPad. I think they are missing the point. The iPad isn't really about a hardware revolution; it's about refinement of the already revolutionary multi-touch user interface and App Store software distribution model.

The App Store allows the individual and small companies to release software to the public without investing money and time in any kind of payment and distribution infrastructure. The fact that both EA and a 15 year old can release a game, and get paid for it, in the exact same way, levels the playing field for the first time.

For the user, the App Store provides a comprehensive and complete catalog of software. If it exists, it's in the App Store. If it's in the App Store, it's safe to download. No viruses, no hassle, no dependencies. The App Store simply takes software distribution into the 21st century.

Many people have gripes with the closedness of the App Store. I can understand that perspective, but I think that the closedness is actually a feature and not a problem. The App Store control minimizes the effort the user has to put into his/her software purchases, making the purchase more likely.

There is a fine line between control in the interest of the user and in the interest of competition stiflement, but so far I think Apple has succeeded in keeping on the right side of the line, albeit with a few notable transgressions.

The multi-touch user interface made mainstream by the iPhone and iPad Touch devices is now a proven type of user interface. Millions of people use multi-touch enabled devices every day. It is time to leverage that familiarity and create software that is almost as natural to use as a sheet of paper. Personally, I'm hoping that the iPad and its inevitable successors will eventually make the mouse-and-keyboard combo fade into obscurity.


About This Site

Hello, my name is Martin Johannesson and this is my home on the web. I live in Stockholm, Sweden, where I work as a software engineer at a software company.

Ever since I was a kid and discovered the art of programming on my C64, I've been tinkering with my own little software projects and experiments. This site is one such experiment.
more...

Recent Entries RSS Feed

Tags

Amiga blog C Cocoa game GLGX GLSL iOS iPad iPhone Java jQuery Mac Mac OS X Objective-C OpenAL OpenGL Programming REBOL Shaders Vertex Shader web

Blog Archive

2011: 01 02 03 04 05 06 07 08 09 10 11 12
2010: 01 02 03 04 05 06 07 08 09 10 11 12
2009: 01 02 03 04 05 06 07 08 09 10 11 12

Random Images Load new images

loading
loading
loading
loading
loading
loading
loading
loading
loading
loading
loading
loading