core/systems/HandleSys.cpp
author David Anderson <dvander@alliedmods.net>
Mon Jan 01 19:50:16 2007 +0000 (2007-01-01)
changeset 252 ce166c465bad
parent 247 c110039da326
child 259 ca9b124a2544
permissions -rw-r--r--
added usage of the Handle System to begin experimenting
     1 #include "HandleSys.h"
     2 #include "ShareSys.h"
     3 #include "PluginSys.h"
     4 #include <assert.h>
     5 
     6 HandleSystem g_HandleSys;
     7 
     8 QHandle *ignore_handle;
     9 
    10 inline HandleType_t TypeParent(HandleType_t type)
    11 {
    12 	return (type & ~HANDLESYS_SUBTYPE_MASK);
    13 }
    14 
    15 inline HandleError IdentityHandle(IdentityToken_t *token, unsigned int *index)
    16 {
    17 	return g_HandleSys.GetHandle(token->ident, g_ShareSys.GetIdentRoot(), &ignore_handle, index, HandleAccess_Read);
    18 }
    19 
    20 HandleSystem::HandleSystem()
    21 {
    22 	m_Handles = new QHandle[HANDLESYS_MAX_HANDLES + 1];
    23 	memset(m_Handles, 0, sizeof(QHandle) * (HANDLESYS_MAX_HANDLES + 1));
    24 
    25 	m_Types = new QHandleType[HANDLESYS_TYPEARRAY_SIZE];
    26 	memset(m_Types, 0, sizeof(QHandleType) * HANDLESYS_TYPEARRAY_SIZE);
    27 
    28 	m_TypeLookup = sm_trie_create();
    29 	m_strtab = new BaseStringTable(512);
    30 
    31 	m_TypeTail = 0;
    32 }
    33 
    34 HandleSystem::~HandleSystem()
    35 {
    36 	delete [] m_Handles;
    37 	delete [] m_Types;
    38 	sm_trie_destroy(m_TypeLookup);
    39 	delete m_strtab;
    40 }
    41 
    42 HandleType_t HandleSystem::CreateType(const char *name, IHandleTypeDispatch *dispatch)
    43 {
    44 	return CreateTypeEx(name, dispatch, 0, NULL, NULL);
    45 }
    46 
    47 HandleType_t HandleSystem::CreateChildType(const char *name, HandleType_t parent, IHandleTypeDispatch *dispatch)
    48 {
    49 	return CreateTypeEx(name, dispatch, parent, NULL, NULL);
    50 }
    51 
    52 HandleType_t HandleSystem::CreateTypeEx(const char *name, 
    53 										IHandleTypeDispatch *dispatch, 
    54 										HandleType_t parent, 
    55 										const HandleSecurity *security,
    56 										IdentityToken_t *ident)
    57 {
    58 	if (!dispatch)
    59 	{
    60 		return 0;
    61 	}
    62 
    63 	bool isChild = false;
    64 
    65 	if (parent != 0)
    66 	{
    67 		isChild = true;
    68 		if (parent & HANDLESYS_SUBTYPE_MASK)
    69 		{
    70 			return 0;
    71 		}
    72 		if (parent >= HANDLESYS_TYPEARRAY_SIZE
    73 			|| m_Types[parent].dispatch == NULL
    74 			|| (m_Types[parent].sec.access[HandleAccess_Inherit] == false
    75 				&& m_Types[parent].sec.owner != ident))
    76 		{
    77 			return 0;
    78 		}
    79 	}
    80 
    81 	if (!security)
    82 	{
    83 		if (isChild)
    84 		{
    85 			security = &m_Types[parent].sec;
    86 		} else {
    87 			static HandleSecurity def_h;
    88 			security = &def_h;
    89 		}
    90 	}
    91 
    92 	if (security->access[HandleAccess_Create]
    93 		&& sm_trie_retrieve(m_TypeLookup, name, NULL))
    94 	{
    95 		return 0;
    96 	}
    97 
    98 	unsigned int index;
    99 
   100 	if (isChild)
   101 	{
   102 		QHandleType *pParent = &m_Types[parent];
   103 		if (pParent->children >= HANDLESYS_MAX_SUBTYPES)
   104 		{
   105 			return 0;
   106 		}
   107 		index = 0;
   108 		for (unsigned int i=1; i<=HANDLESYS_MAX_SUBTYPES; i++)
   109 		{
   110 			if (m_Types[parent + i].dispatch == NULL)
   111 			{
   112 				index = parent + i;
   113 				break;
   114 			}
   115 		}
   116 		if (!index)
   117 		{
   118 			return 0;
   119 		}
   120 		pParent->children++;
   121 	} else {
   122 		if (m_FreeTypes == 0)
   123 		{
   124 			/* Reserve another index */
   125 			if (m_TypeTail >= HANDLESYS_TYPEARRAY_SIZE)
   126 			{
   127 				return 0;
   128 			} else {
   129 				m_TypeTail += (HANDLESYS_MAX_SUBTYPES + 1);
   130 				index = m_TypeTail;
   131 			}
   132 		} else {
   133 			/* The "free array" is compacted into the normal array for easiness */
   134 			index = m_Types[m_FreeTypes--].freeID;
   135 		}
   136 	}
   137 
   138 	QHandleType *pType = &m_Types[index];
   139 	
   140 	pType->dispatch = dispatch;
   141 	if (name && name[0] != '\0')
   142 	{
   143 		pType->nameIdx = m_strtab->AddString(name);
   144 		sm_trie_insert(m_TypeLookup, name, (void *)pType);
   145 	} else {
   146 		pType->nameIdx = -1;
   147 	}
   148 	pType->sec = *security;
   149 	pType->opened = 0;
   150 
   151 	if (!isChild)
   152 	{
   153 		pType->children = 0;
   154 	}
   155 
   156 	return index;
   157 }
   158 
   159 bool HandleSystem::FindHandleType(const char *name, HandleType_t *type)
   160 {
   161 	QHandleType *_type;
   162 
   163 	if (!sm_trie_retrieve(m_TypeLookup, name, (void **)&_type))
   164 	{
   165 		return false;
   166 	}
   167 
   168 	unsigned int offset = _type - m_Types;
   169 
   170 	if (type)
   171 	{
   172 		*type = offset;
   173 	}
   174 
   175 	return true;
   176 }
   177 
   178 HandleError HandleSystem::MakePrimHandle(HandleType_t type, 
   179 						   QHandle **in_pHandle, 
   180 						   unsigned int *in_index, 
   181 						   Handle_t *in_handle,
   182 						   IdentityToken_t *owner)
   183 {
   184 	unsigned int owner_index = 0;
   185 
   186 	if (owner && (IdentityHandle(owner, &owner_index) != HandleError_None))
   187 	{
   188 		return HandleError_Identity;
   189 	}
   190 
   191 	unsigned int handle;
   192 	if (m_FreeHandles == 0)
   193 	{
   194 		if (m_HandleTail >= HANDLESYS_MAX_HANDLES)
   195 		{
   196 			return HandleError_Limit;;
   197 		}
   198 		handle = ++m_HandleTail;
   199 	} else {
   200 		handle = m_Handles[m_FreeHandles--].freeID;
   201 	}
   202 
   203 	QHandle *pHandle = &m_Handles[handle];
   204 	
   205 	assert(pHandle->set == false);
   206 
   207 	if (++m_HSerial >= HANDLESYS_MAX_SERIALS)
   208 	{
   209 		m_HSerial = 1;
   210 	}
   211 
   212 	/* Set essential information */
   213 	pHandle->set = HandleSet_Used;;
   214 	pHandle->refcount = 1;
   215 	pHandle->type = type;
   216 	pHandle->serial = m_HSerial;
   217 	pHandle->owner = owner;
   218 	pHandle->ch_next = 0;
   219 
   220 	/* Create the hash value */
   221 	Handle_t hash = pHandle->serial;
   222 	hash <<= 16;
   223 	hash |= handle;
   224 
   225 	/* Add a reference count to the type */
   226 	m_Types[type].opened++;
   227 
   228 	/* Output */
   229 	*in_pHandle = pHandle;
   230 	*in_index = handle;
   231 	*in_handle = hash;
   232 
   233 	/* Decode the identity token */
   234 	if (owner)
   235 	{
   236 		QHandle *pIdentity = &m_Handles[owner_index];
   237 		if (pIdentity->ch_prev == 0)
   238 		{
   239 			pIdentity->ch_prev = handle;
   240 			pIdentity->ch_next = handle;
   241 			pHandle->ch_prev = 0;
   242 		} else {
   243 			/* Link previous node to us (forward) */
   244 			m_Handles[pIdentity->ch_next].ch_next = handle;
   245 			/* Link us to previous node (backwards) */
   246 			pHandle->ch_prev = pIdentity->ch_next;
   247 			/* Set new tail */
   248 			pIdentity->ch_next = handle;
   249 		}
   250 		pIdentity->refcount++;
   251 	} else {
   252 		pHandle->ch_prev = 0;
   253 	}
   254 
   255 	return HandleError_None;
   256 }
   257 
   258 void HandleSystem::SetTypeSecurityOwner(HandleType_t type, IdentityToken_t *pToken)
   259 {
   260 	if (!type
   261 		|| type >= HANDLESYS_TYPEARRAY_SIZE
   262 		|| m_Types[type].dispatch == NULL)
   263 	{
   264 		return;
   265 	}
   266 
   267 	m_Types[type].sec.owner = pToken;
   268 }
   269 
   270 Handle_t HandleSystem::CreateHandle(HandleType_t type, void *object, IdentityToken_t *source, IdentityToken_t *ident)
   271 {
   272 	if (!type 
   273 		|| type >= HANDLESYS_TYPEARRAY_SIZE
   274 		|| m_Types[type].dispatch == NULL)
   275 	{
   276 		return 0;
   277 	}
   278 
   279 	/* Check the security of this handle */
   280 	QHandleType *pType = &m_Types[type];
   281 	if (!pType->sec.access[HandleAccess_Create]
   282 		&& pType->sec.owner != ident)
   283 	{
   284 		return 0;
   285 	}
   286 
   287 	unsigned int index;
   288 	Handle_t handle;
   289 	QHandle *pHandle;
   290 	HandleError err;
   291 
   292 	if ((err=MakePrimHandle(type, &pHandle, &index, &handle, source)) != HandleError_None)
   293 	{
   294 		return 0;
   295 	}
   296 
   297 	pHandle->object = object;
   298 	pHandle->clone = 0;
   299 
   300 	return handle;
   301 }
   302 
   303 Handle_t HandleSystem::CreateScriptHandle(HandleType_t type, 
   304 										  void *object, 
   305 										  IPluginContext *pContext, 
   306 										  IdentityToken_t *ident)
   307 {
   308 	IPlugin *pPlugin = g_PluginSys.FindPluginByContext(pContext->GetContext());
   309 
   310 	return CreateHandle(type, object, pPlugin->GetIdentity(), ident);
   311 }
   312 
   313 bool HandleSystem::TypeCheck(HandleType_t intype, HandleType_t outtype)
   314 {
   315 	/* Check the type inheritance */
   316 	if (intype & HANDLESYS_SUBTYPE_MASK)
   317 	{
   318 		if (intype != outtype
   319 			&& (TypeParent(intype) != TypeParent(outtype)))
   320 		{
   321 			return false;
   322 		}
   323 	} else {
   324 		if (intype != outtype)
   325 		{
   326 			return false;
   327 		}
   328 	}
   329 
   330 	return true;
   331 }
   332 
   333 HandleError HandleSystem::GetHandle(Handle_t handle,
   334 									IdentityToken_t *ident, 
   335 									QHandle **in_pHandle, 
   336 									unsigned int *in_index,
   337 									HandleAccessRight access)
   338 {
   339 	unsigned int serial = (handle >> 16);
   340 	unsigned int index = (handle & HANDLESYS_HANDLE_MASK);
   341 
   342 	if (index == 0 || index == 0xFFFF)
   343 	{
   344 		return HandleError_Index;
   345 	}
   346 
   347 	QHandle *pHandle = &m_Handles[index];
   348 	QHandleType *pType = &m_Types[pHandle->type];
   349 
   350 	if ((access != HandleAccess_TOTAL)
   351 		&& (!pType->sec.access[access] && pType->sec.owner != ident))
   352 	{
   353 		return HandleError_Access;
   354 	}
   355 	if (!pHandle->set)
   356 	{
   357 		return HandleError_Freed;
   358 	} else if (pHandle->set == HandleSet_Identity
   359 			   && ident != g_ShareSys.GetIdentRoot())
   360 	{
   361 		/* Only IdentityHandle() can read this! */
   362 		return HandleError_Identity;
   363 	}
   364 	if (pHandle->serial != serial)
   365 	{
   366 		return HandleError_Changed;
   367 	}
   368 
   369 	*in_pHandle = pHandle;
   370 	*in_index = index;
   371 
   372 	return HandleError_None;
   373 }
   374 
   375 HandleError HandleSystem::CloneHandle(Handle_t handle, Handle_t *out_newhandle, IdentityToken_t *source, IdentityToken_t *ident)
   376 {
   377 	HandleError err;
   378 	QHandle *pHandle;
   379 	unsigned int index;
   380 
   381 	if ((err=GetHandle(handle, ident, &pHandle, &index, HandleAccess_Clone)) != HandleError_None)
   382 	{
   383 		return err;
   384 	}
   385 
   386 	QHandleType *pType = &m_Types[pHandle->type];
   387 
   388 	/* Get a new Handle ID */
   389 	unsigned int new_index;
   390 	QHandle *pNewHandle;
   391 	Handle_t new_handle;
   392 
   393 	if ((err=MakePrimHandle(pHandle->type, &pNewHandle, &new_index, &new_handle, source)) != HandleError_None)
   394 	{
   395 		return err;
   396 	}
   397 
   398 	pNewHandle->clone = index;
   399 
   400 	if (out_newhandle)
   401 	{
   402 		*out_newhandle = new_handle;
   403 	}
   404 
   405 	return HandleError_None;
   406 }
   407 
   408 HandleError HandleSystem::FreeHandle(Handle_t handle, IdentityToken_t *ident)
   409 {
   410 	unsigned int index;
   411 	QHandle *pHandle;
   412 	HandleError err;
   413 
   414 	if ((err=GetHandle(handle, ident, &pHandle, &index, HandleAccess_Delete)) != HandleError_None)
   415 	{
   416 		return err;
   417 	}
   418 
   419 	QHandleType *pType = &m_Types[pHandle->type];
   420 
   421 	bool dofree = false;
   422 	if (pHandle->clone)
   423 	{
   424 		/* If we're a clone, there's not a lot to do. */
   425 		if (FreeHandle(pHandle->clone, ident) != HandleError_None)
   426 		{
   427 			assert(false);
   428 		}
   429 		dofree = true;
   430 	} else {
   431 		if (--pHandle->refcount == 0)
   432 		{
   433 			dofree = true;
   434 			pType->dispatch->OnHandleDestroy(pHandle->type, pHandle->object);
   435 		}
   436 	}
   437 
   438 	if (dofree)
   439 	{
   440 		ReleasePrimHandle(index);
   441 	}
   442 
   443 	return HandleError_None;
   444 }
   445 
   446 HandleError HandleSystem::ReadHandle(Handle_t handle, 
   447 									 HandleType_t type, 
   448 									 IdentityToken_t *ident, 
   449 									 void **object)
   450 {
   451 	unsigned int index;
   452 	QHandle *pHandle;
   453 	HandleError err;
   454 
   455 	if ((err=GetHandle(handle, ident, &pHandle, &index, HandleAccess_Read)) != HandleError_None)
   456 	{
   457 		return err;
   458 	}
   459 
   460 	/* Check the type inheritance */
   461 	if (pHandle->type & HANDLESYS_SUBTYPE_MASK)
   462 	{
   463 		if (pHandle->type != type
   464 			&& (TypeParent(pHandle->type) != TypeParent(type)))
   465 		{
   466 			return HandleError_Type;
   467 		}
   468 	} else if (type) {
   469 		if (pHandle->type != type)
   470 		{
   471 			return HandleError_Type;
   472 		}
   473 	}
   474 
   475 	if (object)
   476 	{
   477 		/* if we're a clone, the rules change - object is ONLY in our reference */
   478 		if (pHandle->clone)
   479 		{
   480 			pHandle = &m_Handles[pHandle->clone];
   481 		}
   482 		*object = pHandle->object;
   483 	}
   484 
   485 	return HandleError_None;
   486 }
   487 
   488 void HandleSystem::ReleasePrimHandle(unsigned int index)
   489 {
   490 	QHandle *pHandle = &m_Handles[index];
   491 	
   492 	pHandle->set = HandleSet_None;
   493 	m_Types[pHandle->type].opened--;
   494 	m_Handles[++m_FreeHandles].freeID = index;
   495 
   496 	/* Unlink us if necessary */
   497 	if (pHandle->owner)
   498 	{
   499 		unsigned int ident_index;
   500 		if (IdentityHandle(pHandle->owner, &ident_index) != HandleError_None)
   501 		{
   502 			/* Uh-oh! */
   503 			assert(pHandle->owner == 0);
   504 			return;
   505 		}
   506 		/* Note that since 0 is an invalid handle, if any of these links are 0,
   507 		 * the data can still be set.
   508 		 */
   509 		QHandle *pIdentity = &m_Handles[ident_index];
   510 
   511 		/* Unlink case: We're the head AND tail node */
   512 		if (index == pIdentity->ch_prev && index == pIdentity->ch_next)
   513 		{
   514 			pIdentity->ch_prev = 0;
   515 			pIdentity->ch_next = 0;
   516 		}
   517 		/* Unlink case: We're the head node */
   518 		else if (index == pIdentity->ch_prev) {
   519 			/* Link us to the next in the chain */
   520 			pIdentity->ch_prev = pHandle->ch_next;
   521 			/* Patch up the previous link */
   522 			m_Handles[pHandle->ch_next].ch_prev = 0;
   523 		}
   524 		/* Unlink case: We're the tail node */
   525 		else if (index == pIdentity->ch_next) {
   526 			/* Link us to the previous in the chain */
   527 			pIdentity->ch_next = pHandle->ch_prev;
   528 			/* Patch up the next link */
   529 			m_Handles[pHandle->ch_prev].ch_next = 0;
   530 		}
   531 		/* Unlink case: We're in the middle! */
   532 		else {
   533 			/* Patch the forward reference */
   534 			m_Handles[pHandle->ch_next].ch_prev = pHandle->ch_prev;
   535 			/* Patch the backward reference */
   536 			m_Handles[pHandle->ch_prev].ch_next = pHandle->ch_next;
   537 		}
   538 
   539 		/* Lastly, decrease the reference count */
   540 		pIdentity->refcount--;
   541 	}
   542 }
   543 
   544 bool HandleSystem::RemoveType(HandleType_t type, IdentityToken_t *ident)
   545 {
   546 	if (type == 0 || type >= HANDLESYS_TYPEARRAY_SIZE)
   547 	{
   548 		return false;
   549 	}
   550 
   551 	QHandleType *pType = &m_Types[type];
   552 
   553 	if (pType->sec.owner && pType->sec.owner != ident)
   554 	{
   555 		return false;
   556 	}
   557 
   558 	if (pType->dispatch == NULL)
   559 	{
   560 		return false;
   561 	}
   562 
   563 	/* Remove children if we have to */
   564 	if (!(type & HANDLESYS_SUBTYPE_MASK))
   565 	{
   566 		QHandleType *childType;
   567 		for (unsigned int i=1; i<=HANDLESYS_MAX_SUBTYPES; i++)
   568 		{
   569 			childType = &m_Types[type + i];
   570 			if (childType->dispatch)
   571 			{
   572 				RemoveType(type + i, childType->sec.owner);
   573 			}
   574 		}
   575 		/* Link us into the free chain */
   576 		m_Types[++m_FreeTypes].freeID = type;
   577 	}
   578 
   579 	/* Invalidate the type now */
   580 	IHandleTypeDispatch *dispatch = pType->dispatch;
   581 	pType->dispatch = NULL;
   582 
   583 	/* Make sure nothing is using this type. */
   584 	if (pType->opened)
   585 	{
   586 		QHandle *pHandle;
   587 		for (unsigned int i=1; i<m_HandleTail; i++)
   588 		{
   589 			pHandle = &m_Handles[i];
   590 			if (!pHandle->set || pHandle->type != type)
   591 			{
   592 				continue;
   593 			}
   594 			if (pHandle->clone)
   595 			{
   596 				/* Get parent */
   597 				QHandle *pOther = &m_Handles[pHandle->clone];
   598 				if (--pOther->refcount == 0)
   599 				{
   600 					/* Free! */
   601 					dispatch->OnHandleDestroy(type, pOther->object);
   602 					ReleasePrimHandle(pHandle->clone);
   603 				}
   604 				/* Unlink ourselves since we don't have a reference count */
   605 				ReleasePrimHandle(i);
   606 			} else {
   607 				/* If it's not a clone, we still have to check the reference count.
   608 				 * Either way, we'll be destroyed eventually because the handle types do not change.
   609 				 */
   610 				if (--pHandle->refcount == 0)
   611 				{
   612 					/* Free! */
   613 					dispatch->OnHandleDestroy(type, pHandle->object);
   614 					ReleasePrimHandle(i);
   615 				}
   616 			}
   617 			if (pType->opened == 0)
   618 			{
   619 				break;
   620 			}
   621 		}
   622 	}
   623 
   624 	return true;
   625 }
   626 
   627 void HandleSystem::MarkHandleAsIdentity(Handle_t handle)
   628 {
   629 	QHandle *pHandle;
   630 	unsigned int index;
   631 
   632 	if (GetHandle(handle, g_ShareSys.GetIdentRoot(), &pHandle, &index, HandleAccess_Read) != HandleError_None)
   633 	{
   634 		return;
   635 	}
   636 
   637 	pHandle->set = HandleSet_Identity;
   638 }