Skip to content

Instantly share code, notes, and snippets.

@nielsbot
Last active August 8, 2018 01:13
Show Gist options
  • Save nielsbot/3748250 to your computer and use it in GitHub Desktop.
Save nielsbot/3748250 to your computer and use it in GitHub Desktop.
memory mapped core graphics data consumer for writing PDFs
#import <Foundation/Foundation.h>
#import <CoreGraphics/CoreGraphics.h>
@interface MemoryMappedDataConsumer : NSObject
@property ( nonatomic, readonly ) size_t size ;
@property ( nonatomic, readonly ) size_t capacity ;
@property ( nonatomic, readonly ) CGDataConsumerRef CGDataConsumer ;
@property ( nonatomic, readonly, copy ) NSURL * url ;
-(id)initWithURL:(NSURL*)url ;
@end
#import "MemoryMappedDataConsumer.h"
#import <sys/mman.h>
@interface MemoryMappedDataConsumer ()
// private
@property ( nonatomic ) size_t size ;
@property ( nonatomic ) size_t capacity ;
@property ( nonatomic, strong, readonly ) NSFileHandle * fileHandle ;
@property ( nonatomic ) void * buffer ;
@property ( nonatomic, copy ) NSURL * url ;
-(size_t)putBytes:(const void *)bytes count:(size_t)count ;
@end
@implementation MemoryMappedDataConsumer
@synthesize CGDataConsumer = _CGDataConsumer ;
@synthesize fileHandle = _fileHandle ;
static size_t DataConsumerPutBytesCallback( MemoryMappedDataConsumer * consumer, const void *buffer, size_t count)
{
return [ consumer putBytes:buffer count:count ] ;
}
static void DataConsumerReleaseConsumer( MemoryMappedDataConsumer * consumer )
{
CFRelease((__bridge CFTypeRef)consumer);
}
-(id)initWithURL:(NSURL*)url
{
if (( self = [ super init ] ))
{
self.url = url ;
}
return self ;
}
-(CGDataConsumerRef)CGDataConsumer
{
if ( !_CGDataConsumer )
{
_CGDataConsumer = CGDataConsumerCreate( (__bridge_retained void *)self, &(CGDataConsumerCallbacks){
.putBytes = (CGDataConsumerPutBytesCallback)DataConsumerPutBytesCallback,
.releaseConsumer = (CGDataConsumerReleaseInfoCallback)DataConsumerReleaseConsumer
});
}
return _CGDataConsumer ;
}
-(size_t)putBytes:(const void *)bytes count:(size_t)count
{
size_t newSize = self.size + count ;
if ( newSize > self.capacity )
{
size_t newCapacity = self.capacity ;
newCapacity += newCapacity >> 1 ;
newCapacity = round_page( MAX( newCapacity, MAX( 4 * getpagesize(), count ) ) ) ;
[ self.fileHandle truncateFileAtOffset:newCapacity ] ;
if ( self.buffer && self.capacity )
{
munmap( self.buffer, self.capacity ) ;
}
void * newBuffer = mmap( self.buffer
, newCapacity
, PROT_WRITE | PROT_READ
, MAP_SHARED
, self.fileHandle.fileDescriptor
, 0 ) ;
if ( !newBuffer || newBuffer == (void*)~0l ) // mmap returns (-1) on error
{
// couldn't expand buffer, bail:
return 0 ;
}
self.buffer = newBuffer ;
self.capacity = newCapacity ;
}
bcopy( bytes, self.buffer + self.size, count ) ;
self.size = newSize ;
return count ;
}
-(NSFileHandle *)fileHandle
{
if ( !_fileHandle )
{
NSError * error = nil ;
NSFileManager * fileManager = [ NSFileManager defaultManager ] ;
if ( ![ fileManager fileExistsAtPath:self.url.path ] )
{
BOOL success = [ fileManager createFileAtPath:self.url.path contents:nil attributes:nil ] ;
if ( !success )
{
@throw @"Couldn't create backing store file" ;
}
}
NSFileHandle * fileHandle = [ NSFileHandle fileHandleForUpdatingURL:self.url error:&error ] ;
if ( !fileHandle )
{
if ( error ) { @throw error ; }
}
_fileHandle = fileHandle ;
}
return _fileHandle ;
}
@end
// mediaBox and auxInfoDict can be NULL
CGContextRef CreateMemoryMappedPDFContext( NSURL * fileURL, const CGRect * mediaBox, CFDictionaryRef auxInfoDict )
{
MemoryMappedDataConsumer * consumer = [ [ MemoryMappedDataConsumer alloc ] initWithURL:fileURL ] ;
CGContextRef c = CGPDFContextCreate( [ consumer CGDataConsumer ], mediaBox, auxInfoDict ) ;
return c ;
}
@nielsbot
Copy link
Author

I can check

@nielsbot
Copy link
Author

I added it to a project and it works fine...

@nielsbot
Copy link
Author

I added an import for sys/mman.h

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment