/******************************************************************* * * ftsys.c * * Unix-specific system operations. * * Copyright 1996-1998 by * David Turner, Robert Wilhelm, and Werner Lemberg. * * This file is part of the FreeType project, and may only be used * modified and distributed under the terms of the FreeType project * license, LICENSE.TXT. By continuing to use, modify, or distribute * this file you indicate that you have read the license and * understand and accept it fully. * * * This implementation of the 'ftsys' component uses memory-mapped * files, as well as the ANSI malloc/free functions.. * * IMPORTANT NOTE : * * Porters, read carefully the comments in ftsys.h before trying * to port this file to your system. It contains many essential * remarks, and will ease your work greatly.. * ******************************************************************/ #include "ftsys.h" #include "ftobjs.h" #include "ftstream.h" #include "ftdebug.h" /* Memory-mapping includes and definitions.. */ /* */ #ifdef HAVE_UNISTD_H #include #endif #include #ifndef MAP_FILE #define MAP_FILE 0x00 #endif /* * The prototype for munmap() is not provided on SunOS. This needs to * have a check added later to see if the GNU C library is being used. * If so, then this prototype is not needed. */ #if defined(__sun__) && !defined(SVR4) && !defined(__SVR4) extern int munmap( caddr_t addr, int len ); #endif #include #ifdef HAVE_FCNTL_H #include #endif /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ /* parameter of the PTRACE and PERROR macros, used to print/log */ /* messages during execution.. */ /* */ #undef FT_COMPONENT #define FT_COMPONENT trace_io /* To ease porting, we use the macro SYS_STREAM to name the system-specific */ /* stream type. For example, it is a "FILE*" with the ANSI libc, it will be */ /* a file descriptor, i.e. an integer, when using the Unix system api, etc. */ /* we identify each memory-mapped file through its address in memory */ /* hence the following macro definition.. */ #define SYS_STREAM void* /**************************************************************************/ /* */ /* I/O ACCESS AND MANAGEMENT */ /* */ /* We only define the "ANSI" resource class in this class. It is */ /* disk-based, and provides a MRU cache, in order to only keep the file */ /* descriptors of the 10 most recently used resource files. */ /* */ /* it simply contains two lists. One contains the "cached" resources */ /* with a valid FILE* pointer, the second contains all other "flushed" */ /* resource objects. */ /* */ /* The FT_MMapFile class derives from FT_ResourceRec - description : */ /* */ /* FT_AnsiFileRec */ /* */ /* */ /* */ /* root :: */ /* the root resource class fields. */ /* */ /* pathname :: */ /* the file's pathname. Needed because we open and close font */ /* resources dynamically in order to limit the number of */ /* concurrently active mappings (this saves kernel resources). */ /* */ /* file_size :: */ /* the size in bytes of the resource. This field should be set to */ /* -1 until the resource is first opened.. */ /* */ #include typedef struct FT_MMapFileRec_ { FT_ResourceRec root; const char* pathname; FT_Long file_size; /* file size in bytes */ } FT_MMapFileRec, *FT_MMapFile; /* We use the macro STREAM_Name as a convenience to return a given */ /* ANSI resource's pathname. Its "stream" argument is a FT_Resource */ /* which is typecasted to the FT_AnsiFile class */ #define STREAM_Name(stream) ((FT_MMapFile)stream->resource)->pathname /* We use the macro STREAM_File as a convenience to extract the */ /* system-specific stream handle from a given FreeType stream object */ #define STREAM_File(stream) ((void*)stream->stream_id.pointer) /***************************************************************************/ /* */ /* MMapFile_Open */ /* */ /* */ /* This function is used to open a system-stream for a given resource. */ /* */ /* Note that it must update the target FreeType stream object with the */ /* system-stream handle and the resource's size. */ /* */ /* Also, the 'stream->base' field must be set to NULL for disk-based */ /* resource, and to the address in memory of the resource's first byte */ /* for a memory-based one. */ /* */ /* */ /* resource :: the source resource */ /* stream :: the target stream object */ /* */ /* */ /* Error code */ /* */ /* */ /* This function simply opens and maps the resource's file pathname */ /* */ /* The stream object IS NOT CREATED by this function, but by its caller. */ /* */ /***************************************************************************/ static FT_Error MMapFile_Open( FT_MMapFile resource, FT_Stream stream ) { int file; struct stat stat_buf; /* open the file */ file = open( resource->pathname, O_RDONLY ); if (file < 0) { PERROR(( "UnixSys.MMapFile_Open : could not open '%s'\n", resource->pathname )); return FT_Err_Cannot_Open_Stream; } if (fstat( file, &stat_buf ) < 0) { PERROR(( "UnixSys.MMapFile_Open : could not 'fstat' file '%s'\n", resource->pathname )); goto Fail_Map; } if ( resource->file_size < 0 ) resource->file_size = stat_buf.st_size; stream->resource = (FT_Resource)resource; stream->system = resource->root.driver->system; stream->size = resource->file_size; stream->stream_id.pointer = mmap( NULL, resource->file_size, PROT_READ, MAP_FILE | MAP_PRIVATE, file, 0 ); if ( (long)stream->stream_id.pointer == -1 ) { PERROR(( "UnixSys.MMapFile_Open : Could not map file '%s'\n", resource->pathname )); goto Fail_Map; } close(file); stream->base = (FT_Byte*)stream->stream_id.pointer; stream->cursor = stream->base; PTRACE1(( "UnixSys.MMapFile_Open: opened '%s' (%d bytes) succesfully\n", resource->pathname, resource->file_size )); return FT_Err_Ok; Fail_Map: close(file); stream->resource = NULL; stream->size = 0; stream->stream_id.pointer = NULL; stream->base = NULL; stream->cursor = NULL; return FT_Err_Cannot_Open_Stream; } /***************************************************************************/ /* */ /* MMapFile_Close */ /* */ /* Closes a given stream */ /* */ /* */ /* stream :: the target stream object */ /* */ /* */ /* Error code */ /* */ /* */ /* This function simply unmaps the resource.. */ /* */ /***************************************************************************/ static FT_Error MMapFile_Close( FT_Stream stream ) { PTRACE1(( "Closing file '%s'\n", STREAM_Name(stream) )); munmap ( (void*)stream->stream_id.pointer, stream->size ); stream->resource = NULL; stream->stream_id.pointer = NULL; stream->size = 0; stream->base = NULL; stream->cursor = NULL; return FT_Err_Ok; } /***************************************************************************/ /* */ /* MMapFile_Seek */ /* */ /* Seek a stream to a given position */ /* */ /* */ /* stream :: the target stream object */ /* position :: offset in bytes from start of resource/stream */ /* */ /* */ /* Error code */ /* */ /* */ /* This function should never be called for memory-based resources.. */ /* */ /***************************************************************************/ static FT_Error MMapFile_Seek( FT_Stream stream, FT_Long position ) { (void)stream; (void)position; return FT_Err_Invalid_Stream_Operation; } /***************************************************************************/ /* */ /* MMapFile_Skip */ /* */ /* Skip a given number of bytes in an MMap stream. */ /* Useful to skip pad bytes, for example. */ /* */ /* */ /* stream :: the target stream object */ /* count :: number of bytes to skip in the stream */ /* */ /* */ /* Error code */ /* */ /* */ /* This function should never be called for memory-based resources.. */ /* */ /***************************************************************************/ static FT_Error MMapFile_Skip( FT_Stream stream, FT_Long count ) { (void)stream; (void)count; return FT_Err_Invalid_Stream_Operation; } /***************************************************************************/ /* */ /* MMapFile_Pos */ /* */ /* Returns the current offset within an MMap stream's */ /* resource. */ /* */ /* */ /* stream :: the target stream object */ /* */ /* */ /* position :: current offset. -1 in case of error */ /* */ /* */ /* Error code. */ /* */ /* */ /* This function should never be called for memory-based resources.. */ /* */ /***************************************************************************/ static FT_Error MMapFile_Pos( FT_Stream stream, FT_Long* position ) { (void)stream; (void)position; return FT_Err_Invalid_Stream_Operation; } /***************************************************************************/ /* */ /* MMapFile_Read */ /* */ /* Read a given number of bytes from an MMap stream into */ /* memory. */ /* */ /* */ /* stream :: the target stream object */ /* buffer :: the target read buffer where data is copied */ /* size :: number of bytes to read from the stream */ /* */ /* */ /* read_bytes :: the number of bytes effectively read from the stream */ /* used in case of error (i.e. FT_Err_Invalid_Stream_Read) */ /* by some parts of the library.. */ /* */ /* Error code */ /* */ /* */ /* This function should never be called for memory-based resources.. */ /* */ /***************************************************************************/ static FT_Error MMapFile_Read( FT_Stream stream, FT_Byte* buffer, FT_Long size, FT_Long* read_bytes ) { (void)stream; (void)buffer; (void)size; (void)read_bytes; return FT_Err_Invalid_Stream_Operation; } /* The following table is the "virtual method table" for the 'MMap */ /* resource class', which methods are defined above. Its address is */ /* set in the 'interface' field of all resource objects created by */ /* the function FT_Create_MMapFile (see below) */ static FTRes_InterfaceRec FT_MMapFile_Interface = { (FTRes_Open_Func) MMapFile_Open, (FTRes_Close_Func) MMapFile_Close, (FTRes_Seek_Func) MMapFile_Seek, (FTRes_Skip_Func) MMapFile_Skip, (FTRes_Pos_Func) MMapFile_Pos, (FTRes_Read_Func) MMapFile_Read, }; /************************************************************************/ /* */ /* FT_Create_Resource */ /* */ /* */ /* Create a new resource object for a given library. Note that this */ /* function takes an ASCII 'pathname' as an argument. */ /* */ /* */ /* library :: handle to target library object */ /* pathanme :: ASCII pathname of the font file */ /* */ /* */ /* resource :: handle to new resource object */ /* */ /* */ /* Error code. 0 means success */ /* */ /* */ /* When porting the library to exotic environments, where an */ /* ASCII pathname isn't used to name files, developers should */ /* invoke directly their own resource creation function which */ /* must be placed in their port of the "ftsys" component. */ /* */ /* See the porting guide for more information.. */ /* */ EXPORT_FUNC FT_Error FT_Create_Resource( FT_Library library, const char* pathname, FT_Resource* aresource ) { FT_Int len; FT_System system; FT_MMapFile resource; FT_Error error; *aresource = NULL; if (!library) { PERROR(( "Unix.New_Resource : null library handle\n" )); return FT_Err_Invalid_Library_Handle; } system = library->system; if ( !pathname ) goto Fail_Null; len = strlen(pathname); if (len == 0) goto Fail_Null; resource = NULL; if ( ALLOC( resource, sizeof(*resource) ) || ALLOC( resource->pathname, len+1 ) ) goto Fail_Memory; resource->root.library = library; resource->root.interface = &FT_MMapFile_Interface; resource->root.flags = FT_RESOURCE_TYPE_MEMORY_BASED; resource->file_size = -1; strcpy( (char*)resource->pathname, pathname ); PTRACE1(( "Create_MMapFile : MMap resource created for '%s'\n", pathname )); *aresource = (FT_Resource)resource; return FT_Err_Ok; Fail_Null: PERROR(( "Create_MMapFile : null pathname !!\n" )); return FT_Err_Invalid_Argument; Fail_Memory: if (resource) FREE( resource->pathname ); FREE( resource ); PERROR(( "Create_MMapFile : error when creating resource !\n" )); return error; } /***************************************************************************/ /* */ /* FT_Destroy_Resource */ /* */ /* Destroys an MMap resource object. */ /* This function is never called directly by the font */ /* drivers. Only by the higher-level part of FreeType */ /* (called the HLib), or client applications */ /* */ /* */ /* resource :: the MMap resource object */ /* */ /* */ /* This functions does not check that runs or streams are opened for */ /* the resource (for now, we assume developer intelligence. We'll most */ /* probably lower our standard later to ease debugging ;-) */ /* */ /***************************************************************************/ EXPORT_FUNC FT_Error FT_Destroy_Resource( FT_Resource resource ) { FT_System system = resource->library->system; FT_MMapFile res = (FT_MMapFile)resource; if ( !res || res->root.interface != &FT_MMapFile_Interface ) { PERROR(( "Destroy_MMapFile : Trying to destroy an invalid resource !!\n" )); return FT_Err_Invalid_Resource_Handle; } PTRACE1(( "Destroy_MMapFile : destroying resource for '%s'\n", res->pathname )); FREE( res->pathname ); FREE( res ); return FT_Err_Ok; } /**************************************************************************/ /* */ /* MEMORY MANAGEMENT */ /* */ /* */ /* This part copies the old FreeType 1.0 and 1.1 memory management */ /* scheme that was defined in the file "ttmemory.h". One can see that */ /* */ /* - a set of macros is defined for the memory operations used */ /* by the engine ( MEM_Copy, MEM_Move, MEM_Set ). This comes from */ /* the fact that many compilers are able to inline these ops directly */ /* within the compiled code, rather than generating a call to the */ /* C library. However, this obliges us to include the */ /* header file. */ /* */ /* If you provide your own memory operations routine, you can get */ /* rid of the #include below. */ /* */ /* */ /* - the FT_Alloc function has several essential properties that */ /* MUST be retained by each port : */ /* */ /* - it returns an error code, NOT the allocated block's base */ /* address */ /* */ /* - it takes the address of a target pointer, where the block's */ /* base address will be set. if the size is zero, its value */ /* will be NULL and the function returns successfully */ /* */ /* - in case of error, the pointer's value is set to NULL and */ /* an error code is returned.. */ /* */ /* - the new allocated block MUST be zero-filled. This is a strong */ /* convetion the rest of the engine relies on */ /* */ /* */ /* */ /* - the FT_Free function has also its essentials : */ /* */ /* - it takes the address of a pointer which value is the block's */ /* base address. This is UNLIKE a standard "free" which takes the */ /* the block's base directly. */ /* */ /* - it accepts succesfully the address of a pointer which value */ /* is NULL, in which case it simply returns. */ /* */ /* - the pointer is always set to NULL by the function */ /* */ /* */ /* - the MEM_Alloc, ALLOC and ALLOC_ARRAY macros are used by the */ /* library, and should NOT be modified by porters !! */ /* */ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ /* parameter of the PTRACE and PERROR macros, used to print/log */ /* messages during execution.. */ /* */ #undef FT_COMPONENT #define FT_COMPONENT trace_memory #include /**************************************************************************/ /* */ /* FT_Alloc */ /* */ /* */ /* Allocates a new bloc of memory. The returned area is always */ /* zero-filled, this is a strong convention in many FreeType parts */ /* */ /* */ /* system :: handle to a given 'system object' where allocation */ /* occurs.. */ /* */ /* size :: size in bytes of the block to allocate */ /* */ /* */ /* P :: pointer to the fresh new block. It should be set */ /* to NULL if 'size' is 0, of in case of error.. */ /* */ /* */ /* FreeType error code. 0 means success. */ /* */ /**************************************************************************/ BASE_FUNC FT_Error FT_Alloc( FT_System system, long size, void* *P ) { if (!P) { PERROR(( "FT_Alloc : invalid pointer address !!\n" )); return FT_Err_Invalid_Argument; } if ( size > 0 ) { *P = malloc( size ); if ( !*P ) { PERROR(( "FT_Alloc : out of memory (%ld bytes requested) !!\n", size )); return FT_Err_Out_Of_Memory; } system->total_alloc += size; /* ALWAYS ZERO-FILL THE BLOCK !!!!! */ MEM_Set( *P, 0, size ); } else *P = NULL; PTRACE2(( "FT_Alloc : size = %ld, pointer = 0x%08lx, block = 0x%08lx\n", size, (long)P, (long)*P )); return FT_Err_Ok; } /**************************************************************************/ /* */ /* FT_Realloc */ /* */ /* */ /* Reallocates a block of memory pointed to by '*P' to 'Size' */ /* bytes from the hea^, possibly changing '*P'. */ /* */ /* */ /* system :: handle to a given 'system object' where allocation */ /* occurs.. */ /* */ /* size :: size in bytes of the block to allocate */ /* */ /* */ /* P :: pointer to the fresh new block. It should be set */ /* to NULL if 'size' is 0, of in case of error.. */ /* */ /* */ /* FreeType error code. 0 means success. */ /* */ BASE_FUNC int FT_Realloc( FT_System system, long size, void* *P ) { void* Q; if (!P) { PERROR(( "FT_Realloc : invalid pointer address !!\n" )); return FT_Err_Invalid_Argument; } /* if the original pointer is NULL, call FT_Alloc */ if (!*P) return FT_Alloc( system, size, P ); /* if the new block if zero-sized, clear the current one */ if (size <= 0) return FT_Free( system, P ); Q = (void*)realloc( *P, size ); if (!Q) { PERROR(( "FT_Realloc : reallocation failed\n" )); return FT_Err_Out_Of_Memory; } *P = Q; return FT_Err_Ok; } /**************************************************************************/ /* */ /* FT_Free */ /* */ /* */ /* Releases a given block of memory allocated through FT_Alloc */ /* */ /* */ /* system :: handle to a given 'system object' where allocation */ /* occured.. */ /* */ /* P :: This is the _address_ of a _pointer_ which points to */ /* the allocated block. It is always set to NULL on exit */ /* */ /* */ /* FreeType error code. 0 means success. */ /* */ /* */ /* If P or *P are NULL, this function should return successfuly. This */ /* is a strong convention within all of FreeType and its drivers.. */ /* */ BASE_FUNC FT_Error FT_Free( FT_System system, void* *P ) { (void)system; /* unused parameter. Gets rid of warnings */ PTRACE2(( "FT_Free : freeing pointer 0x%08lx (block 0x%08lx)\n", (long)P, (P ? (long)*P : -1) )); if ( !P || !*P ) return FT_Err_Ok; free( *P ); *P = NULL; return FT_Err_Ok; } /**************************************************************************/ /* */ /* SYNCHRONIZATION MANAGEMENT */ /* */ /* */ /* This section deals with mutexes. The library can be compiled to */ /* three distinct thread-support levels ( namely single-threaded, */ /* thread-safe and re-entrant modes ). */ /* */ /* It protects its variables through the MUTEX_Lock and MUTEX_Release */ /* macros which are void in single-threaded mode. */ /* */ /* */ /* It defines a type-less mutex reference type, "TMutex", that you're */ /* free to redefine for your system's needs.. */ /* */ /* The default implementation of ftsys.c contains only dummy functions */ /* which always return succesfully. you NEED to specialize them in */ /* order to port ftsys.c in any multi-threaded environment... */ /* */ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ /* parameter of the PTRACE and PERROR macros, used to print/log */ /* messages during execution.. */ /* */ #undef FT_COMPONENT #define FT_COMPONENT trace_sync #ifdef FT_CONFIG_THREADS BASE_FUNC FT_Error FT_Mutex_Create ( FT_System system, TMutex* mutex ) { (void)system; /* unused parameter. Gets rid of warnings */ mutex = (void*)-1; system->num_mutexes++; return FT_Err_Ok; /* Replace this line with your own mutex creation code */ } BASE_FUNC void FT_Mutex_Delete ( FT_System system, TMutex* mutex ) { (void)system; /* unused parameter. Gets rid of warnings */ mutex = (void*)0; system->num_mutexes--; /* Replace this line with your own mutex destruction code */ } BASE_FUNC void FT_Mutex_Lock ( FT_System system, TMutex* mutex ) { /* NOTE: It is legal to call this function with a NULL argument */ /* in which case an immediate return is appropriate. */ (void)system; /* unused parameter. Gets rid of warnings */ if ( !mutex ) return; ; /* Insert your own mutex locking code here */ } void FT_Mutex_Release( FT_System system, TMutex* mutex ) { /* NOTE: It is legal to call this function with a NULL argument */ /* in which case an immediate return is appropriate */ (void)system; /* unused parameter. Gets rid of warnings */ if ( !mutex ) return; ; /* Insert your own mutex release code here */ } #endif /* FT_CONFIG_THREADS */ EXPORT_FUNC FT_Error FT_New_System( FT_System* system ) { *system = (FT_System)malloc( sizeof(FT_SystemRec) ); if ( !*system ) return FT_Err_Out_Of_Memory; /* the ANSI function 'free' is unable to return the number */ /* of released bytes. Hence, the 'current_alloc' field of the */ /* system object cannot be used */ (*system)->system_flags = FT_SYSTEM_FLAG_TOTAL_ALLOC | FT_SYSTEM_FLAG_MUTEXES; (*system)->total_alloc = 0; (*system)->num_mutexes = 0; /* initialise i/o management (nothing) */ /* initialise synchronisation (nothing) */ /* initialise streams */ return FT_Err_Ok; } EXPORT_FUNC FT_Error FT_Done_System( FT_System system ) { /* finalise syncrhonisation (nothing) */ /* finalise i/o management (nothing) */ /* finalise memory management */ free( system ); return FT_Err_Ok; }