├── KernelModeSparseFile ├── en.lproj │ └── InfoPlist.strings ├── KernelModeSparseFile-Prefix.pch └── KernelModeSparseFile-Info.plist ├── src ├── DldUndocumentedQuirks.h ├── DldSupportingCode.h ├── DldUndocumentedQuirks.cpp ├── DldSupportingCode.cpp ├── DldCommon.h ├── DldCommonHashTable.h ├── DldCommonHashTable.cpp └── DldSparseFile.h ├── README.md └── KernelModeSparseFile.xcodeproj └── project.pbxproj /KernelModeSparseFile/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /KernelModeSparseFile/KernelModeSparseFile-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | -------------------------------------------------------------------------------- /src/DldUndocumentedQuirks.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 Slava Imameev. All rights reserved. 3 | */ 4 | 5 | #ifndef DLDUNDOCUMENTEDQUIRKS_H 6 | #define DLDUNDOCUMENTEDQUIRKS_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "DldCommon.h" 13 | 14 | //-------------------------------------------------------------------- 15 | 16 | bool DldInitUndocumentedQuirks(); 17 | 18 | void DldFreeUndocumentedQuirks(); 19 | 20 | //-------------------------------------------------------------------- 21 | 22 | int DldDisablePreemption(); 23 | 24 | void DldEnablePreemption( __in int cookie ); 25 | 26 | //-------------------------------------------------------------------- 27 | 28 | #endif//DLDUNDOCUMENTEDQUIRKS_H -------------------------------------------------------------------------------- /KernelModeSparseFile/KernelModeSparseFile-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | Slava-Imameev.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | KEXT 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | IOKitPersonalities 26 | 27 | NSHumanReadableCopyright 28 | Copyright © 2016 Slava-Imameev. All rights reserved. 29 | OSBundleLibraries 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/DldSupportingCode.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 Slava Imameev. All rights reserved. 3 | */ 4 | 5 | #ifndef DLDSUPPORTINGCODE_H 6 | #define DLDSUPPORTINGCODE_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include "DldCommon.h" 12 | 13 | #define DLD_INVALID_EVENT_VALUE ((UInt32)(-1)) 14 | 15 | inline 16 | void 17 | DldInitNotificationEvent( 18 | __in UInt32* event 19 | ) 20 | { 21 | *event = 0x0; 22 | } 23 | 24 | // 25 | // sets the event in a signal state 26 | // 27 | void 28 | DldSetNotificationEvent( 29 | __in UInt32* event 30 | ); 31 | 32 | // 33 | // wait for event, the event is not reseted after the wait is completed 34 | // 35 | wait_result_t 36 | DldWaitForNotificationEventWithTimeout( 37 | __in UInt32* event, 38 | __in uint32_t uSecTimeout // mili seconds, if ( -1) the infinite timeout 39 | ); 40 | 41 | inline 42 | void 43 | DldWaitForNotificationEvent( 44 | __in UInt32* event 45 | ) 46 | { 47 | DldWaitForNotificationEventWithTimeout( event, (-1) ); 48 | } 49 | 50 | 51 | inline 52 | void 53 | DldInitSynchronizationEvent( 54 | __in UInt32* event 55 | ) 56 | { 57 | DldInitNotificationEvent( event ); 58 | } 59 | 60 | // 61 | // sets the event in a signal state 62 | // 63 | inline 64 | void 65 | DldSetSynchronizationEvent( 66 | __in UInt32* event 67 | ) 68 | { 69 | DldSetNotificationEvent( event ); 70 | } 71 | 72 | // 73 | // wait for event, the event is reseted if the wait is completed not because of a timeout 74 | // 75 | inline 76 | wait_result_t 77 | DldWaitForSynchronizationEventWithTimeout( 78 | __in UInt32* event, 79 | __in uint32_t uSecTimeout // mili seconds, if ( -1) the infinite timeout 80 | ) 81 | { 82 | wait_result_t waitResult; 83 | 84 | waitResult = DldWaitForNotificationEventWithTimeout( event, uSecTimeout ); 85 | if( THREAD_AWAKENED == waitResult ){ 86 | 87 | // 88 | // reset the event, notice that you can lost the event set after 89 | // DldWaitForNotificationEvent was woken up and before the event 90 | // is reset 91 | // 92 | OSCompareAndSwap( 0x3, 0x0, event ); 93 | } 94 | 95 | return waitResult; 96 | } 97 | 98 | inline 99 | void 100 | DldWaitForSynchronizationEvent( 101 | __in UInt32* event 102 | ) 103 | { 104 | 105 | DldWaitForNotificationEvent( event ); 106 | 107 | // 108 | // reset the event, notice that you can lost the event set after 109 | // DldWaitForNotificationEvent was woken up and before the event 110 | // is reset 111 | // 112 | OSCompareAndSwap( 0x3, 0x0, event ); 113 | } 114 | 115 | void 116 | DldMemoryBarrier(); 117 | 118 | errno_t 119 | DldVnodeSetsize(vnode_t vp, off_t size, int ioflag, vfs_context_t ctx); 120 | 121 | #endif//DLDSUPPORTINGCODE_H 122 | 123 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MacOSX-SparseFile-KernelMode 2 | 3 | ## License 4 | 5 | The license model is a BSD Open Source License. This is a non-viral license, only asking that if you use it, you acknowledge the authors, in this case Slava Imameev. 6 | 7 | ## Features 8 | 9 | This is a sparse file implementation that can be used from macOS IOKit modules/kernel extensions. The DldSparseFile class is a C++ IOKit class that can be instantiated from IOKit module. The class is inherited from OSObject so it supports reference counting. As is common for IOKit classes the init() function must be called for each new allocated object as the class constructor is unable to report errors due to lack of C++ exceptions support in IOKit. 10 | 11 | This is a kernel mode only code, it can't be used for user mode projects without modification. 12 | 13 | The sparse file is implemented as a storage over a general file. The B-Tree is used to store and access data. 14 | 15 | To use DldSparseFile class you should include all files from the src directory to your project. The repository contains an IOKit module project that is provided only for your convinience so you can check that files can be compiled as a standalone project in your build environment. 16 | 17 | ## Usage 18 | 19 | There is an example of a kextd that uses sparse files to support VFS isolation layer, for details see https://github.com/slavaim/MacOSX-VFS-Isolation-Filter . 20 | 21 | Before the first usage the static DldSparseFile::sInitSparseFileSubsystem() initialization routine should be called. The counterpart routine DldSparseFile::sFreeSparseFileSubsystem() should be called on IOKit module unloading or after all DldSparseFile objects have been freed. DldSparseFilesHashTable::CreateStaticTableWithSize() should also be called to initialize a hash table. 22 | 23 | To create a sparse file backed by a file on a filesystem call the static function 24 | 25 | ``` 26 | static DldSparseFile* DldSparseFile::withPath( __in const char* sparseFilePath, // path to a file 27 | __in_opt vnode_t cawlCoveringVnode, // set to NULL 28 | __in_opt unsigned char* cawlCoveredVnodeID, // set to NULL 29 | __in_opt const char* identificationString ); // set to NULL 30 | ``` 31 | The sparseFilePath parameter is a path to a file on any mounted filesystem. 32 | You should ignore cawlCoveringVnode, cawlCoveredVnodeID and identificationString and set them to NULL. These parameters are optional and are used in MacOSX-Kernel-Filter project. Refer to MacOSX-Kernel-Filter ( https://github.com/slavaim/MacOSX-Kernel-Filter ) project for more information. 33 | 34 | To perform file IO(read/write) call 35 | 36 | ``` 37 | void DldSparseFile::performIO( __inout IoBlockDescriptor* descriptors, 38 | __in unsigned int dscrCount ); 39 | ``` 40 | 41 | For an example of sparse IO file usage see DldCoveringFsd::rwData function implementation at https://github.com/slavaim/MacOSX-Kernel-Filter/blob/master/DLDriver/DldCoveringVnode.cpp#L1133 42 | -------------------------------------------------------------------------------- /src/DldUndocumentedQuirks.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 Slava Imameev. All rights reserved. 3 | */ 4 | 5 | #include "DldUndocumentedQuirks.h" 6 | #include 7 | #include 8 | 9 | //-------------------------------------------------------------------- 10 | 11 | // 12 | // it is hardly that we will need more than CPUs in the system, 13 | // but we need a reasonable prime value for a good dustribution 14 | // 15 | static IOSimpleLock* gPreemptionLocks[ 19 ]; 16 | 17 | //-------------------------------------------------------------------- 18 | 19 | bool 20 | DldAllocateInitPreemtionLocks() 21 | { 22 | 23 | for( int i = 0x0; i < DLD_STATIC_ARRAY_SIZE( gPreemptionLocks ); ++i ){ 24 | 25 | gPreemptionLocks[ i ] = IOSimpleLockAlloc(); 26 | assert( gPreemptionLocks[ i ] ); 27 | if( !gPreemptionLocks[ i ] ) 28 | return false; 29 | 30 | } 31 | 32 | return true; 33 | } 34 | 35 | //-------------------------------------------------------------------- 36 | 37 | void 38 | DldFreePreemtionLocks() 39 | { 40 | for( int i = 0x0; i < DLD_STATIC_ARRAY_SIZE( gPreemptionLocks ); ++i ){ 41 | 42 | if( gPreemptionLocks[ i ] ) 43 | IOSimpleLockFree( gPreemptionLocks[ i ] ); 44 | 45 | } 46 | } 47 | 48 | //-------------------------------------------------------------------- 49 | 50 | #define DldTaskToPreemptionLockIndx( _t_ ) (int)( ((vm_offset_t)(_t_)>>5)%DLD_STATIC_ARRAY_SIZE( gPreemptionLocks ) ) 51 | 52 | // 53 | // MAC OS X kernel doesn't export disable_preemtion() and does not 54 | // allow a spin lock allocaton on the stack! Marvelous, first time 55 | // I saw such a reckless design! All of the above are allowed by 56 | // Windows kernel as IRQL rising to DISPATCH_LEVEL and KSPIN_LOCK. 57 | // 58 | 59 | // 60 | // returns a cookie for DldEnablePreemption 61 | // 62 | int 63 | DldDisablePreemption() 64 | { 65 | int indx; 66 | 67 | // 68 | // check for a recursion or already disabled preemption, 69 | // we support a limited recursive functionality - the 70 | // premption must not be enabled by calling enable_preemption() 71 | // while was disabled by this function 72 | // 73 | if( !preemption_enabled() ) 74 | return (int)(-1); 75 | 76 | indx = DldTaskToPreemptionLockIndx( current_task() ); 77 | assert( indx < DLD_STATIC_ARRAY_SIZE( gPreemptionLocks ) ); 78 | 79 | // 80 | // acquiring a spin lock have a side effect of preemption disabling, 81 | // so try to find any free slot for this thread 82 | // 83 | while( !IOSimpleLockTryLock( gPreemptionLocks[ indx ] ) ){ 84 | 85 | indx = (indx+1)%DLD_STATIC_ARRAY_SIZE( gPreemptionLocks ); 86 | 87 | }// end while 88 | 89 | assert( !preemption_enabled() ); 90 | 91 | return indx; 92 | } 93 | 94 | //-------------------------------------------------------------------- 95 | 96 | // 97 | // accepts a value returned by DldDisablePreemption 98 | // 99 | void 100 | DldEnablePreemption( __in int cookie ) 101 | { 102 | assert( !preemption_enabled() ); 103 | 104 | // 105 | // check whether a call to DldDisablePreemption was a void one 106 | // 107 | if( (int)(-1) == cookie ) 108 | return; 109 | 110 | assert( cookie < DLD_STATIC_ARRAY_SIZE( gPreemptionLocks ) ); 111 | 112 | // 113 | // release the lock thus enabling preemption 114 | // 115 | IOSimpleLockUnlock( gPreemptionLocks[ cookie ] ); 116 | 117 | assert( preemption_enabled() ); 118 | } 119 | 120 | //-------------------------------------------------------------------- 121 | 122 | bool 123 | DldInitUndocumentedQuirks() 124 | { 125 | 126 | assert( preemption_enabled() ); 127 | assert( current_task() == kernel_task ); 128 | 129 | if( !DldAllocateInitPreemtionLocks() ){ 130 | 131 | DBG_PRINT_ERROR(("DldAllocateInitPreemtionLocks() failed\n")); 132 | return false; 133 | } 134 | 135 | return true; 136 | } 137 | 138 | //-------------------------------------------------------------------- 139 | 140 | void 141 | DldFreeUndocumentedQuirks() 142 | { 143 | DldFreePreemtionLocks(); 144 | } 145 | 146 | //-------------------------------------------------------------------- 147 | -------------------------------------------------------------------------------- /src/DldSupportingCode.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 Slava Imameev. All rights reserved. 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include "DldSupportingCode.h" 9 | #include "DldUndocumentedQuirks.h" 10 | 11 | //-------------------------------------------------------------------- 12 | 13 | // 14 | // the event states transitions 15 | // 0 - initialized, 16 | // 1 - preparing for waiting ( before entering a wq ) 17 | // 2 - ready for waiting ( after entering in a wq ) 18 | // 3 - signal state 19 | // 20 | // only a single thread can wait on a synchronization event, the case of multiple 21 | // waiting threads will reasult in an infinite waiting and unpredictable behaviour 22 | // 23 | 24 | void 25 | DldSetNotificationEvent( 26 | __in UInt32* event 27 | ) 28 | { 29 | if( OSCompareAndSwap( 0x0, 0x3, event ) ) 30 | return; 31 | 32 | assert( 0x1 == *event || 0x2 == *event ); 33 | 34 | // 35 | // a collision with entering in a wq or already in a wq, 36 | // wait until entering in a wq, as entering is made with preemtion 37 | // disabled the loop won't last long 38 | // 39 | while( !OSCompareAndSwap( 0x2, 0x3, event ) ){ 40 | } 41 | 42 | // 43 | // wake up a waiting thread 44 | // 45 | thread_wakeup( event ); 46 | DLD_DBG_MAKE_POINTER_INVALID( event ); 47 | 48 | } 49 | 50 | //-------------------------------------------------------------------- 51 | 52 | wait_result_t 53 | DldWaitForNotificationEventWithTimeout( 54 | __in UInt32* event, 55 | __in uint32_t uSecTimeout // mili seconds, if ( -1) the infinite timeout 56 | ) 57 | { 58 | wait_result_t waitResult; 59 | bool wait; 60 | int cookie; 61 | 62 | assert( DLD_INVALID_EVENT_VALUE != *event ); 63 | 64 | // 65 | // check for a signal state, the event must be of notification type 66 | // 67 | if( OSCompareAndSwap( 0x3, 0x3, event ) ) 68 | return THREAD_AWAKENED; 69 | 70 | assert( preemption_enabled() ); 71 | 72 | // 73 | // disable preemtion to not delay 74 | // DldSetNotificationEvent for a long time 75 | // and to not freeze in a wait queue forever 76 | // if rescheduling happens after assert_wait 77 | // and before OSCompareAndSwap( 0x1, 0x2, event ) 78 | // thus leaving DldSetNotificationEvent in an 79 | // endless loop 80 | // 81 | cookie = DldDisablePreemption(); 82 | {// start of scheduler disabling 83 | 84 | assert( !preemption_enabled() ); 85 | 86 | if( OSCompareAndSwap( 0x0, 0x1, event ) ){ 87 | 88 | if( (-1) == uSecTimeout ) 89 | assert_wait( event, THREAD_UNINT ); 90 | else 91 | assert_wait_timeout( (event_t)event, THREAD_UNINT, uSecTimeout, 1000*NSEC_PER_USEC ); 92 | 93 | // 94 | // we are now in a wait queue, 95 | // we must not give any chance for the scheduler here 96 | // to move us from a CPU thus leaving in the waiting queue 97 | // forever, so the preemption has been disabled 98 | // 99 | 100 | // 101 | // say to DldSetNotificationEvent that we are in a wq 102 | // 103 | wait = OSCompareAndSwap( 0x1, 0x2, event ); 104 | assert( wait ); 105 | 106 | waitResult = THREAD_AWAKENED; 107 | 108 | } else { 109 | 110 | // 111 | // DldSetNotificationEvent managed to set the event 112 | // 113 | wait = false; 114 | assert( 0x3 == *event ); 115 | 116 | waitResult = THREAD_AWAKENED; 117 | } 118 | 119 | }// end of scheduler disabling 120 | DldEnablePreemption( cookie ); 121 | 122 | assert( preemption_enabled() ); 123 | 124 | if( wait ) 125 | waitResult = thread_block( THREAD_CONTINUE_NULL ); 126 | 127 | if( THREAD_TIMED_OUT == waitResult ){ 128 | 129 | // 130 | // change the "entering wq" state to the "non signal" state 131 | // 132 | OSCompareAndSwap( 0x2, 0x0, event ); 133 | } 134 | 135 | assert( THREAD_AWAKENED == waitResult || THREAD_TIMED_OUT == waitResult ); 136 | 137 | return waitResult; 138 | } 139 | 140 | //-------------------------------------------------------------------- 141 | 142 | volatile SInt32 memoryBarrier = 0x0; 143 | 144 | void 145 | DldMemoryBarrier() 146 | { 147 | /* 148 | "...locked operations serialize all outstanding load and store operations 149 | (that is, wait for them to complete)." ..."Locked operations are atomic 150 | with respect to all other memory operations and all externally visible events. 151 | Only instruction fetch and page table accesses can pass locked instructions. 152 | Locked instructions can be used to synchronize data written by one processor 153 | and read by another processor." - Intel® 64 and IA-32 Architectures Software Developer’s Manual, Chapter 8.1.2. 154 | */ 155 | OSIncrementAtomic( &memoryBarrier ); 156 | } 157 | 158 | //-------------------------------------------------------------------- 159 | 160 | errno_t 161 | DldVnodeSetsize(vnode_t vp, off_t size, int ioflag, vfs_context_t ctx) 162 | { 163 | errno_t error; 164 | struct vnode_attr va; 165 | 166 | // 167 | // get io count reference 168 | // 169 | error = vnode_getwithref( vp ); 170 | if( error ) 171 | return error; 172 | 173 | VATTR_INIT(&va); 174 | VATTR_SET(&va, va_data_size, size); 175 | va.va_vaflags = ioflag & 0xffff; 176 | error = vnode_setattr(vp, &va, ctx); 177 | 178 | vnode_put( vp ); 179 | return error; 180 | } 181 | 182 | //-------------------------------------------------------------------- 183 | -------------------------------------------------------------------------------- /src/DldCommon.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009 Slava Imameev. All rights reserved. 3 | */ 4 | 5 | #ifndef _DLDCOMMON_H 6 | #define _DLDCOMMON_H 7 | 8 | #include 9 | #include 10 | 11 | #if !defined(__i386__) && !defined(__x86_64__) 12 | #error "Unsupported architecture" 13 | #endif 14 | 15 | // 16 | // DBG for debug 17 | // _DLD_LOG - for log 18 | // _DLD_LOG_ERRORS - for error log 19 | // 20 | #if defined(DBG) 21 | #define DLD_INVALID_POINTER_VALUE ((long)(-1)) 22 | #define DLD_DBG_MAKE_POINTER_INVALID( _ptr ) do{ (*(long*)&_ptr) = DLD_INVALID_POINTER_VALUE; }while(0); 23 | #define DLD_IS_POINTER_VALID( _ptr ) ( NULL != _ptr && DLD_INVALID_POINTER_VALUE != (long)_ptr ) 24 | #else//DBG 25 | #define DLD_DBG_MAKE_POINTER_INVALID( _ptr ) do{void(0);}while(0); 26 | #define DLD_IS_POINTER_VALID( _ptr ) ( NULL != _ptr ) 27 | #endif//DBG 28 | 29 | // 30 | // the sleep is required to allow the logger to catch up, 31 | // the macro requires a boolean variable isError being defined in the outer scope, 32 | // the macro uses the internal DldLog if available or the Apple System Logger( ASL ) in the other case 33 | // 34 | #define DLD_COMM_LOG_EXT( _S_ ) do{\ 35 | IOLog("%s %s(%u):%s: ", isError?"ERROR!!":"", __FILE__ , __LINE__, __PRETTY_FUNCTION__ );\ 36 | IOLog _S_ ; \ 37 | IOSleep( 50 );\ 38 | }while(0); 39 | 40 | // 41 | // ASL - Apple System Logger, 42 | // the macro requires a boolean variable isError being defined in the outer scope, 43 | // the macro uses only ASL 44 | // 45 | #define DLD_COMM_LOG_EXT_TO_ASL( _S_ ) do{\ 46 | SInt32 logIndex = OSIncrementAtomic( &gLogIndx ); \ 47 | IOLog(" [%-7d] DldLog:", (int)logIndex ); \ 48 | IOLog("%s %s(%u):%s: ", isError?"ERROR!!":"", __FILE__ , __LINE__, __PRETTY_FUNCTION__ );\ 49 | IOLog _S_ ; \ 50 | IOSleep( 50 );\ 51 | }while(0); 52 | 53 | // 54 | // a common log 55 | // 56 | #if !defined(_DLD_LOG) 57 | 58 | #define DBG_PRINT( _S_ ) do{ void(0); }while(0);// { kprintf _S_ ; } 59 | 60 | #else 61 | 62 | // 63 | // IOSleep called after IOLog allows the system to replenish the log buffer by retrieving the existing entries using syslogd 64 | // 65 | #define DBG_PRINT( _S_ ) do{ bool isError = false; DLD_COMM_LOG_EXT( _S_ ); }while(0); 66 | 67 | #endif 68 | 69 | 70 | // 71 | // errors log 72 | // 73 | #if !defined(_DLD_LOG_ERRORS) 74 | 75 | #define DBG_PRINT_ERROR( _S_ ) do{ void(0); }while(0);//DBG_PRINT( _S_ ) 76 | 77 | #else 78 | 79 | #define DBG_PRINT_ERROR( _S_ ) do{ if( 1 ){ bool isError = true; DLD_COMM_LOG_EXT( _S_ ); } }while(0); 80 | 81 | #endif 82 | 83 | // 84 | // errors log to ASL 85 | // 86 | #if !defined(_DLD_LOG_ERRORS) 87 | 88 | #define DBG_PRINT_ERROR_TO_ASL( _S_ ) do{ void(0); }while(0);//DBG_PRINT( _S_ ) 89 | 90 | #else 91 | 92 | #define DBG_PRINT_ERROR_TO_ASL( _S_ ) do{ if( 1 ){ bool isError = true; DLD_COMM_LOG_EXT_TO_ASL( _S_ ); } }while(0); 93 | 94 | #endif 95 | 96 | // 97 | // common log, should not be used for errors 98 | // 99 | #define DLD_COMM_LOG( _TYPE_ , _S_ ) do{ if( 1 ){ bool isError = false; DLD_COMM_LOG_EXT( _S_ ); } }while(0); 100 | 101 | // 102 | // ASL common log 103 | // 104 | #define DLD_COMM_LOG_TO_ASL( _TYPE_ , _S_ ) do{ if( 1 ){ bool isError = false; DLD_COMM_LOG_EXT_TO_ASL( _S_ ); } }while(0); 105 | 106 | //-------------------------------------------------------------------- 107 | 108 | #define __in 109 | #define __out 110 | #define __inout 111 | #define __in_opt 112 | #define __out_opt 113 | #define __opt 114 | 115 | //-------------------------------------------------------------------- 116 | 117 | #define DLD_STATIC_ARRAY_SIZE( _ARR_ ) ( (unsigned int)( sizeof(_ARR_)/sizeof(_ARR_[0] ) ) ) 118 | 119 | //-------------------------------------------------------------------- 120 | 121 | // 122 | // an addition to the native sloppy circle implementation 123 | // 124 | #define CIRCLEQ_INIT_WITH_TYPE(head, type) do { \ 125 | (head)->cqh_first = (type *)(head); \ 126 | (head)->cqh_last = (type *)(head); \ 127 | } while (0) 128 | 129 | 130 | #define CIRCLEQ_INSERT_HEAD_WITH_TYPE(head, elm, field, type) do { \ 131 | (elm)->field.cqe_next = (head)->cqh_first; \ 132 | (elm)->field.cqe_prev = (type *)(head); \ 133 | if ((head)->cqh_last == (type *)(head)) \ 134 | (head)->cqh_last = (elm); \ 135 | else \ 136 | (head)->cqh_first->field.cqe_prev = (elm); \ 137 | (head)->cqh_first = (elm); \ 138 | } while (0) 139 | 140 | #define CIRCLEQ_INSERT_TAIL_WITH_TYPE(head, elm, field, type) do { \ 141 | (elm)->field.cqe_next = (type *)(head); \ 142 | (elm)->field.cqe_prev = (head)->cqh_last; \ 143 | if ((head)->cqh_first == (type *)(head)) \ 144 | (head)->cqh_first = (elm); \ 145 | else \ 146 | (head)->cqh_last->field.cqe_next = (elm); \ 147 | (head)->cqh_last = (elm); \ 148 | } while (0) 149 | 150 | //-------------------------------------------------------------------- 151 | 152 | #define FIELD_OFFSET(Type,Field) (reinterpret_cast( (&(((Type *)(0))->Field)) ) ) 153 | 154 | //-------------------------------------------------------------------- 155 | 156 | // 157 | // Double linked list manipulation functions, the same as on Windows 158 | // 159 | 160 | typedef struct _LIST_ENTRY { 161 | struct _LIST_ENTRY *Flink; 162 | struct _LIST_ENTRY *Blink; 163 | } LIST_ENTRY, *PLIST_ENTRY; 164 | 165 | inline 166 | void 167 | InitializeListHead( 168 | __inout PLIST_ENTRY ListHead 169 | ) 170 | { 171 | ListHead->Flink = ListHead->Blink = ListHead; 172 | } 173 | 174 | inline 175 | bool 176 | IsListEmpty( 177 | __in const LIST_ENTRY * ListHead 178 | ) 179 | { 180 | return (bool)(ListHead->Flink == ListHead); 181 | } 182 | 183 | inline 184 | bool 185 | RemoveEntryList( 186 | __in PLIST_ENTRY Entry 187 | ) 188 | { 189 | PLIST_ENTRY Blink; 190 | PLIST_ENTRY Flink; 191 | 192 | Flink = Entry->Flink; 193 | Blink = Entry->Blink; 194 | Blink->Flink = Flink; 195 | Flink->Blink = Blink; 196 | return (bool)(Flink == Blink); 197 | } 198 | 199 | inline 200 | PLIST_ENTRY 201 | RemoveHeadList( 202 | __in PLIST_ENTRY ListHead 203 | ) 204 | { 205 | PLIST_ENTRY Flink; 206 | PLIST_ENTRY Entry; 207 | 208 | Entry = ListHead->Flink; 209 | Flink = Entry->Flink; 210 | ListHead->Flink = Flink; 211 | Flink->Blink = ListHead; 212 | return Entry; 213 | } 214 | 215 | inline 216 | PLIST_ENTRY 217 | RemoveTailList( 218 | __in PLIST_ENTRY ListHead 219 | ) 220 | { 221 | PLIST_ENTRY Blink; 222 | PLIST_ENTRY Entry; 223 | 224 | Entry = ListHead->Blink; 225 | Blink = Entry->Blink; 226 | ListHead->Blink = Blink; 227 | Blink->Flink = ListHead; 228 | return Entry; 229 | } 230 | 231 | inline 232 | void 233 | InsertTailList( 234 | __in PLIST_ENTRY ListHead, 235 | __in PLIST_ENTRY Entry 236 | ) 237 | { 238 | PLIST_ENTRY Blink; 239 | 240 | Blink = ListHead->Blink; 241 | Entry->Flink = ListHead; 242 | Entry->Blink = Blink; 243 | Blink->Flink = Entry; 244 | ListHead->Blink = Entry; 245 | } 246 | 247 | 248 | inline 249 | void 250 | InsertHeadList( 251 | __in PLIST_ENTRY ListHead, 252 | __in PLIST_ENTRY Entry 253 | ) 254 | { 255 | PLIST_ENTRY Flink; 256 | 257 | Flink = ListHead->Flink; 258 | Entry->Flink = Flink; 259 | Entry->Blink = ListHead; 260 | Flink->Blink = Entry; 261 | ListHead->Flink = Entry; 262 | } 263 | 264 | inline 265 | void 266 | AppendTailList( 267 | __in PLIST_ENTRY ListHead, 268 | __in PLIST_ENTRY ListToAppend 269 | ) 270 | { 271 | PLIST_ENTRY ListEnd = ListHead->Blink; 272 | 273 | ListHead->Blink->Flink = ListToAppend; 274 | ListHead->Blink = ListToAppend->Blink; 275 | ListToAppend->Blink->Flink = ListHead; 276 | ListToAppend->Blink = ListEnd; 277 | } 278 | 279 | //--------------------------------------------------------------------- 280 | 281 | // 282 | // Calculate the address of the base of the structure given its type, and an 283 | // address of a field within the structure. 284 | // 285 | 286 | #define CONTAINING_RECORD(address, type, field) ((type *)( \ 287 | (char*)(address) - \ 288 | reinterpret_cast(&((type *)0)->field))) 289 | 290 | //--------------------------------------------------------------------- 291 | 292 | #define DlSetFlag( Flag, Value ) ((Flag) |= (Value)) 293 | #define DlClearFlag( Flag, Value ) ((Flag) &= ~(Value)) 294 | #define DlIsFlagOn( Flag, Value ) ( ( (Flag) & (Value) ) != 0x0 ) 295 | 296 | //-------------------------------------------------------------------- 297 | 298 | #ifndef OSCompareAndSwapPtr 299 | /* 300 | 10.5 SDK doesn't define OSCompareAndSwapPtr, so this is an easy way to find that this is a 10.5 compilation, 301 | the Apple document http://developer.apple.com/library/ios/#documentation/DeveloperTools/Conceptual/cross_development/Using/using.html#//apple_ref/doc/uid/20002000-SW6 302 | is incomplete and misguiding 303 | */ 304 | #define DLD_MACOSX_10_5 (0x1) 305 | #endif// 306 | 307 | #ifdef DLD_MACOSX_10_5 308 | 309 | // 310 | // 10.6 SDK uses SAFE_CAST_PTR to cast pointers, 10.5 doesn't 311 | // 312 | #define OSCompareAndSwap(a, b, c) \ 313 | (OSCompareAndSwap((UInt32)a, (UInt32)b, (volatile UInt32*)c )) 314 | 315 | /*! 316 | * @function OSCompareAndSwapPtr 317 | * 318 | * @abstract 319 | * Compare and swap operation, performed atomically with respect to all devices that participate in the coherency architecture of the platform. 320 | * 321 | * @discussion 322 | * The OSCompareAndSwapPtr function compares the pointer-sized value at the specified address with oldVal. The value of newValue is written to the address only if oldValue and the value at the address are equal. OSCompareAndSwapPtr returns true if newValue is written to the address; otherwise, it returns false. 323 | * 324 | * This function guarantees atomicity only with main system memory. It is specifically unsuitable for use on noncacheable memory such as that in devices; this function cannot guarantee atomicity, for example, on memory mapped from a PCI device. Additionally, this function incorporates a memory barrier on systems with weakly-ordered memory architectures. 325 | * @param oldValue The pointer value to compare at address. 326 | * @param newValue The pointer value to write to address if oldValue compares true. 327 | * @param address The pointer-size aligned address of the data to update atomically. 328 | * @result true if newValue was written to the address. 329 | * 330 | extern Boolean OSCompareAndSwapPtr( 331 | void * oldValue, 332 | void * newValue, 333 | void * volatile * address); 334 | */ 335 | #if defined(__x86_64__) 336 | 337 | #error "we don't support 10.5 64 bit kernel" 338 | 339 | /*! 340 | * @function OSCompareAndSwap64 341 | * 342 | * @abstract 343 | * 64-bit compare and swap operation. 344 | * 345 | * @discussion 346 | * See OSCompareAndSwap. 347 | */ 348 | #define OSCompareAndSwapPtr(a, b, c) \ 349 | (OSCompareAndSwap64((UInt64)a, (UInt64)b, (volatile UInt64*)c)) 350 | 351 | #endif//defined(__x86_64__) 352 | 353 | 354 | #if defined(__i386__) 355 | 356 | #define OSCompareAndSwapPtr(a, b, c) \ 357 | (OSCompareAndSwap((UInt32)a, (UInt32)b, (volatile UInt32*)c )) 358 | 359 | #endif//defined(__i386__) 360 | 361 | // 10.6 SDK uses SAFE_CAST_PTR for OSIncrementAtomic's parameter, 10.5 doesn't 362 | #define OSIncrementAtomic(a) \ 363 | (OSIncrementAtomic((volatile SInt32*)a)) 364 | 365 | #define OSDecrementAtomic(a) \ 366 | (OSDecrementAtomic((volatile SInt32*)a)) 367 | 368 | #define vnode_getwithref(v) (vnode_get(v)) 369 | 370 | extern "C" 371 | int vn_rdwr(enum uio_rw rw, struct vnode *vp, caddr_t base, 372 | int len, off_t offset, enum uio_seg segflg, int ioflg, 373 | kauth_cred_t cred, int *aresid, struct proc *p); 374 | 375 | // actually this property doesn't exists on 10.5 , the ID should be retrieved by inquiring a device 376 | #define kUSBSerialNumberString "USB Serial Number" 377 | 378 | #endif//DLD_MACOSX_10_5 379 | 380 | //-------------------------------------------------------------------- 381 | 382 | #endif//_DLDCOMMON_H -------------------------------------------------------------------------------- /KernelModeSparseFile.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | F9BC77481DEB271200F9190F /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F9BC77461DEB271200F9190F /* InfoPlist.strings */; }; 11 | F9BC775C1DEB274000F9190F /* DldCommon.h in Headers */ = {isa = PBXBuildFile; fileRef = F9BC77531DEB274000F9190F /* DldCommon.h */; }; 12 | F9BC775D1DEB274000F9190F /* DldCommonHashTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9BC77541DEB274000F9190F /* DldCommonHashTable.cpp */; }; 13 | F9BC775E1DEB274000F9190F /* DldCommonHashTable.h in Headers */ = {isa = PBXBuildFile; fileRef = F9BC77551DEB274000F9190F /* DldCommonHashTable.h */; }; 14 | F9BC775F1DEB274000F9190F /* DldSparseFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9BC77561DEB274000F9190F /* DldSparseFile.cpp */; }; 15 | F9BC77601DEB274000F9190F /* DldSparseFile.h in Headers */ = {isa = PBXBuildFile; fileRef = F9BC77571DEB274000F9190F /* DldSparseFile.h */; }; 16 | F9BC77611DEB274000F9190F /* DldSupportingCode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9BC77581DEB274000F9190F /* DldSupportingCode.cpp */; }; 17 | F9BC77621DEB274000F9190F /* DldSupportingCode.h in Headers */ = {isa = PBXBuildFile; fileRef = F9BC77591DEB274000F9190F /* DldSupportingCode.h */; }; 18 | F9BC77631DEB274000F9190F /* DldUndocumentedQuirks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9BC775A1DEB274000F9190F /* DldUndocumentedQuirks.cpp */; }; 19 | F9BC77641DEB274000F9190F /* DldUndocumentedQuirks.h in Headers */ = {isa = PBXBuildFile; fileRef = F9BC775B1DEB274000F9190F /* DldUndocumentedQuirks.h */; }; 20 | F9BC77661DEB2BFB00F9190F /* KernelModeSparseFile-Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = F9BC77651DEB2BFB00F9190F /* KernelModeSparseFile-Prefix.pch */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | F9BC773E1DEB271200F9190F /* KernelModeSparseFile.kext */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KernelModeSparseFile.kext; sourceTree = BUILT_PRODUCTS_DIR; }; 25 | F9BC77421DEB271200F9190F /* Kernel.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Kernel.framework; path = System/Library/Frameworks/Kernel.framework; sourceTree = SDKROOT; }; 26 | F9BC77451DEB271200F9190F /* KernelModeSparseFile-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "KernelModeSparseFile-Info.plist"; sourceTree = ""; }; 27 | F9BC77471DEB271200F9190F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 28 | F9BC77531DEB274000F9190F /* DldCommon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DldCommon.h; sourceTree = ""; }; 29 | F9BC77541DEB274000F9190F /* DldCommonHashTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DldCommonHashTable.cpp; sourceTree = ""; }; 30 | F9BC77551DEB274000F9190F /* DldCommonHashTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DldCommonHashTable.h; sourceTree = ""; }; 31 | F9BC77561DEB274000F9190F /* DldSparseFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DldSparseFile.cpp; sourceTree = ""; }; 32 | F9BC77571DEB274000F9190F /* DldSparseFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DldSparseFile.h; sourceTree = ""; }; 33 | F9BC77581DEB274000F9190F /* DldSupportingCode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DldSupportingCode.cpp; sourceTree = ""; }; 34 | F9BC77591DEB274000F9190F /* DldSupportingCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DldSupportingCode.h; sourceTree = ""; }; 35 | F9BC775A1DEB274000F9190F /* DldUndocumentedQuirks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DldUndocumentedQuirks.cpp; sourceTree = ""; }; 36 | F9BC775B1DEB274000F9190F /* DldUndocumentedQuirks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DldUndocumentedQuirks.h; sourceTree = ""; }; 37 | F9BC77651DEB2BFB00F9190F /* KernelModeSparseFile-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "KernelModeSparseFile-Prefix.pch"; sourceTree = ""; }; 38 | /* End PBXFileReference section */ 39 | 40 | /* Begin PBXFrameworksBuildPhase section */ 41 | F9BC77391DEB271200F9190F /* Frameworks */ = { 42 | isa = PBXFrameworksBuildPhase; 43 | buildActionMask = 2147483647; 44 | files = ( 45 | ); 46 | runOnlyForDeploymentPostprocessing = 0; 47 | }; 48 | /* End PBXFrameworksBuildPhase section */ 49 | 50 | /* Begin PBXGroup section */ 51 | F9BC77331DEB271200F9190F = { 52 | isa = PBXGroup; 53 | children = ( 54 | F9BC77521DEB274000F9190F /* src */, 55 | F9BC77431DEB271200F9190F /* KernelModeSparseFile */, 56 | F9BC77401DEB271200F9190F /* Frameworks */, 57 | F9BC773F1DEB271200F9190F /* Products */, 58 | ); 59 | sourceTree = ""; 60 | }; 61 | F9BC773F1DEB271200F9190F /* Products */ = { 62 | isa = PBXGroup; 63 | children = ( 64 | F9BC773E1DEB271200F9190F /* KernelModeSparseFile.kext */, 65 | ); 66 | name = Products; 67 | sourceTree = ""; 68 | }; 69 | F9BC77401DEB271200F9190F /* Frameworks */ = { 70 | isa = PBXGroup; 71 | children = ( 72 | F9BC77411DEB271200F9190F /* Other Frameworks */, 73 | ); 74 | name = Frameworks; 75 | sourceTree = ""; 76 | }; 77 | F9BC77411DEB271200F9190F /* Other Frameworks */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | F9BC77421DEB271200F9190F /* Kernel.framework */, 81 | ); 82 | name = "Other Frameworks"; 83 | sourceTree = ""; 84 | }; 85 | F9BC77431DEB271200F9190F /* KernelModeSparseFile */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | F9BC77441DEB271200F9190F /* Supporting Files */, 89 | ); 90 | path = KernelModeSparseFile; 91 | sourceTree = ""; 92 | }; 93 | F9BC77441DEB271200F9190F /* Supporting Files */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | F9BC77651DEB2BFB00F9190F /* KernelModeSparseFile-Prefix.pch */, 97 | F9BC77451DEB271200F9190F /* KernelModeSparseFile-Info.plist */, 98 | F9BC77461DEB271200F9190F /* InfoPlist.strings */, 99 | ); 100 | name = "Supporting Files"; 101 | sourceTree = ""; 102 | }; 103 | F9BC77521DEB274000F9190F /* src */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | F9BC77531DEB274000F9190F /* DldCommon.h */, 107 | F9BC77541DEB274000F9190F /* DldCommonHashTable.cpp */, 108 | F9BC77551DEB274000F9190F /* DldCommonHashTable.h */, 109 | F9BC77561DEB274000F9190F /* DldSparseFile.cpp */, 110 | F9BC77571DEB274000F9190F /* DldSparseFile.h */, 111 | F9BC77581DEB274000F9190F /* DldSupportingCode.cpp */, 112 | F9BC77591DEB274000F9190F /* DldSupportingCode.h */, 113 | F9BC775A1DEB274000F9190F /* DldUndocumentedQuirks.cpp */, 114 | F9BC775B1DEB274000F9190F /* DldUndocumentedQuirks.h */, 115 | ); 116 | path = src; 117 | sourceTree = ""; 118 | }; 119 | /* End PBXGroup section */ 120 | 121 | /* Begin PBXHeadersBuildPhase section */ 122 | F9BC773A1DEB271200F9190F /* Headers */ = { 123 | isa = PBXHeadersBuildPhase; 124 | buildActionMask = 2147483647; 125 | files = ( 126 | F9BC775C1DEB274000F9190F /* DldCommon.h in Headers */, 127 | F9BC77641DEB274000F9190F /* DldUndocumentedQuirks.h in Headers */, 128 | F9BC77601DEB274000F9190F /* DldSparseFile.h in Headers */, 129 | F9BC77661DEB2BFB00F9190F /* KernelModeSparseFile-Prefix.pch in Headers */, 130 | F9BC775E1DEB274000F9190F /* DldCommonHashTable.h in Headers */, 131 | F9BC77621DEB274000F9190F /* DldSupportingCode.h in Headers */, 132 | ); 133 | runOnlyForDeploymentPostprocessing = 0; 134 | }; 135 | /* End PBXHeadersBuildPhase section */ 136 | 137 | /* Begin PBXNativeTarget section */ 138 | F9BC773D1DEB271200F9190F /* KernelModeSparseFile */ = { 139 | isa = PBXNativeTarget; 140 | buildConfigurationList = F9BC774F1DEB271200F9190F /* Build configuration list for PBXNativeTarget "KernelModeSparseFile" */; 141 | buildPhases = ( 142 | F9BC77381DEB271200F9190F /* Sources */, 143 | F9BC77391DEB271200F9190F /* Frameworks */, 144 | F9BC773A1DEB271200F9190F /* Headers */, 145 | F9BC773B1DEB271200F9190F /* Resources */, 146 | F9BC773C1DEB271200F9190F /* Rez */, 147 | ); 148 | buildRules = ( 149 | ); 150 | dependencies = ( 151 | ); 152 | name = KernelModeSparseFile; 153 | productName = KernelModeSparseFile; 154 | productReference = F9BC773E1DEB271200F9190F /* KernelModeSparseFile.kext */; 155 | productType = "com.apple.product-type.kernel-extension"; 156 | }; 157 | /* End PBXNativeTarget section */ 158 | 159 | /* Begin PBXProject section */ 160 | F9BC77341DEB271200F9190F /* Project object */ = { 161 | isa = PBXProject; 162 | attributes = { 163 | LastUpgradeCheck = 0510; 164 | ORGANIZATIONNAME = "Slava-Imameev"; 165 | }; 166 | buildConfigurationList = F9BC77371DEB271200F9190F /* Build configuration list for PBXProject "KernelModeSparseFile" */; 167 | compatibilityVersion = "Xcode 3.2"; 168 | developmentRegion = English; 169 | hasScannedForEncodings = 0; 170 | knownRegions = ( 171 | en, 172 | ); 173 | mainGroup = F9BC77331DEB271200F9190F; 174 | productRefGroup = F9BC773F1DEB271200F9190F /* Products */; 175 | projectDirPath = ""; 176 | projectRoot = ""; 177 | targets = ( 178 | F9BC773D1DEB271200F9190F /* KernelModeSparseFile */, 179 | ); 180 | }; 181 | /* End PBXProject section */ 182 | 183 | /* Begin PBXResourcesBuildPhase section */ 184 | F9BC773B1DEB271200F9190F /* Resources */ = { 185 | isa = PBXResourcesBuildPhase; 186 | buildActionMask = 2147483647; 187 | files = ( 188 | F9BC77481DEB271200F9190F /* InfoPlist.strings in Resources */, 189 | ); 190 | runOnlyForDeploymentPostprocessing = 0; 191 | }; 192 | /* End PBXResourcesBuildPhase section */ 193 | 194 | /* Begin PBXRezBuildPhase section */ 195 | F9BC773C1DEB271200F9190F /* Rez */ = { 196 | isa = PBXRezBuildPhase; 197 | buildActionMask = 2147483647; 198 | files = ( 199 | ); 200 | runOnlyForDeploymentPostprocessing = 0; 201 | }; 202 | /* End PBXRezBuildPhase section */ 203 | 204 | /* Begin PBXSourcesBuildPhase section */ 205 | F9BC77381DEB271200F9190F /* Sources */ = { 206 | isa = PBXSourcesBuildPhase; 207 | buildActionMask = 2147483647; 208 | files = ( 209 | F9BC77611DEB274000F9190F /* DldSupportingCode.cpp in Sources */, 210 | F9BC775D1DEB274000F9190F /* DldCommonHashTable.cpp in Sources */, 211 | F9BC775F1DEB274000F9190F /* DldSparseFile.cpp in Sources */, 212 | F9BC77631DEB274000F9190F /* DldUndocumentedQuirks.cpp in Sources */, 213 | ); 214 | runOnlyForDeploymentPostprocessing = 0; 215 | }; 216 | /* End PBXSourcesBuildPhase section */ 217 | 218 | /* Begin PBXVariantGroup section */ 219 | F9BC77461DEB271200F9190F /* InfoPlist.strings */ = { 220 | isa = PBXVariantGroup; 221 | children = ( 222 | F9BC77471DEB271200F9190F /* en */, 223 | ); 224 | name = InfoPlist.strings; 225 | sourceTree = ""; 226 | }; 227 | /* End PBXVariantGroup section */ 228 | 229 | /* Begin XCBuildConfiguration section */ 230 | F9BC774D1DEB271200F9190F /* Debug */ = { 231 | isa = XCBuildConfiguration; 232 | buildSettings = { 233 | ALWAYS_SEARCH_USER_PATHS = NO; 234 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 235 | CLANG_CXX_LIBRARY = "libc++"; 236 | CLANG_ENABLE_MODULES = YES; 237 | CLANG_ENABLE_OBJC_ARC = YES; 238 | CLANG_WARN_BOOL_CONVERSION = YES; 239 | CLANG_WARN_CONSTANT_CONVERSION = YES; 240 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 241 | CLANG_WARN_EMPTY_BODY = YES; 242 | CLANG_WARN_ENUM_CONVERSION = YES; 243 | CLANG_WARN_INT_CONVERSION = YES; 244 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 245 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 246 | COPY_PHASE_STRIP = NO; 247 | GCC_C_LANGUAGE_STANDARD = gnu99; 248 | GCC_DYNAMIC_NO_PIC = NO; 249 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 250 | GCC_OPTIMIZATION_LEVEL = 0; 251 | GCC_PREPROCESSOR_DEFINITIONS = ( 252 | "DEBUG=1", 253 | "$(inherited)", 254 | DBG, 255 | ); 256 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 257 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 258 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 259 | GCC_WARN_UNDECLARED_SELECTOR = YES; 260 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 261 | GCC_WARN_UNUSED_FUNCTION = YES; 262 | GCC_WARN_UNUSED_VARIABLE = YES; 263 | MACOSX_DEPLOYMENT_TARGET = 10.9; 264 | ONLY_ACTIVE_ARCH = YES; 265 | SDKROOT = macosx; 266 | }; 267 | name = Debug; 268 | }; 269 | F9BC774E1DEB271200F9190F /* Release */ = { 270 | isa = XCBuildConfiguration; 271 | buildSettings = { 272 | ALWAYS_SEARCH_USER_PATHS = NO; 273 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 274 | CLANG_CXX_LIBRARY = "libc++"; 275 | CLANG_ENABLE_MODULES = YES; 276 | CLANG_ENABLE_OBJC_ARC = YES; 277 | CLANG_WARN_BOOL_CONVERSION = YES; 278 | CLANG_WARN_CONSTANT_CONVERSION = YES; 279 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 280 | CLANG_WARN_EMPTY_BODY = YES; 281 | CLANG_WARN_ENUM_CONVERSION = YES; 282 | CLANG_WARN_INT_CONVERSION = YES; 283 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 284 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 285 | COPY_PHASE_STRIP = YES; 286 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 287 | ENABLE_NS_ASSERTIONS = NO; 288 | GCC_C_LANGUAGE_STANDARD = gnu99; 289 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 290 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 291 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 292 | GCC_WARN_UNDECLARED_SELECTOR = YES; 293 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 294 | GCC_WARN_UNUSED_FUNCTION = YES; 295 | GCC_WARN_UNUSED_VARIABLE = YES; 296 | MACOSX_DEPLOYMENT_TARGET = 10.9; 297 | SDKROOT = macosx; 298 | }; 299 | name = Release; 300 | }; 301 | F9BC77501DEB271200F9190F /* Debug */ = { 302 | isa = XCBuildConfiguration; 303 | buildSettings = { 304 | COMBINE_HIDPI_IMAGES = YES; 305 | CURRENT_PROJECT_VERSION = 1.0.0d1; 306 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 307 | GCC_PREFIX_HEADER = "KernelModeSparseFile/KernelModeSparseFile-Prefix.pch"; 308 | INFOPLIST_FILE = "KernelModeSparseFile/KernelModeSparseFile-Info.plist"; 309 | MODULE_NAME = "Slava-Imameev.KernelModeSparseFile"; 310 | MODULE_VERSION = 1.0.0d1; 311 | PRODUCT_NAME = "$(TARGET_NAME)"; 312 | WRAPPER_EXTENSION = kext; 313 | }; 314 | name = Debug; 315 | }; 316 | F9BC77511DEB271200F9190F /* Release */ = { 317 | isa = XCBuildConfiguration; 318 | buildSettings = { 319 | COMBINE_HIDPI_IMAGES = YES; 320 | CURRENT_PROJECT_VERSION = 1.0.0d1; 321 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 322 | GCC_PREFIX_HEADER = "KernelModeSparseFile/KernelModeSparseFile-Prefix.pch"; 323 | INFOPLIST_FILE = "KernelModeSparseFile/KernelModeSparseFile-Info.plist"; 324 | MODULE_NAME = "Slava-Imameev.KernelModeSparseFile"; 325 | MODULE_VERSION = 1.0.0d1; 326 | PRODUCT_NAME = "$(TARGET_NAME)"; 327 | WRAPPER_EXTENSION = kext; 328 | }; 329 | name = Release; 330 | }; 331 | /* End XCBuildConfiguration section */ 332 | 333 | /* Begin XCConfigurationList section */ 334 | F9BC77371DEB271200F9190F /* Build configuration list for PBXProject "KernelModeSparseFile" */ = { 335 | isa = XCConfigurationList; 336 | buildConfigurations = ( 337 | F9BC774D1DEB271200F9190F /* Debug */, 338 | F9BC774E1DEB271200F9190F /* Release */, 339 | ); 340 | defaultConfigurationIsVisible = 0; 341 | defaultConfigurationName = Release; 342 | }; 343 | F9BC774F1DEB271200F9190F /* Build configuration list for PBXNativeTarget "KernelModeSparseFile" */ = { 344 | isa = XCConfigurationList; 345 | buildConfigurations = ( 346 | F9BC77501DEB271200F9190F /* Debug */, 347 | F9BC77511DEB271200F9190F /* Release */, 348 | ); 349 | defaultConfigurationIsVisible = 0; 350 | defaultConfigurationName = Release; 351 | }; 352 | /* End XCConfigurationList section */ 353 | }; 354 | rootObject = F9BC77341DEB271200F9190F /* Project object */; 355 | } 356 | -------------------------------------------------------------------------------- /src/DldCommonHashTable.h: -------------------------------------------------------------------------------- 1 | /* 2 | based on Simon Kagstrom's C hash table implementation 3 | Copyright (C) 2001-2005, Simon Kagstrom 4 | Copyright (C) 2010, Slava Imameev 5 | */ 6 | 7 | #include "DldCommon.h" 8 | #include 9 | #include 10 | 11 | /** 12 | * @file 13 | * libghthash is a generic hash table used for storing arbitrary 14 | * data. 15 | * 16 | * Libghthash really stores pointers to data - the hash 17 | * table knows nothing about the actual type of the data. 18 | * 19 | * A simple example to get started can be found in the 20 | * example/simple.c file found in the distribution. 21 | * hash_test.c provides a more comlpete example. 22 | * 23 | * Some basic properties of the hash table are: 24 | * 25 | * - Both the data stored and the keys are of void type, which 26 | * means that you can store any kind of data. 27 | * 28 | * - The only functions you probably will need to start is: 29 | * - ght_create(), which creates a new hash table. 30 | * - ght_insert(), which inserts a new entry into a table. 31 | * - ght_get(), which searches for an entry. 32 | * - ght_remove(), which removes and entry. 33 | * - ght_finalize(), which destroys a hash table. 34 | * 35 | * - Inserting entries is done without first creating a key, 36 | * i.e. you insert with the data, the datasize, the key and the 37 | * key size directly. 38 | * 39 | * - The hash table copies the key data when inserting new 40 | * entries. This means that you should not malloc() the key 41 | * before inserting a new entry. 42 | * 43 | */ 44 | #ifndef DLDCOMMONHASHTABLE_H 45 | #define DLDCOMMONHASHTABLE_H 46 | 47 | 48 | #define GHT_HEURISTICS_NONE 0 49 | #define GHT_HEURISTICS_TRANSPOSE 1 50 | #define GHT_HEURISTICS_MOVE_TO_FRONT 2 51 | #define GHT_AUTOMATIC_REHASH 4 52 | 53 | #ifndef TRUE 54 | #define TRUE 1 55 | #endif 56 | 57 | #ifndef FALSE 58 | #define FALSE 0 59 | #endif 60 | 61 | typedef enum _GHT_STATUS_CODE{ 62 | GHT_OK = 0x0, 63 | GHT_ALREADY_IN_HASH = (int)(-2), 64 | GHT_ERROR = (int)(-1) 65 | } GHT_STATUS_CODE; 66 | 67 | /** unsigned 32 bit integer. */ 68 | typedef unsigned int ght_uint32_t; 69 | 70 | /** 71 | * The structure for hash keys. You should not care about this 72 | * structure unless you plan to write your own hash functions. 73 | */ 74 | typedef struct s_hash_key 75 | { 76 | unsigned int i_size; /**< The size in bytes of the key p_key */ 77 | const void* p_key; /**< A pointer to the key. */ 78 | } ght_hash_key_t; 79 | 80 | /* 81 | * The structure for hash entries. 82 | * 83 | * LOCK: Should be possible to do somewhat atomically 84 | */ 85 | typedef struct s_hash_entry 86 | { 87 | #if defined( DBG ) 88 | // 89 | // a shadow copy of the key 90 | // 91 | ght_hash_key_t key_shadow; 92 | 93 | #endif//DBG 94 | // 95 | // put the most usable fields on a single cache line 96 | // 97 | struct s_hash_entry* p_next; 98 | struct s_hash_entry* p_prev; 99 | ght_hash_key_t key; 100 | void* p_data; 101 | 102 | struct s_hash_entry* p_older; 103 | struct s_hash_entry* p_newer; 104 | 105 | // 106 | // size of the alocation = sizeof(ght_hash_entry_t) + key_size 107 | // 108 | size_t size; 109 | 110 | } ght_hash_entry_t; 111 | 112 | /* 113 | * The structure used in iterations. You should not care about the 114 | * contents of this, it will be filled and updated by ght_first() and 115 | * ght_next(). 116 | */ 117 | typedef struct 118 | { 119 | ght_hash_entry_t *p_entry; /* The current entry */ 120 | ght_hash_entry_t *p_next; /* The next entry */ 121 | } ght_iterator_t; 122 | 123 | /** 124 | * Definition of the hash function pointers. @c ght_fn_hash_t should be 125 | * used when implementing new hash functions. Look at the supplied 126 | * hash functions, like @c ght_one_at_a_time_hash(), for examples of hash 127 | * functions. 128 | * 129 | * @param p_key the key to calculate the hash value for. 130 | * 131 | * @return a 32 bit hash value. 132 | * 133 | * @see @c ght_one_at_a_time_hash(), @c ght_rotating_hash(), 134 | * @c ght_crc_hash() 135 | */ 136 | typedef ght_uint32_t (*ght_fn_hash_t)(ght_hash_key_t *p_key); 137 | 138 | __BEGIN_DECLS 139 | 140 | /** 141 | * Definition of the allocation function pointers. This is simply the 142 | * same definition as @c mac_kalloc(). 143 | * 144 | * @param size the size to allocate. This will always be 145 | * sizeof(ght_hash_entry_t) + key_size. 146 | * 147 | * @param how - one of the flags 148 | * M_WAITOK 0x0000 149 | * M_NOWAIT 0x0001 150 | * 151 | * @return a pointer to the allocated region, or NULL if the 152 | * allocation failed. 153 | */ 154 | typedef void *(*ght_fn_alloc_t)(vm_size_t size, int how); 155 | 156 | /** 157 | * Definition of the deallocation function pointers. This is simply the 158 | * same definition as @c mac_kfree(). 159 | * 160 | * @param ptr a pointer to the region to free. 161 | */ 162 | typedef void (*ght_fn_free_t)(void *ptr, vm_size_t size ); 163 | 164 | __END_DECLS 165 | 166 | /** 167 | * Definition of bounded bucket free callback function pointers. 168 | * 169 | * The keys is passed back as const, since it was accepted by ght_insert() 170 | * as const, but if the callback function knows that a non-const pointer 171 | * was passed in, it can cast it back to non-const. 172 | */ 173 | typedef void (*ght_fn_bucket_free_callback_t)(void *data, const void *key); 174 | 175 | /** 176 | * The hash table structure. 177 | */ 178 | typedef struct 179 | { 180 | unsigned int i_items; /**< The current number of items in the table */ 181 | unsigned int i_size; /**< The number of buckets */ 182 | ght_fn_hash_t fn_hash; /**< The hash function used */ 183 | ght_fn_alloc_t fn_alloc; /**< The function used for allocating entries */ 184 | ght_fn_free_t fn_free; /**< The function used for freeing entries */ 185 | ght_fn_bucket_free_callback_t fn_bucket_free; /**< The function called when a bucket overflows */ 186 | int i_heuristics; /**< The type of heuristics used */ 187 | int i_automatic_rehash; /**< TRUE if automatic rehashing is used */ 188 | 189 | /* private: */ 190 | ght_hash_entry_t **pp_entries; 191 | int *p_nr; /* The number of entries in each bucket */ 192 | int i_size_mask; /* The number of bits used in the size */ 193 | unsigned int bucket_limit; 194 | 195 | bool non_block; /* TRUE if the allocations shoud not block */ 196 | 197 | ght_hash_entry_t *p_oldest; /* The entry inserted the earliest. */ 198 | ght_hash_entry_t *p_newest; /* The entry inserted the latest. */ 199 | } ght_hash_table_t; 200 | 201 | /** 202 | * Create a new hash table. The number of buckets should be about as 203 | * big as the number of elements you wish to store in the table for 204 | * good performance. The number of buckets is rounded to the next 205 | * higher power of two. 206 | * 207 | * The hash table is created with @c ght_one_at_a_time_hash() as hash 208 | * function, automatic rehashing disabled, @c malloc() as the memory 209 | * allocator and no heuristics. 210 | * 211 | * @param i_size the number of buckets in the hash table. Giving a 212 | * non-power of two here will round the size up to the next 213 | * power of two. 214 | * 215 | * @see ght_set_hash(), ght_set_heuristics(), ght_set_rehash(), 216 | * @see ght_set_alloc() 217 | * 218 | * @return a pointer to the hash table or NULL upon error. 219 | */ 220 | ght_hash_table_t* 221 | ght_create( 222 | __in unsigned int i_size, 223 | __in bool non_block 224 | ); 225 | 226 | /** 227 | * Set the allocation/freeing functions to use for a hash table. The 228 | * allocation function will only be called when a new entry is 229 | * inserted. 230 | * 231 | * The allocation size will always be sizeof(ght_hash_entry_t) + 232 | * sizeof(ght_hash_key_t) + key_size. The actual size varies with 233 | * the key size. 234 | * 235 | * If this function is not called, @c malloc() and @c free() 236 | * will be used for allocation and freeing. 237 | * 238 | * @warning Always call this function before any entries are 239 | * inserted into the table. Otherwise, the new free() might be called 240 | * on something that were allocated with another allocation function. 241 | * 242 | * @param p_ht the hash table to set the memory management functions 243 | * for. 244 | * @param fn_alloc the allocation function to use. 245 | * @param fn_free the deallocation function to use. 246 | */ 247 | void ght_set_alloc(ght_hash_table_t *p_ht, ght_fn_alloc_t fn_alloc, ght_fn_free_t fn_free); 248 | 249 | /** 250 | * Set the hash function to use for a hash table. 251 | * 252 | * @warning Always call this function before any entries are inserted 253 | * into the table. Otherwise, it will not be possible to find entries 254 | * that were inserted before this function was called. 255 | * 256 | * @param p_ht the hash table set the hash function for. 257 | * @param fn_hash the hash function. 258 | */ 259 | void ght_set_hash(ght_hash_table_t *p_ht, ght_fn_hash_t fn_hash); 260 | 261 | /** 262 | * Set the heuristics to use for the hash table. The possible values are: 263 | * 264 | * - GHT_HEURISTICS_NONE: Don't use any heuristics. 265 | * - 0: Same as above. 266 | * - GHT_HEURISTICS_TRANSPOSE: Use transposing heuristics. An 267 | * accessed element will move one step up in the bucket-list with this 268 | * method. 269 | * - GHT_HEURISTICS_MOVE_TO_FRONT: Use move-to-front 270 | * heuristics. An accessed element will be moved the front of the 271 | * bucket list with this method. 272 | * 273 | * @param p_ht the hash table set the heuristics for. 274 | * @param i_heuristics the heuristics to use. 275 | */ 276 | void ght_set_heuristics(ght_hash_table_t *p_ht, int i_heuristics); 277 | 278 | /** 279 | * Enable or disable automatic rehashing. 280 | * 281 | * With automatic rehashing, the table will rehash itself when the 282 | * number of elements in the table are twice as many as the number of 283 | * buckets. You should note that automatic rehashing will cause your 284 | * application to be really slow when the table is rehashing (which 285 | * might happen at times when you need speed), you should therefore be 286 | * careful with this in time-constrainted applications. 287 | * 288 | * @param p_ht the hash table to set rehashing for. 289 | * @param b_rehash TRUE if rehashing should be used or FALSE if it 290 | * should not be used. 291 | */ 292 | void ght_set_rehash(ght_hash_table_t *p_ht, int b_rehash); 293 | 294 | /** 295 | * Enable or disable bounded buckets. 296 | * 297 | * With bounded buckets, the hash table will act as a cache, only 298 | * holding a fixed number of elements per bucket. @a limit specifies 299 | * the limit of elements per bucket. When inserting elements with @a 300 | * ght_insert into a bounded table, the last entry in the bucket chain 301 | * will be free:d. libghthash will then call the callback function @a 302 | * fn, which allow the user of the library to dispose of the key and data. 303 | * 304 | * Bounded buckets are disabled by default. 305 | * 306 | * @param p_ht the hash table to set the bounded buckets for. 307 | * @param limit the maximum number of items in each bucket. If @a 308 | * limit is set to 0, bounded buckets are disabled. 309 | * @param fn a pointer to a callback function that is called when an 310 | * entry is free:d. The function should return 0 if the entry can be 311 | * freed, or -1 otherwise. If -1 is returned, libghthash will select 312 | * the second last entry and call the callback with that instead. 313 | */ 314 | void ght_set_bounded_buckets(ght_hash_table_t *p_ht, unsigned int limit, ght_fn_bucket_free_callback_t fn); 315 | 316 | 317 | /** 318 | * Get the size (the number of items) of the hash table. 319 | * 320 | * @param p_ht the hash table to get the size for. 321 | * 322 | * @return the number of items in the hash table. 323 | */ 324 | unsigned int ght_size(ght_hash_table_t *p_ht); 325 | 326 | /** 327 | * Get the table size (the number of buckets) of the hash table. 328 | * 329 | * @param p_ht the hash table to get the table size for. 330 | * 331 | * @return the number of buckets in the hash table. 332 | */ 333 | unsigned int ght_table_size(ght_hash_table_t *p_ht); 334 | 335 | 336 | /** 337 | * Insert an entry into the hash table. Prior to inserting anything, 338 | * make sure that the table is created with ght_create(). If an 339 | * element with the same key as this one already exists in the table, 340 | * the insertion will fail and -1 is returned. 341 | * 342 | * A typical example is shown below, where the string "blabla" 343 | * (including the '\0'-terminator) is used as a key for the integer 344 | * 15. 345 | * 346 | *
347 |  * ght_hash_table_t *p_table;
348 |  * char *p_key_data;
349 |  * int *p_data;
350 |  * int ret;
351 |  *
352 |  * [Create p_table etc...]
353 |  * p_data = malloc(sizeof(int));
354 |  * p_key_data = "blabla";
355 |  * *p_data = 15;
356 |  *
357 |  * ret = ght_insert(p_table,
358 |  *                  p_data,
359 |  *                  sizeof(char)*(strlen(p_key_data)+1), p_key_data);
360 |  * 
361 | * 362 | * @param p_ht the hash table to insert into. 363 | * @param p_entry_data the data to insert. 364 | * @param i_key_size the size of the key to associate the data with (in bytes). 365 | * @param p_key_data the key to use. The value will be copied, and it 366 | * is therefore OK to use a stack-allocated entry here. 367 | * 368 | * @returns 369 | * 0 if the element could be inserted, 370 | * -2 if the elemnt is already in the hash 371 | * -1 if there is an error ( mac_kalloc failed ) 372 | */ 373 | GHT_STATUS_CODE 374 | ght_insert( 375 | __in ght_hash_table_t *p_ht, 376 | __in void *p_entry_data, 377 | __in unsigned int i_key_size, 378 | __in const void *p_key_data 379 | ); 380 | 381 | /** 382 | * Replace an entry in the hash table. This function will return an 383 | * error if the entry to be replaced does not exist, i.e. it cannot be 384 | * used to insert new entries. Replacing an entry does not affect its 385 | * iteration order. 386 | * 387 | * @param p_ht the hash table to search in. 388 | * @param p_entry_data the new data for the key. 389 | * @param i_key_size the size of the key to search with (in bytes). 390 | * @param p_key_data the key to search for. 391 | * 392 | * @return a pointer to the old value or NULL if the operation failed. 393 | */ 394 | void *ght_replace(ght_hash_table_t *p_ht, 395 | void *p_entry_data, 396 | unsigned int i_key_size, const void *p_key_data); 397 | 398 | 399 | /** 400 | * Lookup an entry in the hash table. The entry is not removed from 401 | * the table. 402 | * 403 | * @param p_ht the hash table to search in. 404 | * @param i_key_size the size of the key to search with (in bytes). 405 | * @param p_key_data the key to search for. 406 | * 407 | * @return a pointer to the found entry or NULL if no entry could be found. 408 | */ 409 | void* 410 | ght_get( 411 | __in ght_hash_table_t *p_ht, 412 | __in unsigned int i_key_size, 413 | __in const void *p_key_data 414 | ); 415 | 416 | /** 417 | * Remove an entry from the hash table. The entry is removed from the 418 | * table, but not freed (that is, the data stored is not freed). 419 | * 420 | * @param p_ht the hash table to use. 421 | * @param i_key_size the size of the key to search with (in bytes). 422 | * @param p_key_data the key to search for. 423 | * 424 | * @return a pointer to the removed entry or NULL if the entry could be found. 425 | */ 426 | void* 427 | ght_remove( 428 | __in ght_hash_table_t *p_ht, 429 | __in unsigned int i_key_size, 430 | __in const void *p_key_data 431 | ); 432 | 433 | /** 434 | * Return the first entry in the hash table. This function should be 435 | * used for iteration and is used together with ght_next(). The order 436 | * of the entries will be from the oldest inserted entry to the newest 437 | * inserted entry. If an entry is inserted during an iteration, the entry 438 | * might or might not occur in the iteration. Note that removal during 439 | * an iteration is only safe for the current entry or an entry 440 | * which has already been iterated over. 441 | * 442 | * The use of the ght_iterator_t allows for several concurrent 443 | * iterations, where you would use one ght_iterator_t for each 444 | * iteration. In threaded environments, you should still lock access 445 | * to the hash table for insertion and removal. 446 | * 447 | * A typical example might look as follows: 448 | *
449 |  * ght_hash_table_t *p_table;
450 |  * ght_iterator_t iterator;
451 |  * void *p_key;
452 |  * void *p_e;
453 |  *
454 |  * [Create table etc...]
455 |  * for(p_e = ght_first(p_table, &iterator, &p_key); p_e; p_e = ght_next(p_table, &iterator, &p_key))
456 |  *   {
457 |  *      [Do something with the current entry p_e and it's key p_key]
458 |  *   }
459 |  * 
460 | * 461 | * @param p_ht the hash table to iterate through. 462 | * 463 | * @param p_iterator the iterator to use. The value of the structure 464 | * is filled in by this function and may be stack allocated. 465 | * 466 | * @param pp_key a pointer to the pointer of the key (NULL if none). 467 | * 468 | * @return a pointer to the first entry in the table or NULL if there 469 | * are no entries. 470 | * 471 | * 472 | * @see ght_next() 473 | */ 474 | void *ght_first(ght_hash_table_t *p_ht, ght_iterator_t *p_iterator, const void **pp_key); 475 | 476 | /** 477 | * See ght_first() detailed description. This function augments 478 | * ght_first() by providing a facility to get the size of the keys 479 | * also. This interface is beneficial for hashtables which use 480 | * variable length keys. 481 | * 482 | * @param p_ht the hash table to iterate through. 483 | * 484 | * @param p_iterator the iterator to use. The value of the structure 485 | * is filled in by this function and may be stack allocated. 486 | * 487 | * @param pp_key a pointer to the pointer of the key (NULL if none). 488 | * 489 | * @param size a pointer to the size of the key pointer to by pp_key. 490 | * 491 | * @return a pointer to the first entry in the table or NULL if there 492 | * are no entries. 493 | * 494 | * 495 | * @see ght_next() 496 | */ 497 | 498 | void *ght_first_keysize(ght_hash_table_t *p_ht, ght_iterator_t *p_iterator, const void **pp_key, unsigned int *size); 499 | 500 | /** 501 | * Return the next entry in the hash table. This function should be 502 | * used for iteration, and must be called after ght_first(). 503 | * 504 | * @warning calling this without first having called ght_first will 505 | * give undefined results (probably a crash), since p_iterator isn't 506 | * filled correctly. 507 | * 508 | * @param p_ht the hash table to iterate through. 509 | * 510 | * @param p_iterator the iterator to use. 511 | * 512 | * @param pp_key a pointer to the pointer of the key (NULL if none). 513 | * 514 | * @return a pointer to the next entry in the table or NULL if there 515 | * are no more entries in the table. 516 | * 517 | * @see ght_first() 518 | */ 519 | void *ght_next(ght_hash_table_t *p_ht, ght_iterator_t *p_iterator, const void **pp_key); 520 | 521 | /** 522 | * This functions works just like ght_next() but also returns the 523 | * keysize. This is beneficial for users of the hash table which use 524 | * variable length keys. 525 | * 526 | * @warning calling this without first having called ght_first will 527 | * give undefined results (probably a crash), since p_iterator isn't 528 | * filled correctly. 529 | * 530 | * @param p_ht the hash table to iterate through. 531 | * 532 | * @param p_iterator the iterator to use. 533 | * 534 | * @param pp_key a pointer to the pointer of the key (NULL if none). 535 | * 536 | * @param size a pointer to the size of the key pointer to by pp_key. 537 | * 538 | * @return a pointer to the next entry in the table or NULL if there 539 | * are no more entries in the table. 540 | * 541 | * @see ght_first_keysize() 542 | */ 543 | 544 | void *ght_next_keysize(ght_hash_table_t *p_ht, ght_iterator_t *p_iterator, const void **pp_key, unsigned int *size); 545 | 546 | /** 547 | * Rehash the hash table. 548 | * 549 | * Rehashing will change the size of the hash table, retaining all 550 | * elements. This is very costly and should be avoided unless really 551 | * needed. If GHT_AUTOMATIC_REHASH is specified in the flag 552 | * parameter when ght_create() is called, the hash table is 553 | * automatically rehashed when the number of stored elements exceeds 554 | * two times the number of buckets in the table (making calls to this 555 | * function unessessary). 556 | * 557 | * @param p_ht the hash table to rehash. 558 | * @param i_size the new size of the table. 559 | * 560 | * @see ght_create() 561 | */ 562 | void ght_rehash(ght_hash_table_t *p_ht, unsigned int i_size); 563 | 564 | /** 565 | * Free the hash table. ght_finalize() should typically be called 566 | * at the end of the program. Note that only the metadata and the keys 567 | * of the table is freed, not the entries. If you want to free the 568 | * entries when removing the table, the entries will have to be 569 | * manually freed before ght_finalize() is called like: 570 | * 571 | *
572 |  * ght_iterator_t iterator;
573 |  * void *p_key;
574 |  * void *p_e;
575 |  *
576 |  * for(p_e = ght_first(p_table, &iterator, &p_key); p_e; p_e = ght_next(p_table, &iterator, &p_key))
577 |  *   {
578 |  *     free(p_e);
579 |  *   }
580 |  *
581 |  * ght_finalize(p_table);
582 |  * 
583 | * 584 | * @param p_ht the table to remove. 585 | */ 586 | void ght_finalize(ght_hash_table_t *p_ht); 587 | 588 | /* exported hash functions */ 589 | 590 | /** 591 | * One-at-a-time-hash. One-at-a-time-hash is a good hash function, and 592 | * is the default when ght_create() is called with NULL as the 593 | * fn_hash parameter. This was found in a DrDobbs article, see 594 | * http://burtleburtle.net/bob/hash/doobs.html 595 | * 596 | * @warning Don't call this function directly, it is only meant to be 597 | * used as a callback for the hash table. 598 | * 599 | * @see ght_fn_hash_t 600 | * @see ght_rotating_hash(), ght_crc_hash() 601 | */ 602 | ght_uint32_t ght_one_at_a_time_hash(ght_hash_key_t *p_key); 603 | 604 | /** 605 | * Rotating hash. Not so good hash function. This was found in a 606 | * DrDobbs article, see http://burtleburtle.net/bob/hash/doobs.html 607 | * 608 | * @warning Don't call this function directly, it is only meant to be 609 | * used as a callback for the hash table. 610 | * 611 | * @see ght_fn_hash_t 612 | * @see ght_one_at_a_time_hash(), ght_crc_hash() 613 | */ 614 | ght_uint32_t ght_rotating_hash(ght_hash_key_t *p_key); 615 | 616 | /** 617 | * CRC32 hash. CRC32 hash is a good hash function. This came from Dru 618 | * Lemley . 619 | * 620 | * @warning Don't call this function directly, it is only meant to be 621 | * used as a callback for the hash table. 622 | * 623 | * @see ght_fn_hash_t 624 | * @see ght_one_at_a_time_hash(), ght_rotating_hash() 625 | */ 626 | ght_uint32_t ght_crc_hash(ght_hash_key_t *p_key); 627 | 628 | #ifdef USE_PROFILING 629 | /** 630 | * Print some statistics about the table. Only available if the 631 | * library was compiled with USE_PROFILING defined. 632 | */ 633 | void ght_print(ght_hash_table_t *p_ht); 634 | #endif 635 | 636 | #endif//DLDCOMMONHASHTABLE_H 637 | -------------------------------------------------------------------------------- /src/DldCommonHashTable.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | based on Simon Kagstrom's C hash table implementation 3 | Copyright (C) 2001-2005, Simon Kagstrom 4 | 2010, Slava Imameev - porting to Mac OS X kernel 5 | */ 6 | 7 | #include "DldCommonHashTable.h" 8 | 9 | //-------------------------------------------------------------------- 10 | 11 | __BEGIN_DECLS 12 | /* 13 | * Kernel Memory allocator 14 | */ 15 | void * mac_kalloc (vm_size_t size, int how); 16 | void mac_kfree (void *data, vm_size_t size); 17 | __END_DECLS 18 | 19 | //-------------------------------------------------------------------- 20 | 21 | /* Flags for the elements. This is currently unused. */ 22 | #define FLAGS_NONE 0 /* No flags */ 23 | #define FLAGS_NORMAL 0 /* Normal item. All user-inserted stuff is normal */ 24 | #define FLAGS_INTERNAL 1 /* The item is internal to the hash table */ 25 | 26 | /* Prototypes */ 27 | static inline void transpose(ght_hash_table_t *p_ht, ght_uint32_t l_bucket, ght_hash_entry_t *p_entry); 28 | static inline void move_to_front(ght_hash_table_t *p_ht, ght_uint32_t l_bucket, ght_hash_entry_t *p_entry); 29 | static inline void free_entry_chain(ght_hash_table_t *p_ht, ght_hash_entry_t *p_entry); 30 | 31 | #if !defined( DBG ) 32 | static inline 33 | #endif//!DBG 34 | ght_hash_entry_t *search_in_bucket(ght_hash_table_t *p_ht, ght_uint32_t l_bucket, ght_hash_key_t *p_key, unsigned char i_heuristics); 35 | 36 | static inline void hk_fill(ght_hash_key_t *p_hk, int i_size, const void *p_key); 37 | static inline ght_hash_entry_t *he_create(ght_hash_table_t *p_ht, void *p_data, unsigned int i_key_size, const void *p_key_data); 38 | static inline void he_finalize(ght_hash_table_t *p_ht, ght_hash_entry_t *p_he); 39 | 40 | //-------------------------------------------------------------------- 41 | 42 | /* --- private methods --- */ 43 | 44 | /* Move p_entry one up in its list. */ 45 | static inline void transpose(ght_hash_table_t *p_ht, ght_uint32_t l_bucket, ght_hash_entry_t *p_entry) 46 | { 47 | /* 48 | * __ __ __ __ 49 | * |A_|->|X_|->|Y_|->|B_| 50 | * / 51 | * => p_entry 52 | * __ __/ __ __ 53 | * |A_|->|Y_|->|X_|->|B_| 54 | */ 55 | if (p_entry->p_prev) /* Otherwise p_entry is already first. */ 56 | { 57 | ght_hash_entry_t *p_x = p_entry->p_prev; 58 | ght_hash_entry_t *p_a = p_x?p_x->p_prev:NULL; 59 | ght_hash_entry_t *p_b = p_entry->p_next; 60 | 61 | if (p_a) 62 | { 63 | p_a->p_next = p_entry; 64 | } 65 | else /* This element is now placed first */ 66 | { 67 | p_ht->pp_entries[l_bucket] = p_entry; 68 | } 69 | 70 | if (p_b) 71 | { 72 | p_b->p_prev = p_x; 73 | } 74 | if (p_x) 75 | { 76 | p_x->p_next = p_entry->p_next; 77 | p_x->p_prev = p_entry; 78 | } 79 | p_entry->p_next = p_x; 80 | p_entry->p_prev = p_a; 81 | } 82 | } 83 | 84 | //-------------------------------------------------------------------- 85 | 86 | /* Move p_entry first */ 87 | static inline void move_to_front(ght_hash_table_t *p_ht, ght_uint32_t l_bucket, ght_hash_entry_t *p_entry) 88 | { 89 | /* 90 | * __ __ __ 91 | * |A_|->|B_|->|X_| 92 | * / 93 | * => p_entry 94 | * __/ __ __ 95 | * |X_|->|A_|->|B_| 96 | */ 97 | if (p_entry == p_ht->pp_entries[l_bucket]) 98 | { 99 | return; 100 | } 101 | 102 | /* Link p_entry out of the list. */ 103 | p_entry->p_prev->p_next = p_entry->p_next; 104 | if (p_entry->p_next) 105 | { 106 | p_entry->p_next->p_prev = p_entry->p_prev; 107 | } 108 | 109 | /* Place p_entry first */ 110 | p_entry->p_next = p_ht->pp_entries[l_bucket]; 111 | p_entry->p_prev = NULL; 112 | p_ht->pp_entries[l_bucket]->p_prev = p_entry; 113 | p_ht->pp_entries[l_bucket] = p_entry; 114 | } 115 | 116 | //-------------------------------------------------------------------- 117 | 118 | static inline void remove_from_chain(ght_hash_table_t *p_ht, ght_uint32_t l_bucket, ght_hash_entry_t *p) 119 | { 120 | if (p->p_prev) 121 | { 122 | p->p_prev->p_next = p->p_next; 123 | } 124 | else /* first in list */ 125 | { 126 | p_ht->pp_entries[l_bucket] = p->p_next; 127 | } 128 | if (p->p_next) 129 | { 130 | p->p_next->p_prev = p->p_prev; 131 | } 132 | 133 | if (p->p_older) 134 | { 135 | p->p_older->p_newer = p->p_newer; 136 | } 137 | else /* oldest */ 138 | { 139 | p_ht->p_oldest = p->p_newer; 140 | } 141 | if (p->p_newer) 142 | { 143 | p->p_newer->p_older = p->p_older; 144 | } 145 | else /* newest */ 146 | { 147 | p_ht->p_newest = p->p_older; 148 | } 149 | } 150 | 151 | //-------------------------------------------------------------------- 152 | 153 | /* Search for an element in a bucket */ 154 | #if !defined( DBG ) 155 | static inline 156 | #endif//!DBG 157 | ght_hash_entry_t* 158 | search_in_bucket( 159 | __in ght_hash_table_t *p_ht, 160 | __in ght_uint32_t l_bucket, 161 | __in ght_hash_key_t *p_key, 162 | __in unsigned char i_heuristics 163 | ) 164 | { 165 | ght_hash_entry_t *p_e; 166 | #if defined( DBG ) 167 | int entries = 0x0; 168 | #endif//DBG 169 | 170 | for (p_e = p_ht->pp_entries[l_bucket]; 171 | p_e; 172 | p_e = p_e->p_next) 173 | { 174 | #if defined( DBG ) 175 | ++entries; 176 | assert( p_e->key_shadow.i_size == p_e->key.i_size && 177 | 0x0 == memcmp( p_e->key.p_key, p_e->key_shadow.p_key, p_e->key_shadow.i_size ) ); 178 | #endif//DBG 179 | 180 | if( (p_e->key.i_size == p_key->i_size) && 181 | (memcmp(p_e->key.p_key, p_key->p_key, p_e->key.i_size) == 0) ) 182 | { 183 | /* Matching entry found - Apply heuristics, if any */ 184 | switch (i_heuristics) 185 | { 186 | case GHT_HEURISTICS_MOVE_TO_FRONT: 187 | move_to_front(p_ht, l_bucket, p_e); 188 | break; 189 | case GHT_HEURISTICS_TRANSPOSE: 190 | transpose(p_ht, l_bucket, p_e); 191 | break; 192 | default: 193 | break; 194 | } 195 | // 196 | // I do not have intention to insert NULL in the hash table 197 | // 198 | assert( p_e->p_data ); 199 | return p_e; 200 | } 201 | } 202 | 203 | #if defined( DBG ) 204 | assert( entries == p_ht->p_nr[ l_bucket ] ); 205 | #endif//DBG 206 | 207 | return NULL; 208 | } 209 | 210 | //-------------------------------------------------------------------- 211 | 212 | /* Free a chain of entries (in a bucket) */ 213 | static inline 214 | void 215 | free_entry_chain( 216 | __in ght_hash_table_t *p_ht, 217 | __in ght_hash_entry_t *p_entry 218 | ) 219 | { 220 | ght_hash_entry_t *p_e = p_entry; 221 | 222 | while(p_e) 223 | { 224 | ght_hash_entry_t *p_e_next = p_e->p_next; 225 | he_finalize(p_ht, p_e); 226 | p_e = p_e_next; 227 | } 228 | } 229 | 230 | //-------------------------------------------------------------------- 231 | 232 | /* Fill in the data to a existing hash key */ 233 | static inline void hk_fill(ght_hash_key_t *p_hk, int i_size, const void *p_key) 234 | { 235 | assert(p_hk); 236 | 237 | p_hk->i_size = i_size; 238 | p_hk->p_key = p_key; 239 | } 240 | 241 | //-------------------------------------------------------------------- 242 | 243 | /* Create a hash entry */ 244 | static inline 245 | ght_hash_entry_t* 246 | he_create( 247 | __in ght_hash_table_t *p_ht, 248 | __in void *p_data, 249 | __in unsigned int i_key_size, 250 | __in const void *p_key_data 251 | ) 252 | { 253 | ght_hash_entry_t *p_he; 254 | size_t size; 255 | 256 | assert( p_ht->non_block || preemption_enabled() ); 257 | 258 | /* 259 | * An element like the following is allocated: 260 | * elem->p_key 261 | * / elem->p_key->p_key_data 262 | * ____|___/________ 263 | * |elem|key|key data| 264 | * |____|___|________| 265 | * 266 | * That is, the key and the key data is stored "inline" within the 267 | * hash entry. 268 | * 269 | * This saves space since mac_kalloc only is called once and thus avoids 270 | * some fragmentation. Thanks to Dru Lemley for this idea. 271 | */ 272 | size = sizeof(ght_hash_entry_t)+i_key_size; 273 | if( !(p_he = (ght_hash_entry_t*)p_ht->fn_alloc( size, p_ht->non_block? M_NOWAIT :M_WAITOK ) ) ) 274 | { 275 | DBG_PRINT_ERROR( ( "p_he = p_ht->fn_alloc( %d, %d ) failed!\n", (int)size, p_ht->non_block? M_NOWAIT :M_WAITOK ) ); 276 | 277 | // 278 | // if the alloaction was non-blocking try the blocking one even if there is a risk of a deadlock 279 | // 280 | if( p_ht->non_block && !(p_he = (ght_hash_entry_t*)p_ht->fn_alloc( size, M_WAITOK ) ) ){ 281 | 282 | DBG_PRINT_ERROR( ( "p_he = p_ht->fn_alloc( %d, %d ) second fail!\n", (int)size, M_WAITOK ) ); 283 | return NULL; 284 | 285 | } 286 | 287 | if( !p_he ) 288 | return NULL; 289 | } 290 | 291 | #if defined( DBG ) 292 | // 293 | // allocate a shadow copy for the key, used to test the entry consistency 294 | // 295 | p_he->key_shadow.i_size = i_key_size; 296 | p_he->key_shadow.p_key = p_ht->fn_alloc( i_key_size, p_ht->non_block? M_NOWAIT :M_WAITOK ); 297 | assert( p_he->key_shadow.p_key ); 298 | if( !p_he->key_shadow.p_key ){ 299 | 300 | p_ht->fn_free( p_he, size ); 301 | return NULL; 302 | } 303 | memcpy( (void*)p_he->key_shadow.p_key, p_key_data, i_key_size ); 304 | #endif//DBG 305 | 306 | p_he->size = size; 307 | p_he->p_data = p_data; 308 | p_he->p_next = NULL; 309 | p_he->p_prev = NULL; 310 | p_he->p_older = NULL; 311 | p_he->p_newer = NULL; 312 | 313 | /* Create the key */ 314 | p_he->key.i_size = i_key_size; 315 | memcpy(p_he+1, p_key_data, i_key_size); 316 | p_he->key.p_key = (void*)(p_he+1); 317 | 318 | return p_he; 319 | } 320 | 321 | //-------------------------------------------------------------------- 322 | 323 | /* Finalize (free) a hash entry */ 324 | static inline void he_finalize(ght_hash_table_t *p_ht, ght_hash_entry_t *p_he) 325 | { 326 | assert( p_he ); 327 | assert( p_ht->non_block || preemption_enabled() ); 328 | 329 | #if defined(DBG) 330 | p_he->p_next = NULL; 331 | p_he->p_prev = NULL; 332 | p_he->p_older = NULL; 333 | p_he->p_newer = NULL; 334 | #endif /* DBG */ 335 | 336 | #if defined( DBG ) 337 | if( p_he->key_shadow.p_key ){ 338 | 339 | p_ht->fn_free( (void*)p_he->key_shadow.p_key, p_he->key_shadow.i_size ); 340 | } 341 | #endif//DBG 342 | 343 | /* Free the entry */ 344 | p_ht->fn_free( p_he, p_he->size ); 345 | } 346 | 347 | //-------------------------------------------------------------------- 348 | 349 | #if 0 350 | /* Tried this to avoid recalculating hash values by caching 351 | * them. Overhead larger than benefits. 352 | */ 353 | static inline ght_uint32_t get_hash_value(ght_hash_table_t *p_ht, ght_hash_key_t *p_key) 354 | { 355 | int i; 356 | 357 | if (p_key->i_size > sizeof(uint64_t)) 358 | return p_ht->fn_hash(p_key); 359 | 360 | /* Lookup in the hash value cache */ 361 | for (i = 0; i < GHT_N_CACHED_HASH_KEYS; i++) 362 | { 363 | if ( p_key->i_size == p_ht->cached_keys[i].key.i_size && 364 | memcmp(p_key->p_key, p_ht->cached_keys[i].key.p_key, p_key->i_size) == 0) 365 | return p_ht->cached_keys[i].hash_val; 366 | } 367 | p_ht->cur_cache_evict = (p_ht->cur_cache_evict + 1) % GHT_N_CACHED_HASH_KEYS; 368 | p_ht->cached_keys[ p_ht->cur_cache_evict ].key = *p_key; 369 | p_ht->cached_keys[ p_ht->cur_cache_evict ].hash_val = p_ht->fn_hash(p_key); 370 | 371 | return p_ht->cached_keys[ p_ht->cur_cache_evict ].hash_val; 372 | } 373 | #else 374 | # define get_hash_value(p_ht, p_key) ( (p_ht)->fn_hash(p_key) ) 375 | #endif 376 | 377 | //-------------------------------------------------------------------- 378 | 379 | /* --- Exported methods --- */ 380 | /* Create a new hash table */ 381 | ght_hash_table_t* 382 | ght_create( 383 | __in unsigned int i_size, 384 | __in bool non_block 385 | ) 386 | { 387 | ght_hash_table_t *p_ht; 388 | int i=1; 389 | 390 | assert( non_block || preemption_enabled() ); 391 | 392 | if ( !(p_ht = (ght_hash_table_t*)mac_kalloc( sizeof(ght_hash_table_t), non_block? M_NOWAIT : M_WAITOK )) ) 393 | { 394 | DBG_PRINT_ERROR( ( "ght_create-> p_ht = mac_kalloc( %d, %d ) failed\n", 395 | (int)sizeof(ght_hash_table_t), 396 | non_block? M_NOWAIT : M_WAITOK ) ); 397 | return NULL; 398 | } 399 | 400 | /* Set the size of the hash table to the nearest 2^i higher then i_size */ 401 | p_ht->i_size = 1; 402 | while(p_ht->i_size < i_size) 403 | { 404 | p_ht->i_size = 1<i_size_mask = (1<<(i-1))-1; /* Mask to & with */ 408 | p_ht->i_items = 0; 409 | 410 | p_ht->fn_hash = ght_one_at_a_time_hash; 411 | 412 | /* Standard values for allocations */ 413 | p_ht->fn_alloc = mac_kalloc; 414 | p_ht->fn_free = mac_kfree; 415 | 416 | /* Set flags */ 417 | p_ht->i_heuristics = GHT_HEURISTICS_NONE; 418 | p_ht->i_automatic_rehash = FALSE; 419 | 420 | p_ht->bucket_limit = 0; 421 | p_ht->fn_bucket_free = NULL; 422 | 423 | p_ht->non_block = non_block; 424 | 425 | /* Create an empty bucket list. */ 426 | if ( !(p_ht->pp_entries = (ght_hash_entry_t**)mac_kalloc( p_ht->i_size*sizeof(ght_hash_entry_t*), non_block? M_NOWAIT : M_WAITOK ) ) ) 427 | { 428 | DBG_PRINT_ERROR( ( "ght_create-> p_ht->pp_entries = mac_kalloc( %d, %d ) failed\n", 429 | (int)(p_ht->i_size*sizeof(ght_hash_entry_t*)), 430 | non_block? M_NOWAIT : M_WAITOK ) ); 431 | mac_kfree( p_ht, sizeof(ght_hash_table_t) ); 432 | return NULL; 433 | } 434 | memset(p_ht->pp_entries, 0, p_ht->i_size*sizeof(ght_hash_entry_t*)); 435 | 436 | /* Initialise the number of entries in each bucket to zero */ 437 | if ( !(p_ht->p_nr = (int*)mac_kalloc( p_ht->i_size*sizeof(int), non_block? M_NOWAIT : M_WAITOK ) ) ) 438 | { 439 | DBG_PRINT_ERROR( ( "ght_create-> p_ht->p_nr = mac_kalloc( %d, %d ) failed\n", 440 | (int)(p_ht->i_size*sizeof(int)), 441 | non_block? M_NOWAIT : M_WAITOK ) ); 442 | 443 | mac_kfree( p_ht->pp_entries, p_ht->i_size*sizeof(ght_hash_entry_t*) ); 444 | mac_kfree( p_ht, sizeof(ght_hash_table_t) ); 445 | return NULL; 446 | } 447 | 448 | memset( p_ht->p_nr, 0, p_ht->i_size*sizeof(int) ); 449 | 450 | p_ht->p_oldest = NULL; 451 | p_ht->p_newest = NULL; 452 | 453 | return p_ht; 454 | } 455 | 456 | //-------------------------------------------------------------------- 457 | 458 | /* Set the allocation/deallocation function to use */ 459 | void ght_set_alloc(ght_hash_table_t *p_ht, ght_fn_alloc_t fn_alloc, ght_fn_free_t fn_free) 460 | { 461 | p_ht->fn_alloc = fn_alloc; 462 | p_ht->fn_free = fn_free; 463 | } 464 | 465 | //-------------------------------------------------------------------- 466 | 467 | /* Set the hash function to use */ 468 | void ght_set_hash(ght_hash_table_t *p_ht, ght_fn_hash_t fn_hash) 469 | { 470 | p_ht->fn_hash = fn_hash; 471 | } 472 | 473 | //-------------------------------------------------------------------- 474 | 475 | /* Set the heuristics to use. */ 476 | void ght_set_heuristics(ght_hash_table_t *p_ht, int i_heuristics) 477 | { 478 | p_ht->i_heuristics = i_heuristics; 479 | } 480 | 481 | //-------------------------------------------------------------------- 482 | 483 | /* Set the rehashing status of the table. */ 484 | void ght_set_rehash(ght_hash_table_t *p_ht, int b_rehash) 485 | { 486 | p_ht->i_automatic_rehash = b_rehash; 487 | } 488 | 489 | //-------------------------------------------------------------------- 490 | 491 | void ght_set_bounded_buckets(ght_hash_table_t *p_ht, unsigned int limit, ght_fn_bucket_free_callback_t fn) 492 | { 493 | p_ht->bucket_limit = limit; 494 | p_ht->fn_bucket_free = fn; 495 | 496 | if( limit > 0 && fn == NULL ) 497 | { 498 | panic( "ght_set_bounded_buckets: The bucket callback function is NULL but the limit is %d\n", limit ); 499 | } 500 | } 501 | 502 | //-------------------------------------------------------------------- 503 | 504 | /* Get the number of items in the hash table */ 505 | unsigned int ght_size(ght_hash_table_t *p_ht) 506 | { 507 | return p_ht->i_items; 508 | } 509 | 510 | //-------------------------------------------------------------------- 511 | 512 | /* Get the size of the hash table */ 513 | unsigned int ght_table_size(ght_hash_table_t *p_ht) 514 | { 515 | return p_ht->i_size; 516 | } 517 | 518 | //-------------------------------------------------------------------- 519 | 520 | /* Insert an entry into the hash table */ 521 | GHT_STATUS_CODE 522 | ght_insert( 523 | __in ght_hash_table_t *p_ht, 524 | __in void *p_entry_data, 525 | __in unsigned int i_key_size, 526 | __in const void *p_key_data 527 | ) 528 | { 529 | ght_hash_entry_t *p_entry; 530 | ght_uint32_t l_key; 531 | ght_hash_key_t key; 532 | 533 | assert(p_ht); 534 | 535 | hk_fill(&key, i_key_size, p_key_data); 536 | l_key = get_hash_value(p_ht, &key) & p_ht->i_size_mask; 537 | if (search_in_bucket(p_ht, l_key, &key, 0)) 538 | { 539 | /* Don't insert if the key is already present. */ 540 | return GHT_ALREADY_IN_HASH; 541 | } 542 | if (!(p_entry = he_create( p_ht, p_entry_data, 543 | i_key_size, p_key_data))) 544 | { 545 | DBG_PRINT_ERROR( ( "ght_insert-> he_create failed\n" ) ); 546 | return GHT_ERROR; 547 | } 548 | 549 | /* Rehash if the number of items inserted is too high. */ 550 | if( p_ht->i_automatic_rehash && ( p_ht->i_items > 2*p_ht->i_size ) ) 551 | { 552 | ght_rehash( p_ht, 2*p_ht->i_size ); 553 | /* Recalculate l_key after ght_rehash has updated i_size_mask */ 554 | l_key = get_hash_value(p_ht, &key) & p_ht->i_size_mask; 555 | } 556 | 557 | /* Place the entry first in the list. */ 558 | p_entry->p_next = p_ht->pp_entries[l_key]; 559 | p_entry->p_prev = NULL; 560 | if (p_ht->pp_entries[l_key]) 561 | { 562 | p_ht->pp_entries[l_key]->p_prev = p_entry; 563 | } 564 | p_ht->pp_entries[l_key] = p_entry; 565 | 566 | /* If this is a limited bucket hash table, potentially remove the last item */ 567 | if( p_ht->bucket_limit != 0 && 568 | p_ht->p_nr[l_key] >= p_ht->bucket_limit) 569 | { 570 | ght_hash_entry_t *p; 571 | 572 | /* Loop through entries until the last 573 | * 574 | * FIXME: Better with a pointer to the last entry 575 | */ 576 | for (p = p_ht->pp_entries[l_key]; 577 | p->p_next != NULL; 578 | p = p->p_next); 579 | 580 | assert(p && p->p_next == NULL); 581 | 582 | remove_from_chain(p_ht, l_key, p); /* To allow it to be reinserted in fn_bucket_free */ 583 | p_ht->fn_bucket_free(p->p_data, p->key.p_key); 584 | 585 | he_finalize( p_ht, p ); 586 | } 587 | else 588 | { 589 | p_ht->p_nr[l_key]++; 590 | 591 | assert( p_ht->pp_entries[l_key]?p_ht->pp_entries[l_key]->p_prev == NULL:1 ); 592 | 593 | p_ht->i_items++; 594 | } 595 | 596 | if (p_ht->p_oldest == NULL) 597 | { 598 | p_ht->p_oldest = p_entry; 599 | } 600 | p_entry->p_older = p_ht->p_newest; 601 | 602 | if (p_ht->p_newest != NULL) 603 | { 604 | p_ht->p_newest->p_newer = p_entry; 605 | } 606 | 607 | p_ht->p_newest = p_entry; 608 | 609 | return GHT_OK; 610 | } 611 | 612 | //-------------------------------------------------------------------- 613 | 614 | /* Get an entry from the hash table. The entry is returned, or NULL if it wasn't found */ 615 | void* 616 | ght_get( 617 | __in ght_hash_table_t *p_ht, 618 | __in unsigned int i_key_size, 619 | __in const void *p_key_data 620 | ) 621 | { 622 | ght_hash_entry_t *p_e; 623 | ght_hash_key_t key; 624 | ght_uint32_t l_key; 625 | 626 | assert(p_ht); 627 | 628 | hk_fill(&key, i_key_size, p_key_data); 629 | 630 | l_key = get_hash_value(p_ht, &key) & p_ht->i_size_mask; 631 | 632 | /* Check that the first element in the list really is the first. */ 633 | assert( p_ht->pp_entries[l_key]?p_ht->pp_entries[l_key]->p_prev == NULL:1 ); 634 | 635 | /* LOCK: p_ht->pp_entries[l_key] */ 636 | p_e = search_in_bucket(p_ht, l_key, &key, p_ht->i_heuristics); 637 | /* UNLOCK: p_ht->pp_entries[l_key] */ 638 | 639 | return (p_e?p_e->p_data:NULL); 640 | } 641 | 642 | //-------------------------------------------------------------------- 643 | 644 | /* Replace an entry from the hash table. The entry is returned, or NULL if it wasn't found */ 645 | void *ght_replace(ght_hash_table_t *p_ht, 646 | void *p_entry_data, 647 | unsigned int i_key_size, const void *p_key_data) 648 | { 649 | ght_hash_entry_t *p_e; 650 | ght_hash_key_t key; 651 | ght_uint32_t l_key; 652 | void *p_old; 653 | 654 | assert(p_ht); 655 | 656 | hk_fill(&key, i_key_size, p_key_data); 657 | 658 | l_key = get_hash_value(p_ht, &key) & p_ht->i_size_mask; 659 | 660 | /* Check that the first element in the list really is the first. */ 661 | assert( p_ht->pp_entries[l_key]?p_ht->pp_entries[l_key]->p_prev == NULL:1 ); 662 | 663 | /* LOCK: p_ht->pp_entries[l_key] */ 664 | p_e = search_in_bucket(p_ht, l_key, &key, p_ht->i_heuristics); 665 | /* UNLOCK: p_ht->pp_entries[l_key] */ 666 | 667 | if ( !p_e ) 668 | return NULL; 669 | 670 | p_old = p_e->p_data; 671 | p_e->p_data = p_entry_data; 672 | 673 | return p_old; 674 | } 675 | 676 | //-------------------------------------------------------------------- 677 | 678 | /* Remove an entry from the hash table. The removed entry, or NULL, is 679 | returned (and NOT free'd). */ 680 | void *ght_remove(ght_hash_table_t *p_ht, 681 | unsigned int i_key_size, const void *p_key_data) 682 | { 683 | ght_hash_entry_t *p_out; 684 | ght_hash_key_t key; 685 | ght_uint32_t l_key; 686 | void *p_ret=NULL; 687 | 688 | assert(p_ht); 689 | 690 | hk_fill(&key, i_key_size, p_key_data); 691 | l_key = get_hash_value(p_ht, &key) & p_ht->i_size_mask; 692 | 693 | /* Check that the first element really is the first */ 694 | assert( (p_ht->pp_entries[l_key]?p_ht->pp_entries[l_key]->p_prev == NULL:1) ); 695 | 696 | /* LOCK: p_ht->pp_entries[l_key] */ 697 | p_out = search_in_bucket(p_ht, l_key, &key, 0); 698 | 699 | /* Link p_out out of the list. */ 700 | if (p_out) 701 | { 702 | remove_from_chain(p_ht, l_key, p_out); 703 | 704 | /* This should ONLY be done for normal items (for now all items) */ 705 | p_ht->i_items--; 706 | 707 | p_ht->p_nr[l_key]--; 708 | /* UNLOCK: p_ht->pp_entries[l_key] */ 709 | #if !defined(NDEBUG) 710 | p_out->p_next = NULL; 711 | p_out->p_prev = NULL; 712 | #endif /* NDEBUG */ 713 | 714 | p_ret = p_out->p_data; 715 | he_finalize(p_ht, p_out); 716 | } 717 | /* else: UNLOCK: p_ht->pp_entries[l_key] */ 718 | 719 | return p_ret; 720 | } 721 | 722 | //-------------------------------------------------------------------- 723 | 724 | static inline void *first_keysize(ght_hash_table_t *p_ht, ght_iterator_t *p_iterator, const void **pp_key, unsigned int *size) 725 | { 726 | assert(p_ht && p_iterator); 727 | 728 | /* Fill the iterator */ 729 | p_iterator->p_entry = p_ht->p_oldest; 730 | 731 | if (p_iterator->p_entry) 732 | { 733 | p_iterator->p_next = p_iterator->p_entry->p_newer; 734 | *pp_key = p_iterator->p_entry->key.p_key; 735 | if (size != NULL) 736 | *size = p_iterator->p_entry->key.i_size; 737 | 738 | return p_iterator->p_entry->p_data; 739 | } 740 | 741 | p_iterator->p_next = NULL; 742 | *pp_key = NULL; 743 | if (size != NULL) 744 | *size = 0; 745 | 746 | return NULL; 747 | } 748 | 749 | //-------------------------------------------------------------------- 750 | 751 | /* Get the first entry in an iteration */ 752 | void *ght_first(ght_hash_table_t *p_ht, ght_iterator_t *p_iterator, const void **pp_key) 753 | { 754 | return first_keysize(p_ht, p_iterator, pp_key, NULL); 755 | } 756 | 757 | //-------------------------------------------------------------------- 758 | 759 | void *ght_first_keysize(ght_hash_table_t *p_ht, ght_iterator_t *p_iterator, const void **pp_key, unsigned int *size) 760 | { 761 | return first_keysize(p_ht, p_iterator, pp_key, size); 762 | } 763 | 764 | //-------------------------------------------------------------------- 765 | 766 | static inline void *next_keysize(ght_hash_table_t *p_ht, ght_iterator_t *p_iterator, const void **pp_key, unsigned int *size) 767 | { 768 | assert(p_ht && p_iterator); 769 | 770 | if (p_iterator->p_next) 771 | { 772 | /* More entries */ 773 | p_iterator->p_entry = p_iterator->p_next; 774 | p_iterator->p_next = p_iterator->p_next->p_newer; 775 | 776 | *pp_key = p_iterator->p_entry->key.p_key; 777 | if (size != NULL) 778 | *size = p_iterator->p_entry->key.i_size; 779 | 780 | return p_iterator->p_entry->p_data; /* We know that this is non-NULL */ 781 | } 782 | 783 | /* Last entry */ 784 | p_iterator->p_entry = NULL; 785 | p_iterator->p_next = NULL; 786 | 787 | *pp_key = NULL; 788 | if (size != NULL) 789 | *size = 0; 790 | 791 | return NULL; 792 | } 793 | 794 | //-------------------------------------------------------------------- 795 | 796 | /* Get the next entry in an iteration. You have to call ght_first 797 | once initially before you use this function */ 798 | void *ght_next(ght_hash_table_t *p_ht, ght_iterator_t *p_iterator, const void **pp_key) 799 | { 800 | return next_keysize(p_ht, p_iterator, pp_key, NULL); 801 | } 802 | 803 | //-------------------------------------------------------------------- 804 | 805 | void *ght_next_keysize(ght_hash_table_t *p_ht, ght_iterator_t *p_iterator, const void **pp_key, unsigned int *size) 806 | { 807 | return next_keysize(p_ht, p_iterator, pp_key, size); 808 | } 809 | 810 | //-------------------------------------------------------------------- 811 | 812 | /* Finalize (free) a hash table */ 813 | void 814 | ght_finalize( 815 | __in ght_hash_table_t *p_ht 816 | ) 817 | { 818 | int i; 819 | 820 | assert(p_ht); 821 | 822 | if (p_ht->pp_entries) 823 | { 824 | /* For each bucket, free all entries */ 825 | for (i=0; ii_size; i++) 826 | { 827 | free_entry_chain( p_ht, p_ht->pp_entries[i] ); 828 | p_ht->pp_entries[i] = NULL; 829 | } 830 | 831 | mac_kfree( p_ht->pp_entries, p_ht->i_size*sizeof(ght_hash_entry_t*) ); 832 | p_ht->pp_entries = NULL; 833 | 834 | } 835 | if (p_ht->p_nr) 836 | { 837 | mac_kfree( p_ht->p_nr, p_ht->i_size*sizeof(int) ); 838 | p_ht->p_nr = NULL; 839 | } 840 | 841 | mac_kfree( p_ht, sizeof(ght_hash_table_t) ); 842 | } 843 | 844 | //-------------------------------------------------------------------- 845 | 846 | /* Rehash the hash table (i.e. change its size and reinsert all 847 | * items). This operation is slow and should not be used frequently. 848 | */ 849 | void 850 | ght_rehash( 851 | __in ght_hash_table_t *p_ht, 852 | __in unsigned int i_size 853 | ) 854 | { 855 | ght_hash_table_t *p_tmp; 856 | ght_iterator_t iterator; 857 | const void *p_key; 858 | void *p; 859 | int i; 860 | 861 | assert(p_ht); 862 | 863 | /* Recreate the hash table with the new size */ 864 | p_tmp = ght_create(i_size, p_ht->non_block ); 865 | assert(p_tmp); 866 | 867 | /* Set the flags for the new hash table */ 868 | ght_set_hash(p_tmp, p_ht->fn_hash); 869 | ght_set_alloc(p_tmp, p_ht->fn_alloc, p_ht->fn_free); 870 | ght_set_heuristics(p_tmp, GHT_HEURISTICS_NONE); 871 | ght_set_rehash(p_tmp, FALSE); 872 | 873 | /* Walk through all elements in the table and insert them into the temporary one. */ 874 | for (p = ght_first(p_ht, &iterator, &p_key); p; p = ght_next(p_ht, &iterator, &p_key)) 875 | { 876 | assert(iterator.p_entry); 877 | 878 | /* Insert the entry into the new table */ 879 | if (ght_insert(p_tmp, 880 | iterator.p_entry->p_data, 881 | iterator.p_entry->key.i_size, iterator.p_entry->key.p_key) < 0) 882 | { 883 | DBG_PRINT_ERROR( ( "DldCommonHashTable.cpp ERROR: Out of memory error or entry already in hash table\n" 884 | "when rehashing (internal error)\n" ) ); 885 | } 886 | } 887 | 888 | /* Remove the old table... */ 889 | for (i=0; ii_size; i++) 890 | { 891 | if (p_ht->pp_entries[i]) 892 | { 893 | /* Delete the entries in the bucket */ 894 | free_entry_chain (p_ht, p_ht->pp_entries[i]); 895 | p_ht->pp_entries[i] = NULL; 896 | } 897 | } 898 | 899 | mac_kfree( p_ht->pp_entries, p_ht->i_size*sizeof(ght_hash_entry_t*) ); 900 | mac_kfree( p_ht->p_nr, p_ht->i_size*sizeof(int) ); 901 | 902 | /* ... and replace it with the new */ 903 | p_ht->i_size = p_tmp->i_size; 904 | p_ht->i_size_mask = p_tmp->i_size_mask; 905 | p_ht->i_items = p_tmp->i_items; 906 | p_ht->pp_entries = p_tmp->pp_entries; 907 | p_ht->p_nr = p_tmp->p_nr; 908 | 909 | p_ht->p_oldest = p_tmp->p_oldest; 910 | p_ht->p_newest = p_tmp->p_newest; 911 | 912 | /* Clean up */ 913 | p_tmp->pp_entries = NULL; 914 | p_tmp->p_nr = NULL; 915 | mac_kfree( p_tmp, sizeof(ght_hash_table_t) ); 916 | } 917 | 918 | //-------------------------------------------------------------------- 919 | 920 | static ght_uint32_t crc32_table[256] = 921 | { 922 | 0x00000000,0x04c11db7,0x09823b6e,0x0d4326d9,0x130476dc,0x17c56b6b,0x1a864db2,0x1e475005, 923 | 0x2608edb8,0x22c9f00f,0x2f8ad6d6,0x2b4bcb61,0x350c9b64,0x31cd86d3,0x3c8ea00a,0x384fbdbd, 924 | 0x4c11db70,0x48d0c6c7,0x4593e01e,0x4152fda9,0x5f15adac,0x5bd4b01b,0x569796c2,0x52568b75, 925 | 0x6a1936c8,0x6ed82b7f,0x639b0da6,0x675a1011,0x791d4014,0x7ddc5da3,0x709f7b7a,0x745e66cd, 926 | 0x9823b6e0,0x9ce2ab57,0x91a18d8e,0x95609039,0x8b27c03c,0x8fe6dd8b,0x82a5fb52,0x8664e6e5, 927 | 0xbe2b5b58,0xbaea46ef,0xb7a96036,0xb3687d81,0xad2f2d84,0xa9ee3033,0xa4ad16ea,0xa06c0b5d, 928 | 0xd4326d90,0xd0f37027,0xddb056fe,0xd9714b49,0xc7361b4c,0xc3f706fb,0xceb42022,0xca753d95, 929 | 0xf23a8028,0xf6fb9d9f,0xfbb8bb46,0xff79a6f1,0xe13ef6f4,0xe5ffeb43,0xe8bccd9a,0xec7dd02d, 930 | 0x34867077,0x30476dc0,0x3d044b19,0x39c556ae,0x278206ab,0x23431b1c,0x2e003dc5,0x2ac12072, 931 | 0x128e9dcf,0x164f8078,0x1b0ca6a1,0x1fcdbb16,0x018aeb13,0x054bf6a4,0x0808d07d,0x0cc9cdca, 932 | 0x7897ab07,0x7c56b6b0,0x71159069,0x75d48dde,0x6b93dddb,0x6f52c06c,0x6211e6b5,0x66d0fb02, 933 | 0x5e9f46bf,0x5a5e5b08,0x571d7dd1,0x53dc6066,0x4d9b3063,0x495a2dd4,0x44190b0d,0x40d816ba, 934 | 0xaca5c697,0xa864db20,0xa527fdf9,0xa1e6e04e,0xbfa1b04b,0xbb60adfc,0xb6238b25,0xb2e29692, 935 | 0x8aad2b2f,0x8e6c3698,0x832f1041,0x87ee0df6,0x99a95df3,0x9d684044,0x902b669d,0x94ea7b2a, 936 | 0xe0b41de7,0xe4750050,0xe9362689,0xedf73b3e,0xf3b06b3b,0xf771768c,0xfa325055,0xfef34de2, 937 | 0xc6bcf05f,0xc27dede8,0xcf3ecb31,0xcbffd686,0xd5b88683,0xd1799b34,0xdc3abded,0xd8fba05a, 938 | 0x690ce0ee,0x6dcdfd59,0x608edb80,0x644fc637,0x7a089632,0x7ec98b85,0x738aad5c,0x774bb0eb, 939 | 0x4f040d56,0x4bc510e1,0x46863638,0x42472b8f,0x5c007b8a,0x58c1663d,0x558240e4,0x51435d53, 940 | 0x251d3b9e,0x21dc2629,0x2c9f00f0,0x285e1d47,0x36194d42,0x32d850f5,0x3f9b762c,0x3b5a6b9b, 941 | 0x0315d626,0x07d4cb91,0x0a97ed48,0x0e56f0ff,0x1011a0fa,0x14d0bd4d,0x19939b94,0x1d528623, 942 | 0xf12f560e,0xf5ee4bb9,0xf8ad6d60,0xfc6c70d7,0xe22b20d2,0xe6ea3d65,0xeba91bbc,0xef68060b, 943 | 0xd727bbb6,0xd3e6a601,0xdea580d8,0xda649d6f,0xc423cd6a,0xc0e2d0dd,0xcda1f604,0xc960ebb3, 944 | 0xbd3e8d7e,0xb9ff90c9,0xb4bcb610,0xb07daba7,0xae3afba2,0xaafbe615,0xa7b8c0cc,0xa379dd7b, 945 | 0x9b3660c6,0x9ff77d71,0x92b45ba8,0x9675461f,0x8832161a,0x8cf30bad,0x81b02d74,0x857130c3, 946 | 0x5d8a9099,0x594b8d2e,0x5408abf7,0x50c9b640,0x4e8ee645,0x4a4ffbf2,0x470cdd2b,0x43cdc09c, 947 | 0x7b827d21,0x7f436096,0x7200464f,0x76c15bf8,0x68860bfd,0x6c47164a,0x61043093,0x65c52d24, 948 | 0x119b4be9,0x155a565e,0x18197087,0x1cd86d30,0x029f3d35,0x065e2082,0x0b1d065b,0x0fdc1bec, 949 | 0x3793a651,0x3352bbe6,0x3e119d3f,0x3ad08088,0x2497d08d,0x2056cd3a,0x2d15ebe3,0x29d4f654, 950 | 0xc5a92679,0xc1683bce,0xcc2b1d17,0xc8ea00a0,0xd6ad50a5,0xd26c4d12,0xdf2f6bcb,0xdbee767c, 951 | 0xe3a1cbc1,0xe760d676,0xea23f0af,0xeee2ed18,0xf0a5bd1d,0xf464a0aa,0xf9278673,0xfde69bc4, 952 | 0x89b8fd09,0x8d79e0be,0x803ac667,0x84fbdbd0,0x9abc8bd5,0x9e7d9662,0x933eb0bb,0x97ffad0c, 953 | 0xafb010b1,0xab710d06,0xa6322bdf,0xa2f33668,0xbcb4666d,0xb8757bda,0xb5365d03,0xb1f740b4 954 | }; 955 | 956 | //-------------------------------------------------------------------- 957 | 958 | /* One-at-a-time hash (found in a web article from ddj), this is the 959 | * standard hash function. 960 | * 961 | * See http://burtleburtle.net/bob/hash/doobs.html 962 | * for the hash functions used here. 963 | */ 964 | ght_uint32_t ght_one_at_a_time_hash(ght_hash_key_t *p_key) 965 | { 966 | ght_uint32_t i_hash=0; 967 | int i; 968 | 969 | // TEST 970 | //return 0x0; 971 | 972 | assert(p_key); 973 | 974 | for (i=0; ii_size; ++i) 975 | { 976 | i_hash += ((unsigned char*)p_key->p_key)[i]; 977 | i_hash += (i_hash << 10); 978 | i_hash ^= (i_hash >> 6); 979 | } 980 | i_hash += (i_hash << 3); 981 | i_hash ^= (i_hash >> 11); 982 | i_hash += (i_hash << 15); 983 | 984 | return i_hash; 985 | } 986 | 987 | //-------------------------------------------------------------------- 988 | 989 | /* CRC32 hash based on code from comp.compression FAQ. 990 | * Added by Dru Lemley 991 | */ 992 | ght_uint32_t ght_crc_hash(ght_hash_key_t *p_key) 993 | { 994 | unsigned char *p, *p_end; 995 | ght_uint32_t crc; 996 | 997 | assert(p_key); 998 | 999 | crc = 0xffffffff; /* preload shift register, per CRC-32 spec */ 1000 | p = (unsigned char *)p_key->p_key; 1001 | p_end = p + p_key->i_size; 1002 | while (p < p_end) 1003 | crc = (crc << 8) ^ crc32_table[(crc >> 24) ^ *(p++)]; 1004 | return ~crc; /* transmit complement, per CRC-32 spec */ 1005 | } 1006 | 1007 | //-------------------------------------------------------------------- 1008 | 1009 | /* Rotating hash function. */ 1010 | ght_uint32_t ght_rotating_hash(ght_hash_key_t *p_key) 1011 | { 1012 | ght_uint32_t i_hash=0; 1013 | int i; 1014 | 1015 | assert(p_key); 1016 | 1017 | for (i=0; ii_size; ++i) 1018 | { 1019 | i_hash = (i_hash<<4)^(i_hash>>28)^((unsigned char*)p_key->p_key)[i]; 1020 | } 1021 | 1022 | return i_hash; 1023 | } 1024 | 1025 | //-------------------------------------------------------------------- 1026 | 1027 | -------------------------------------------------------------------------------- /src/DldSparseFile.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Slava Imameev. All rights reserved. 3 | */ 4 | 5 | #ifndef _DLDSPARSEFILE_H 6 | #define _DLDSPARSEFILE_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "DldCommon.h" 15 | #include "DldCommonHashTable.h" 16 | 17 | //-------------------------------------------------------------------- 18 | 19 | // 20 | // the class represents a sparse file 21 | // 22 | class DldSparseFile: public OSObject{ 23 | 24 | OSDeclareDefaultStructors( DldSparseFile ) 25 | 26 | friend class DldSparseFilesHashTable; 27 | 28 | protected: 29 | 30 | // 31 | // all sparse files are placed in the global list, 32 | // the objects are referenced 33 | // 34 | static LIST_ENTRY sSparseFileListHead; 35 | LIST_ENTRY listEntry; 36 | 37 | // 38 | // the entry for the temporay list used by the reaper thread 39 | // 40 | LIST_ENTRY staleListEntry; 41 | 42 | // 43 | // the sSparseFileListHead is protected by the RW lock 44 | // 45 | static IORWLock* sSparseFileListLock; 46 | 47 | // 48 | // if false the entry is not in the hash, be careful as the stalled entry 49 | // and the valid one might exists in tha same time, the stalled entry might exists 50 | // because of reference to be released, if the entry is not in hash then 51 | // it should not be used as becomes invalid 52 | // 53 | bool inHashTable; 54 | 55 | // 56 | // if greater than 0x0, then must not be prepared for reclaiming even if the corresponding 57 | // covering vnode is being reclaimed, this behaviour is required for the concurrent 58 | // vnodes termination and creation, normally this count is 0x1 and becomes bigger 59 | // for a small time lapse 60 | // 61 | SInt32 activeUsersCount; 62 | 63 | // 64 | // true if the sparse file has been accessed in the last interval set by the reaper thread 65 | // 66 | bool accessed; 67 | UInt32 accessedCachedNodesCount; 68 | 69 | // 70 | // a usage threshold when cache shrinking is started 71 | // 72 | static UInt32 sCacheUsageThreshold; 73 | 74 | // 75 | // inserts the sparse file in the sSparseFileListHead list 76 | // 77 | void insertInList(); 78 | void removeFromList(); // undo insertInList() 79 | 80 | // 81 | // a thread's continuation, frees unused cached nodes 82 | // 83 | static void sCachedNodesSetTrimmer( void* ); 84 | static thread_t sCachedNodesSetTrimmerThread; 85 | 86 | // 87 | // the synchronization event to wake up the sCachedNodesSetTrimmerThread thread 88 | // 89 | static UInt32 sCachedNodeReaperEvent; 90 | static UInt32 sCachedNodeReaperTerminatedEvent; 91 | 92 | // 93 | // if true the sCachedNodesSetTrimmerThread is being terminated 94 | // 95 | static bool sTerminateCachedNodeReaper; 96 | 97 | public: 98 | 99 | // 100 | // in case of sInitSparseFileSubsystem returns an error 101 | // the caller must call sFreeSparseFileSubsystem() 102 | // to free alocated resources 103 | // 104 | static IOReturn sInitSparseFileSubsystem(); 105 | static void sFreeSparseFileSubsystem(); 106 | 107 | // 108 | // the data are saved in chunks, if there is no data 109 | // for the start or the end of a chunk then they are zeroed, 110 | // the chunk size must be a power of 2 111 | // 112 | const static unsigned int BlockSize = 4*PAGE_SIZE; 113 | const static off_t InvalidOffset = (off_t)(-1); 114 | 115 | // 116 | // the sparse file layout is as follows 117 | // Header (occupies the entire first block) 118 | // Sparse offsets B-Tree root 119 | // Tags B-Tree root 120 | // Data 121 | // 122 | const static off_t HeaderOffset = 0x0; 123 | 124 | 125 | // 126 | // used by the CAWL subsystem 127 | // 128 | typedef struct _BlockTag{ 129 | 130 | SInt64 timeStamp; 131 | 132 | } BlockTag; 133 | 134 | typedef struct _IdentificatioInfo{ 135 | 136 | // 137 | // a CAWL covering vnode associated with the 138 | // sparse file, if exists 139 | // 140 | vnode_t cawlCoveringVnode; 141 | 142 | // 143 | // a CAWL covered vnode unique ID, if exists, this is not the same as DldIOVnodeID, 144 | // this ID defines uniquely the data stream on the storage and stays the same regardles 145 | // of whether any vnode exists for the data stream 146 | // 147 | unsigned char cawlCoveredVnodeID[16]; 148 | 149 | const char* identificationString; // optional 150 | 151 | } IdentificatioInfo; 152 | 153 | private: 154 | 155 | typedef struct _OffsetMapEntry{ 156 | 157 | // 158 | // an offset in a sparse file, 159 | // if the value is (-1) the entry is invalid 160 | // 161 | off_t sparseFileOffset; 162 | 163 | // 164 | // an offset in a dataFile 165 | // 166 | off_t dataFileOffset; 167 | 168 | // 169 | // a tag associated with the chunk, 170 | // used by the CAWL subsystem 171 | // 172 | BlockTag tag; 173 | 174 | } OffsetMapEntry; 175 | 176 | 177 | typedef struct _TagListEntry{ 178 | 179 | // 180 | // all blocks are arranged by their tags 181 | // in the circular list, the following 182 | // offsets are forward and back links 183 | // expressed as offsets to blocks, 184 | // if they are equal to the block offset 185 | // the tag is related to then the block 186 | // is not in the list, also the offsets 187 | // mught be set to InvalidOffset meaning 188 | // the same - the block is not in the list 189 | // 190 | 191 | off_t flink; 192 | off_t blink; 193 | } TagListEntry; 194 | 195 | typedef struct _FreeChunkHeader{ 196 | 197 | // 198 | // free chunks are in a single linked list 199 | // 200 | 201 | // 202 | // offset to the next forward free block or InvalidOffset if invalid 203 | // 204 | off_t flink; 205 | 206 | // 207 | // number of valid entries 208 | // 209 | int numberOfValidEntries; 210 | 211 | // 212 | // array of entries 213 | // 214 | off_t entries[ 0x1 ]; 215 | 216 | } FreeChunkHeader; 217 | 218 | static const int sFreeChunksArrayCapacity = ( DldSparseFile::BlockSize - sizeof( DldSparseFile::FreeChunkHeader) + sizeof(off_t) )/ sizeof( off_t ); 219 | 220 | typedef struct _BackingFile{ 221 | 222 | // 223 | // only the user reference is held, the io reference is not held, 224 | // so always call vnode_getwithref() and check for error before 225 | // calling any KPI function which accepts this vnode as a parameter, 226 | // 227 | vnode_t vnode; 228 | 229 | // 230 | // a context for all operations 231 | // 232 | vfs_context_t vfsContext; 233 | 234 | // 235 | // a full file size, must be greater or equal to breakOffset 236 | // 237 | off_t fileSize; 238 | 239 | // 240 | // an offset of the first free chunk descriptor in the disk( this chunk is also cached in the memory - 241 | // freeChunksDescriptor ), must be smaller or equal to breakOffset, 242 | // each free chunk descriptor starts with the FreeChunkHeader, if there is no free 243 | // chunk then InvalidOffset, in that case the allocation is made from 244 | // a pool starting at breakOffset 245 | // 246 | off_t firstFreeChunk; 247 | 248 | // 249 | // a pointer to in memory free chunks descriptor, NULL if doesn't exist 250 | // 251 | FreeChunkHeader* freeChunksDescriptor; 252 | 253 | // 254 | // an offset of the completely free area up to the end of the file, 255 | // the free chunks are not created there, they just allocated by demand 256 | // from this area 257 | // 258 | off_t breakOffset; 259 | 260 | } BackingFile; 261 | 262 | // 263 | // a header for the file, synchronized only on covering vnode reclaim, 264 | // occupies the first block 265 | // 266 | typedef struct _FileHeader{ 267 | 268 | #define DLD_SPARSE_FILE_SIGNATURE 0xABDDEFEF 269 | SInt64 signature; 270 | 271 | IdentificatioInfo identificationInfo; 272 | 273 | // 274 | // not all fields are valid, it is obvious 275 | // that vnode is invalid 276 | // 277 | BackingFile dataFile; 278 | 279 | // 280 | // a tree root 281 | // 282 | off_t sparseFileOffsetBTreeRoot; 283 | 284 | // 285 | // an offset for the block with the oldest time stamp, this is an offset in the file 286 | // not in the data file representing the sparse file, i.e. the offset is a key for 287 | // the sparseFileOffsetBTree B-Tree 288 | // 289 | off_t oldestWrittenBlock; 290 | 291 | // 292 | // currently contains the path for the file which data the sparse file contains, 293 | // could be used ony for the debug purposses 294 | // 295 | char identificationString[ 128 ]; 296 | 297 | } FileHeader; 298 | 299 | // 300 | // set ups a file descriptor, creates the file 301 | // 302 | errno_t createDataFileByName( __in const char* name, __in IdentificatioInfo* info ); 303 | 304 | // 305 | // a counterpart for createBackingFileByName 306 | // 307 | void freeDataFile(); 308 | 309 | // 310 | // allocates a chunk from a file 311 | // 312 | off_t allocateChunkFromDataFile( __in bool acquireLock ); 313 | 314 | // 315 | // return back a chunk allocated by allocateChunkFromDataFile() 316 | // 317 | errno_t returnChunkToFreeList( __in off_t chunkOffset, __in bool acquireLock ); 318 | 319 | // 320 | // read and write one chunk of data 321 | // 322 | errno_t readChunkFromDataFile( __in void* buffer, __in off_t offsetInDataFile ); 323 | errno_t writeChunkToDataFile( __in void* buffer, __in off_t offsetInDataFile ); 324 | 325 | private: 326 | 327 | struct{ 328 | 329 | // 330 | // if 0x1 the the entire B-Tree and data structure 331 | // must be preserved in the data file so it can be 332 | // reopened and used to retrive data 333 | // 334 | unsigned int preserveDataFile:0x1; 335 | 336 | } flags; 337 | 338 | // 339 | // a file which contains data, the data are written in chunks 340 | // and the chunks are raw data with a corresponding B-tree key 341 | // saved in the file in the chunks of the same size as for the data, 342 | // the user reference is bumped to retain the vnode 343 | // 344 | BackingFile dataFile; 345 | 346 | // 347 | // a divisor that defines the file grow policy, it defines 348 | // how many add to the file in the terms of the file size, 349 | // set it to power of 2, if 1 then the file size doubles 350 | // 351 | static int sGrowDivisor; 352 | 353 | // 354 | // contains a map for valid data, one bit for each chunk, 355 | // might be NULL if the map is entirely in the memory(i.e. in validDataMapCache), 356 | // the user reference is bumped to retain the vnode 357 | // TO DO - do we need it? 358 | // vnode_t validDataMapFile; // optional 359 | 360 | // 361 | // a cached offset map, TO DO - it seems the node cache will do this 362 | // 363 | // OffsetMapEntry offsetMapCache[ 8 ]; 364 | 365 | // 366 | // a cached valid data map, one bit represents one chunk 367 | // on the corresponding offset in the sparse file, 368 | // the cache is a window in the full map desribing a region 369 | // at the validDataMapCacheOffset offset in the sparse file 370 | // TO DO - do we need it? 371 | // uint8_t validDataMapCache[ 32 ]; 372 | // off_t validDataMapCacheOffset; // set to InvalidOffset if the map cache is invalid 373 | 374 | // 375 | // a RW lock to protect the internal data 376 | // 377 | IORWLock* rwLock; 378 | 379 | #if defined(DBG) 380 | thread_t exclusiveThread; 381 | #endif//DBG 382 | 383 | // 384 | // a mount structutre for the related voume under the CAWL control, 385 | // must not be referenced, used only as a key to retrieve the DldVfsMntHook object 386 | // 387 | mount_t mnt; 388 | 389 | protected: // identificationInfo must be accessible for the hash table friend class 390 | 391 | // 392 | // identification info as it was passed to the create routine, 393 | // so all pointers are invalid if touched outside the create routine 394 | // 395 | IdentificatioInfo identificationInfo; 396 | 397 | protected: 398 | 399 | virtual void free(); 400 | virtual bool init(); 401 | 402 | virtual void LockShared(); 403 | virtual void UnLockShared(); 404 | virtual void LockExclusive(); 405 | virtual void UnLockExclusive(); 406 | 407 | public: 408 | 409 | // 410 | // returns true if the counter has been incremented and the exisiting object is safe to assign to a covering vnode 411 | // 412 | bool incrementUsersCount(); 413 | void decrementUsersCount(); 414 | 415 | private: 416 | 417 | // 418 | // B-Tree node layout in a file, each node occupies a complete chunk, 419 | // the size of BTreeNode must be smaller that the size of the chunk! 420 | // 421 | // A B-tree T is a rooted tree (whose root is root[T ]) having the following properties: 422 | // 423 | // 1. Every node x has the following fields: a. n[x], the number of keys currently stored in node x, 424 | // b. the n[x] keys themselves, stored in nondecreasing order, so that key1[x] ≤ key2[x] ≤ ··· ≤ keyn[x][x], 425 | // c. leaf[x], a boolean value that is TRUE if x is a leaf and FALSE if x is an internal node. 426 | // 427 | // 2. Each internal node x also contains n[x]+1 pointers c1[x],c2[x],...,cn[x]+1[x] to its children. 428 | // Leaf nodes have no children, so their ci fields are undefined. 429 | // 430 | // 3. The keys keyi[x] separate the ranges of keys stored in each subtree: if ki is any key stored in 431 | // the subtree with root ci [x ], then 432 | // k1 ≤ key1[x] ≤ k2 ≤ key2[x] ≤ ··· ≤ keyn[x][x] ≤ kn[x]+1 . 433 | // 434 | // 4. All leaves have the same depth, which is the tree’s height h. 435 | // 436 | // 5. There are lower and upper bounds on the number of keys a node can contain. These bounds can be expressed 437 | // in terms of a fixed integer t ≥ 2 called the minimum degree of the B-tree: 438 | // a. Every node other than the root must have at least t − 1 keys. Every internal node other than the 439 | // root thus has at least t children. If the tree is nonempty, the root must have at least one key. 440 | // b. Every node can contain at most 2t − 1 keys. Therefore, an internal node can have at most 2t children. 441 | // We say that a node is full if it contains exactly 2t − 1 keys. 442 | // 443 | // for reference see "Introduction to algorithms" / Thomas H. Cormen . . . [et al.].—2nd ed. pp 434 - 452 444 | // 445 | 446 | typedef struct _BTreeKey{ 447 | 448 | OffsetMapEntry mapEntry; 449 | 450 | } BTreeKey; 451 | 452 | // 453 | // a B-Tree node header 454 | // 455 | typedef struct _BTreeNodeHeader { 456 | 457 | #if defined(DBG) 458 | #define DLD_BTNODE_SIGNATURE (0xABCD9977) 459 | uint32_t signature; 460 | #endif // DBG 461 | 462 | // 463 | // a list entry, all entries cached in the memory are put 464 | // in the list anchored in the BTree structure 465 | // 466 | LIST_ENTRY listEntry; 467 | 468 | // 469 | // a reference count, used to manage the lifespan of the cahed entry 470 | // 471 | SInt32 referenceCount; 472 | 473 | // 474 | // this node offset in the file 475 | // 476 | off_t nodeOffset; 477 | 478 | // 479 | // Used to indicate whether leaf or not 480 | // 481 | bool leaf; 482 | 483 | // 484 | // true if the node is not synchronized with the on disk copy 485 | // 486 | bool dirty; 487 | 488 | // 489 | // true if the node has been accessed 490 | // 491 | bool accessed; 492 | 493 | // 494 | // Number of active keys 495 | // 496 | unsigned int nrActive; 497 | 498 | // 499 | // Level in the B-Tree 500 | // 501 | unsigned int level; 502 | 503 | } BTreeNodeHeader; 504 | 505 | 506 | typedef struct _BTreeKeyEntry{ 507 | 508 | BTreeKey keyValue; 509 | 510 | // 511 | // each vaid key entry should be in the tags list which is maintained in ascending order, 512 | // the list entries are bound to the keyVals, if the keyVals is removed or moved the list 513 | // entry must be also moved or removed respectively, so this is no-brainer to copy keyVals 514 | // operation with tagListEnties 515 | // 516 | TagListEntry tagListEntry; 517 | 518 | } BTreeKeyEntry; 519 | 520 | 521 | // 522 | // the order of the tree, the order t means that there are maximum 2t children and 2t-1 keys 523 | // 524 | const static unsigned int BTreeOrder = (BlockSize - sizeof(BTreeNodeHeader)) / (2*(sizeof(BTreeKeyEntry) + sizeof(off_t))); 525 | 526 | // 527 | // a B-Tree node, nodes are stashed in a chunk 528 | // 529 | typedef struct _BTreeNode { 530 | 531 | // 532 | // a header, hence an "h" 533 | // 534 | BTreeNodeHeader h; 535 | 536 | // 537 | // Array of keys and values, a node with (2t - 1) keys has at most (2t) children! 538 | // 539 | BTreeKeyEntry keyEntries[ (2 * DldSparseFile::BTreeOrder) - 0x1 ]; 540 | 541 | // 542 | // Array of offsets for child nodes, if the offset doesn't point to any child 543 | // the value must be set to InvalidOffset, this is important as comparing the 544 | // last child offset value ( i.e. at children[h.nrActive]) with the invalid 545 | // value is the only way to infer whether the offset points to a valid child 546 | // 547 | off_t children[ 2 * DldSparseFile::BTreeOrder ]; 548 | 549 | } BTreeNode; 550 | 551 | typedef enum _BTreeKeyCmp{ 552 | kKeysEquals = 0x0, 553 | kKeyOneBigger = 0x1, 554 | kKeyOneSmaller = 0x2 555 | } BTreeKeyCmp; 556 | 557 | // 558 | // key1 == key2 - kKeysEquals 559 | // key1 > key2 - kKeyOneBigger 560 | // key2 < key2 - kKeyOneSmaller 561 | // 562 | typedef BTreeKeyCmp (*CompareKeysFtr)( __in BTreeKey* key1, __in BTreeKey* key2); 563 | 564 | // 565 | // a B-Tree itself 566 | // 567 | typedef struct _BTree { 568 | 569 | // 570 | // if true there were errors in the tree processing and it can't be trusted anymore 571 | // 572 | bool damaged; 573 | 574 | // 575 | // a function for keys comparision 576 | // 577 | CompareKeysFtr compareKeys; 578 | 579 | // 580 | // current root node of the B-Tree, the node is referenced(!) 581 | // and retained even if empty 582 | // 583 | BTreeNode* root; 584 | 585 | } BTree; 586 | 587 | typedef enum {left = -1,right = 1} BtPosition; 588 | 589 | typedef struct _BtNodePos{ 590 | 591 | // 592 | // this node pointer is optional and may be NULL 593 | // 594 | BTreeNode* node; 595 | 596 | // 597 | // node offset, must be valid if node is NULL, else optional 598 | // 599 | off_t nodeOffset; 600 | 601 | // 602 | // the key index, must be valid 603 | // 604 | unsigned int index; 605 | 606 | } BtNodePos; 607 | 608 | // 609 | // if rootNodeOffset is InvalidOffset a new root is allocated, 610 | // else a root is read from the file 611 | // 612 | BTree* createBTree( __in CompareKeysFtr comparator, __in off_t rootNodeOffset, __in bool acquireLock ); 613 | void deleteBTree( __in BTree* btree, __in bool acquireLock ); 614 | 615 | // 616 | // tries to find and reference an entry in the cache 617 | // 618 | BTreeNode* referenceCachedBTreeNodeByOffset( __in off_t offset, __in bool acquireLock ); 619 | 620 | // 621 | // flushes the cached nodes and purges them from the cache 622 | // 623 | void flushAndPurgeCachedNodes( __in bool prepareForReclaim ); 624 | 625 | // 626 | // if the offset is set to DldSparseFile::InvalidOffset a new node is allocated with the reserved space, 627 | // the entry must be dereferenced when no longer needed 628 | // 629 | BTreeNode* getBTreeNodeReference( __in off_t offset, __in bool acquireLock ); 630 | 631 | // 632 | // reference the node 633 | // 634 | void referenceBTreeNode( __in BTreeNode* node ); 635 | 636 | // 637 | // if the reference count drops to zero the node is removed 638 | // from the cache list and from the memory but not from 639 | // a backing store 640 | // 641 | void dereferenceBTreeNode( __in BTreeNode* node, __in bool acquireLock ); 642 | 643 | // 644 | // frees the allocated memory, doesn't remove from the backing store 645 | // 646 | void freeBTreeNodeMemory( __in BTreeNode* node ); 647 | 648 | // 649 | // removed the node from the backing store 650 | // 651 | errno_t removeBTreeNodeFromBackingStore( __in BTreeNode* node, __in bool acquireLock ); 652 | 653 | // 654 | // the function splits a full node y (having 2t − 1 keys) around its median key keyt [y] 655 | // into two nodes having t − 1 keys each. The median key moves up into y’s parent to identify 656 | // the dividing point between the two new trees, 657 | // @param index Index of the child node 658 | // 659 | errno_t BTreeSplitChild( __in BTree* btree, 660 | __inout BTreeNode* parent, 661 | __in unsigned int index, 662 | __inout BTreeNode* child, 663 | __in bool acquireLock ); 664 | 665 | // 666 | // the function inserts key k into node x, which is assumed to be nonfull when the procedure is called. 667 | // 668 | errno_t BTreeInsertNonfull( __in BTree* btree, 669 | __inout BTreeNode* parentNode, 670 | __in BTreeKey* keyVal, 671 | __in bool acquireLock ); 672 | 673 | // 674 | // The function is used to insert node into a B-Tree 675 | // 676 | errno_t BTreeInsertKey( __inout BTree* btree, 677 | __in BTreeKey* key_val, 678 | __in bool acquireLock ); 679 | 680 | // 681 | // The function is used to get the position of the MAX or MIN key within the subtree 682 | // @param btree The btree 683 | // @param subtree The subtree to be searched 684 | // @return The node containing the key and position of the key, 685 | // if there was an error then nodePos.nodeOffset == DldSparseFile::InvalidOffset 686 | // 687 | BtNodePos BTreeGetMaxOrMinKeyPos( __in BTree* btree, 688 | __in BTreeNode* subtree, 689 | __in bool getMax, // if true returns MAX, else returns MIN 690 | __in bool acquireLock ); 691 | 692 | BtNodePos BTreeGetMaxKeyPos( __in BTree* btree, 693 | __in BTreeNode* subtree, 694 | __in bool acquireLock ); 695 | 696 | BtNodePos BTreeGetMinKeyPos( __in BTree* btree, 697 | __in BTreeNode* subtree, 698 | __in bool acquireLock ); 699 | 700 | 701 | BTreeNode* BTreeMergeSiblings( __inout BTree* btree, 702 | __in BTreeNode* parent, 703 | __in unsigned int index, 704 | __in BtPosition pos, 705 | __in bool acquireLock ); 706 | 707 | errno_t BTreeMoveKey( __inout BTree* btree, 708 | __in BTreeNode* node, 709 | __in unsigned int index, 710 | __in BtPosition pos, 711 | __in bool acquireLock ); 712 | 713 | // 714 | // Merge nodes n1 and n2 715 | // @param n1 First node 716 | // @param n2 Second node 717 | // @return combined node 718 | // 719 | BTreeNode* BTreeMergeNodes( __inout BTree* btree, 720 | __in BTreeNode* n1, 721 | __in BTreeKeyEntry* kv, 722 | __in BTreeNode* n2, 723 | __in bool acquireLock ); 724 | 725 | // 726 | // delete a key from the B-tree node 727 | // @param btree The btree 728 | // @param node The node from which the key is to be deleted 729 | // @param key The key to be deleted 730 | // @return 0 on success -1 on error 731 | // 732 | errno_t BTreeDeleteKeyFromNode( __inout BTree* btree, 733 | __in BtNodePos* nodePos, 734 | __in bool acquireLock ); 735 | 736 | // 737 | // Function is used to delete a node from a B-Tree 738 | // @param btree The B-Tree 739 | // @param key Key of the node to be deleted 740 | // @param value function to map the key to an unique integer value 741 | // @param compare Function used to compare the two nodes of the tree 742 | // @return success or failure 743 | // 744 | errno_t BTreeDeleteKey( __inout BTree* btree, 745 | __inout BTreeNode* subtree, 746 | __in BTreeKey* key, 747 | __in bool acquireLock ); 748 | 749 | // 750 | // Function used to get the node containing the given key 751 | // @param btree The btree to be searched 752 | // @param key The the key to be searched 753 | // @return The node and position of the key within the node 754 | // the non NULL node is returned and must be freed by a caller! 755 | // 756 | BtNodePos BTreeGetNodeByKey( __in BTree* btree, 757 | __in BTreeKey* key, 758 | __in bool acquireLock ); 759 | 760 | // 761 | // returns the index of the element that are equal or bigger or nrActive if there is no bigger vaue, 762 | // a caller must hold the lock 763 | // 764 | int BTreeGetKeyIndex( __in BTree* btree, 765 | __in BTreeNode* node, 766 | __in BTreeKey* key ); 767 | 768 | // 769 | // Function used to get the node either 770 | // - containing the given key 771 | // - containing the next bigger key if the equal key value doesn't exist 772 | // 773 | // @param btree The btree to be searched 774 | // @param key The the key to be searched 775 | // @return The node and position of the key within the node 776 | // the non NULL node is returned and must be freed by a caller 777 | // the caller must be cautios in changing the node as the node 778 | // might be a root one so any changes must be reflected in the 779 | // BTree root 780 | // 781 | BtNodePos BTreeGetEqualOrBiggerNodeByKey( __in BTree* btree, 782 | __in BTreeKey* key, 783 | __in bool acquireLock ); 784 | 785 | errno_t BTreeUpdateTagsListPosition( __inout BTree* btree, 786 | __inout BTreeNode* node, 787 | __in int index, 788 | __in bool acquireLock ); 789 | 790 | errno_t BTreeDeleteFromTagsListPosition( __inout BTree* btree, 791 | __inout BTreeNode* node, 792 | __in int index, 793 | __in bool acquireLock ); 794 | 795 | // 796 | // synchronize the node with its copy in the backing store 797 | // 798 | errno_t BTreeFlushNode( __in BTree* btree, __in BTreeNode* node ); 799 | 800 | static BTreeKeyCmp compareOffsets( __in BTreeKey* key1, __in BTreeKey* key2); 801 | static BTreeKeyCmp compareTags( __in BTreeKey* key1, __in BTreeKey* key2); 802 | 803 | // 804 | // a B-Tree for sparse file offsets, 805 | // might be NULL if the file is small 806 | // enought to be processed using only the cache 807 | // 808 | BTree* sparseFileOffsetBTree; // optional 809 | 810 | // 811 | // an initial root node offset, do not used after the initialization was completed, 812 | // if set to InvalidValue a new root is created instead reading the existing one from the file 813 | // 814 | off_t sparseFileOffsetBTreeRootInit; 815 | 816 | // 817 | // an offset for the block with the oldest time stamp, this is an offset in the file 818 | // not in the data file representing the sparse file, i.e. the offset is a key for 819 | // the sparseFileOffsetBTree B-Tree 820 | // 821 | off_t oldestWrittenBlock; 822 | 823 | // 824 | // a list of cached BTreeNode entries, the list is not sorted 825 | // 826 | LIST_ENTRY cachedBTreeNodesList; 827 | 828 | // 829 | // a number of node entries in the cached list 830 | // 831 | SInt32 numberOfCachedNodes; 832 | 833 | 834 | 835 | public: 836 | 837 | static DldSparseFile* withPath( __in const char* sparseFilePath, 838 | __in_opt vnode_t cawlCoveringVnode, 839 | __in_opt unsigned char* cawlCoveredVnodeID, // char bytes[16] 840 | __in_opt const char* identificationString ); 841 | 842 | // 843 | // a descriptor for a single block IO operation, 844 | // the buffer should not cross the block, the IO 845 | // can be performed only for one block or its part 846 | // 847 | typedef struct _IoBlockDesriptor{ 848 | 849 | // 850 | // offset in the sparse file, allowed to be unaligned 851 | // but unaligned write can fail if there is no full block 852 | // found, in that case the blockIsNotPresent bit is set, 853 | // if the offset is set to InvalidOffset nothing 854 | // will be done 855 | // 856 | off_t fileOffset; 857 | 858 | // 859 | // a tag for write, ignored for read 860 | // 861 | BlockTag tag; 862 | 863 | // 864 | // ponter to data 865 | // 866 | void* buffer; 867 | 868 | // 869 | // data size, must be smaller or equal to BlockSize 870 | // 871 | unsigned int size; 872 | 873 | // 874 | // IO operation result, if failed check for blockIsNotPresent 875 | // flag that signals that the operation must be repeated with 876 | // an aligned bock for write and that the data is not present 877 | // for read, in the latter case a caller should either zero the 878 | // buffer or read the data from another source 879 | // 880 | errno_t error; 881 | 882 | struct{ 883 | 884 | // 885 | // 0x0 for read 886 | // 0x1 for write 887 | // 888 | unsigned int write:0x1; 889 | 890 | // 891 | // output value, if 0x1 then not aligned block write 892 | // was not processed as a block was not found in the 893 | // sparse file, for read means the same - the block 894 | // is not present in the file, the buffer is not zeroed 895 | // 896 | unsigned int blockIsNotPresent:0x1; 897 | 898 | } flags; 899 | 900 | } IoBlockDescriptor; 901 | 902 | void performIO( __inout IoBlockDescriptor* descriptors, __in unsigned int dscrCount ); 903 | 904 | // 905 | // flushes the data from this sparse file to coveredVnode up to fileDataSize, 906 | // the flushed data has the time stamp less or equal to timeStamp, a caller 907 | // is responsible for protecting from file trnuncation ( the growth is not a 908 | // problem as the new data will have a greater time stamp) 909 | // 910 | errno_t flushUpToTimeStamp( __in SInt64 timeStamp, 911 | __in vnode_t coveredVnode, 912 | __in off_t fileDataSize, 913 | __in vfs_context_t vfsContext ); 914 | 915 | // 916 | // purges data fromn the sparse file starting from the purgeOffset 917 | // 918 | errno_t purgeFromOffset( __in off_t purgeOffset ); 919 | 920 | // 921 | // returns a pointer to 16 bytes ID 922 | // 923 | const unsigned char* sparseFileID() { return this->identificationInfo.cawlCoveredVnodeID; }; 924 | 925 | // 926 | // sets a new CAWL related vnode, returns the old one 927 | // 928 | vnode_t exchangeCawlRelatedVnode( __in vnode_t ); 929 | 930 | // 931 | // prepares the sparse file object for reclaiming by releasing all external references 932 | // 933 | void prepareForReclaim(); 934 | 935 | // 936 | // aligns offset to lower block boundary 937 | // 938 | static off_t alignToBlock( __in off_t fileOffset ) { return (fileOffset & ~((off_t)DldSparseFile::BlockSize - 1)); } 939 | static off_t roundToBlock( __in off_t fileOffset ) { return (( fileOffset + (off_t)DldSparseFile::BlockSize - 1 ) & ~((off_t)DldSparseFile::BlockSize - 1)); } 940 | 941 | // 942 | // should be used mainly for fast checks or tests as doesn't acquire any lock 943 | // 944 | off_t getOldestWrittenBlockOffset() { return this->oldestWrittenBlock; } 945 | 946 | static void test(); 947 | 948 | }; 949 | 950 | //-------------------------------------------------------------------- 951 | 952 | class DldSparseFilesHashTable 953 | { 954 | 955 | private: 956 | 957 | ght_hash_table_t* HashTable; 958 | IORWLock* RWLock; 959 | 960 | #if defined(DBG) 961 | thread_t ExclusiveThread; 962 | #endif//DBG 963 | 964 | // 965 | // returns an allocated hash table object 966 | // 967 | static DldSparseFilesHashTable* withSize( int size, bool non_block ); 968 | 969 | // 970 | // free must be called before the hash table object is deleted 971 | // 972 | void free(); 973 | 974 | // 975 | // as usual for IOKit the desctructor and constructor do nothing 976 | // as it is impossible to return an error from the constructor 977 | // in the kernel mode 978 | // 979 | DldSparseFilesHashTable() 980 | { 981 | 982 | this->HashTable = NULL; 983 | this->RWLock = NULL; 984 | #if defined(DBG) 985 | this->ExclusiveThread = NULL; 986 | #endif//DBG 987 | 988 | } 989 | 990 | // 991 | // the destructor checks that the free() has been called 992 | // 993 | ~DldSparseFilesHashTable() 994 | { 995 | 996 | assert( !this->HashTable && !this->RWLock ); 997 | }; 998 | 999 | public: 1000 | 1001 | static bool CreateStaticTableWithSize( int size, bool non_block ); 1002 | static void DeleteStaticTable(); 1003 | 1004 | // 1005 | // adds an entry to the hash table, the entry is referenced so the caller must 1006 | // dereference the entry if it has been referenced 1007 | // 1008 | bool AddEntry( __in unsigned char* coveredVnodeID/*char[16]*/, __in DldSparseFile* entry ); 1009 | 1010 | // 1011 | // removes the entry from the hash and returns the removed entry, NULL if there 1012 | // is no entry for an object, the returned entry is referenced 1013 | // 1014 | DldSparseFile* RemoveEntry( __in const unsigned char* coveredVnodeID/*char[16]*/ ); 1015 | 1016 | // 1017 | // remove entry by object, i.e. removes the object from the hash 1018 | // a caller must not acquire the lock 1019 | // 1020 | void RemoveEntryByObject( __in DldSparseFile* sparseFile ); 1021 | 1022 | // 1023 | // returns an entry from the hash table, the returned entry is referenced 1024 | // if the refrence's value is "true" 1025 | // 1026 | DldSparseFile* RetrieveEntry( __in const unsigned char* coveredVnodeID/*char[16]*/, __in bool reference = true ); 1027 | 1028 | void 1029 | LockShared() 1030 | { assert( this->RWLock ); 1031 | assert( preemption_enabled() ); 1032 | 1033 | IORWLockRead( this->RWLock ); 1034 | }; 1035 | 1036 | 1037 | void 1038 | UnLockShared() 1039 | { assert( this->RWLock ); 1040 | assert( preemption_enabled() ); 1041 | 1042 | IORWLockUnlock( this->RWLock ); 1043 | }; 1044 | 1045 | 1046 | void 1047 | LockExclusive() 1048 | { 1049 | assert( this->RWLock ); 1050 | assert( preemption_enabled() ); 1051 | 1052 | #if defined(DBG) 1053 | assert( current_thread() != this->ExclusiveThread ); 1054 | #endif//DBG 1055 | 1056 | IORWLockWrite( this->RWLock ); 1057 | 1058 | #if defined(DBG) 1059 | assert( NULL == this->ExclusiveThread ); 1060 | this->ExclusiveThread = current_thread(); 1061 | #endif//DBG 1062 | 1063 | }; 1064 | 1065 | 1066 | void 1067 | UnLockExclusive() 1068 | { 1069 | assert( this->RWLock ); 1070 | assert( preemption_enabled() ); 1071 | 1072 | #if defined(DBG) 1073 | assert( current_thread() == this->ExclusiveThread ); 1074 | this->ExclusiveThread = NULL; 1075 | #endif//DBG 1076 | 1077 | IORWLockUnlock( this->RWLock ); 1078 | }; 1079 | 1080 | static DldSparseFilesHashTable* sSparseFilesHashTable; 1081 | }; 1082 | 1083 | //-------------------------------------------------------------------- 1084 | 1085 | #endif // _DLDSPARSEFILE_H --------------------------------------------------------------------------------