Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
sqstate.cpp
00001 /* 00002 see copyright notice in squirrel.h 00003 */ 00004 #include "sqpcheader.h" 00005 #include "sqopcodes.h" 00006 #include "sqvm.h" 00007 #include "sqfuncproto.h" 00008 #include "sqclosure.h" 00009 #include "sqstring.h" 00010 #include "sqtable.h" 00011 #include "sqarray.h" 00012 #include "squserdata.h" 00013 #include "sqclass.h" 00014 00015 //SQObjectPtr _null_; 00016 //SQObjectPtr _true_(true); 00017 //SQObjectPtr _false_(false); 00018 //SQObjectPtr _one_((SQInteger)1); 00019 //SQObjectPtr _minusone_((SQInteger)-1); 00020 00021 SQSharedState::SQSharedState() 00022 { 00023 _compilererrorhandler = NULL; 00024 _printfunc = NULL; 00025 _errorfunc = NULL; 00026 _debuginfo = false; 00027 _notifyallexceptions = false; 00028 } 00029 00030 #define newsysstring(s) { \ 00031 _systemstrings->push_back(SQString::Create(this,s)); \ 00032 } 00033 00034 #define newmetamethod(s) { \ 00035 _metamethods->push_back(SQString::Create(this,s)); \ 00036 _table(_metamethodsmap)->NewSlot(_metamethods->back(),(SQInteger)(_metamethods->size()-1)); \ 00037 } 00038 00039 bool CompileTypemask(SQIntVec &res,const SQChar *typemask) 00040 { 00041 SQInteger i = 0; 00042 00043 SQInteger mask = 0; 00044 while(typemask[i] != 0) { 00045 00046 switch(typemask[i]){ 00047 case 'o': mask |= _RT_NULL; break; 00048 case 'i': mask |= _RT_INTEGER; break; 00049 case 'f': mask |= _RT_FLOAT; break; 00050 case 'n': mask |= (_RT_FLOAT | _RT_INTEGER); break; 00051 case 's': mask |= _RT_STRING; break; 00052 case 't': mask |= _RT_TABLE; break; 00053 case 'a': mask |= _RT_ARRAY; break; 00054 case 'u': mask |= _RT_USERDATA; break; 00055 case 'c': mask |= (_RT_CLOSURE | _RT_NATIVECLOSURE); break; 00056 case 'b': mask |= _RT_BOOL; break; 00057 case 'g': mask |= _RT_GENERATOR; break; 00058 case 'p': mask |= _RT_USERPOINTER; break; 00059 case 'v': mask |= _RT_THREAD; break; 00060 case 'x': mask |= _RT_INSTANCE; break; 00061 case 'y': mask |= _RT_CLASS; break; 00062 case 'r': mask |= _RT_WEAKREF; break; 00063 case '.': mask = -1; res.push_back(mask); i++; mask = 0; continue; 00064 case ' ': i++; continue; //ignores spaces 00065 default: 00066 return false; 00067 } 00068 i++; 00069 if(typemask[i] == '|') { 00070 i++; 00071 if(typemask[i] == 0) 00072 return false; 00073 continue; 00074 } 00075 res.push_back(mask); 00076 mask = 0; 00077 00078 } 00079 return true; 00080 } 00081 00082 SQTable *CreateDefaultDelegate(SQSharedState *ss,SQRegFunction *funcz) 00083 { 00084 SQInteger i=0; 00085 SQTable *t=SQTable::Create(ss,0); 00086 while(funcz[i].name!=0){ 00087 SQNativeClosure *nc = SQNativeClosure::Create(ss,funcz[i].f,0); 00088 nc->_nparamscheck = funcz[i].nparamscheck; 00089 nc->_name = SQString::Create(ss,funcz[i].name); 00090 if(funcz[i].typemask && !CompileTypemask(nc->_typecheck,funcz[i].typemask)) 00091 return NULL; 00092 t->NewSlot(SQString::Create(ss,funcz[i].name),nc); 00093 i++; 00094 } 00095 return t; 00096 } 00097 00098 void SQSharedState::Init() 00099 { 00100 _scratchpad=NULL; 00101 _scratchpadsize=0; 00102 #ifndef NO_GARBAGE_COLLECTOR 00103 _gc_chain=NULL; 00104 #endif 00105 _stringtable = (SQStringTable*)SQ_MALLOC(sizeof(SQStringTable)); 00106 new (_stringtable) SQStringTable(this); 00107 sq_new(_metamethods,SQObjectPtrVec); 00108 sq_new(_systemstrings,SQObjectPtrVec); 00109 sq_new(_types,SQObjectPtrVec); 00110 _metamethodsmap = SQTable::Create(this,MT_LAST-1); 00111 //adding type strings to avoid memory trashing 00112 //types names 00113 newsysstring(_SC("null")); 00114 newsysstring(_SC("table")); 00115 newsysstring(_SC("array")); 00116 newsysstring(_SC("closure")); 00117 newsysstring(_SC("string")); 00118 newsysstring(_SC("userdata")); 00119 newsysstring(_SC("integer")); 00120 newsysstring(_SC("float")); 00121 newsysstring(_SC("userpointer")); 00122 newsysstring(_SC("function")); 00123 newsysstring(_SC("generator")); 00124 newsysstring(_SC("thread")); 00125 newsysstring(_SC("class")); 00126 newsysstring(_SC("instance")); 00127 newsysstring(_SC("bool")); 00128 //meta methods 00129 newmetamethod(MM_ADD); 00130 newmetamethod(MM_SUB); 00131 newmetamethod(MM_MUL); 00132 newmetamethod(MM_DIV); 00133 newmetamethod(MM_UNM); 00134 newmetamethod(MM_MODULO); 00135 newmetamethod(MM_SET); 00136 newmetamethod(MM_GET); 00137 newmetamethod(MM_TYPEOF); 00138 newmetamethod(MM_NEXTI); 00139 newmetamethod(MM_CMP); 00140 newmetamethod(MM_CALL); 00141 newmetamethod(MM_CLONED); 00142 newmetamethod(MM_NEWSLOT); 00143 newmetamethod(MM_DELSLOT); 00144 newmetamethod(MM_TOSTRING); 00145 newmetamethod(MM_NEWMEMBER); 00146 newmetamethod(MM_INHERITED); 00147 00148 _constructoridx = SQString::Create(this,_SC("constructor")); 00149 _registry = SQTable::Create(this,0); 00150 _consts = SQTable::Create(this,0); 00151 _table_default_delegate = CreateDefaultDelegate(this,_table_default_delegate_funcz); 00152 _array_default_delegate = CreateDefaultDelegate(this,_array_default_delegate_funcz); 00153 _string_default_delegate = CreateDefaultDelegate(this,_string_default_delegate_funcz); 00154 _number_default_delegate = CreateDefaultDelegate(this,_number_default_delegate_funcz); 00155 _closure_default_delegate = CreateDefaultDelegate(this,_closure_default_delegate_funcz); 00156 _generator_default_delegate = CreateDefaultDelegate(this,_generator_default_delegate_funcz); 00157 _thread_default_delegate = CreateDefaultDelegate(this,_thread_default_delegate_funcz); 00158 _class_default_delegate = CreateDefaultDelegate(this,_class_default_delegate_funcz); 00159 _instance_default_delegate = CreateDefaultDelegate(this,_instance_default_delegate_funcz); 00160 _weakref_default_delegate = CreateDefaultDelegate(this,_weakref_default_delegate_funcz); 00161 00162 } 00163 00164 SQSharedState::~SQSharedState() 00165 { 00166 _constructoridx.Null(); 00167 _table(_registry)->Finalize(); 00168 _table(_consts)->Finalize(); 00169 _table(_metamethodsmap)->Finalize(); 00170 _registry.Null(); 00171 _consts.Null(); 00172 _metamethodsmap.Null(); 00173 while(!_systemstrings->empty()) { 00174 _systemstrings->back().Null(); 00175 _systemstrings->pop_back(); 00176 } 00177 _thread(_root_vm)->Finalize(); 00178 _root_vm.Null(); 00179 _table_default_delegate.Null(); 00180 _array_default_delegate.Null(); 00181 _string_default_delegate.Null(); 00182 _number_default_delegate.Null(); 00183 _closure_default_delegate.Null(); 00184 _generator_default_delegate.Null(); 00185 _thread_default_delegate.Null(); 00186 _class_default_delegate.Null(); 00187 _instance_default_delegate.Null(); 00188 _weakref_default_delegate.Null(); 00189 _refs_table.Finalize(); 00190 #ifndef NO_GARBAGE_COLLECTOR 00191 SQCollectable *t = _gc_chain; 00192 SQCollectable *nx = NULL; 00193 if(t) { 00194 t->_uiRef++; 00195 while(t) { 00196 t->Finalize(); 00197 nx = t->_next; 00198 if(nx) nx->_uiRef++; 00199 if(--t->_uiRef == 0) 00200 t->Release(); 00201 t = nx; 00202 } 00203 } 00204 assert(_gc_chain==NULL); //just to proove a theory 00205 while(_gc_chain){ 00206 _gc_chain->_uiRef++; 00207 _gc_chain->Release(); 00208 } 00209 #endif 00210 00211 sq_delete(_types,SQObjectPtrVec); 00212 sq_delete(_systemstrings,SQObjectPtrVec); 00213 sq_delete(_metamethods,SQObjectPtrVec); 00214 sq_delete(_stringtable,SQStringTable); 00215 if(_scratchpad)SQ_FREE(_scratchpad,_scratchpadsize); 00216 } 00217 00218 00219 SQInteger SQSharedState::GetMetaMethodIdxByName(const SQObjectPtr &name) 00220 { 00221 if(type(name) != OT_STRING) 00222 return -1; 00223 SQObjectPtr ret; 00224 if(_table(_metamethodsmap)->Get(name,ret)) { 00225 return _integer(ret); 00226 } 00227 return -1; 00228 } 00229 00230 #ifndef NO_GARBAGE_COLLECTOR 00231 00232 void SQSharedState::MarkObject(SQObjectPtr &o,SQCollectable **chain) 00233 { 00234 switch(type(o)){ 00235 case OT_TABLE:_table(o)->Mark(chain);break; 00236 case OT_ARRAY:_array(o)->Mark(chain);break; 00237 case OT_USERDATA:_userdata(o)->Mark(chain);break; 00238 case OT_CLOSURE:_closure(o)->Mark(chain);break; 00239 case OT_NATIVECLOSURE:_nativeclosure(o)->Mark(chain);break; 00240 case OT_GENERATOR:_generator(o)->Mark(chain);break; 00241 case OT_THREAD:_thread(o)->Mark(chain);break; 00242 case OT_CLASS:_class(o)->Mark(chain);break; 00243 case OT_INSTANCE:_instance(o)->Mark(chain);break; 00244 case OT_OUTER:_outer(o)->Mark(chain);break; 00245 case OT_FUNCPROTO:_funcproto(o)->Mark(chain);break; 00246 default: break; //shutup compiler 00247 } 00248 } 00249 00250 00251 void SQSharedState::RunMark(SQVM *vm,SQCollectable **tchain) 00252 { 00253 SQVM *vms = _thread(_root_vm); 00254 00255 vms->Mark(tchain); 00256 00257 _refs_table.Mark(tchain); 00258 MarkObject(_registry,tchain); 00259 MarkObject(_consts,tchain); 00260 MarkObject(_metamethodsmap,tchain); 00261 MarkObject(_table_default_delegate,tchain); 00262 MarkObject(_array_default_delegate,tchain); 00263 MarkObject(_string_default_delegate,tchain); 00264 MarkObject(_number_default_delegate,tchain); 00265 MarkObject(_generator_default_delegate,tchain); 00266 MarkObject(_thread_default_delegate,tchain); 00267 MarkObject(_closure_default_delegate,tchain); 00268 MarkObject(_class_default_delegate,tchain); 00269 MarkObject(_instance_default_delegate,tchain); 00270 MarkObject(_weakref_default_delegate,tchain); 00271 00272 } 00273 00274 SQInteger SQSharedState::ResurrectUnreachable(SQVM *vm) 00275 { 00276 SQInteger n=0; 00277 SQCollectable *tchain=NULL; 00278 00279 RunMark(vm,&tchain); 00280 00281 SQCollectable *resurrected = _gc_chain; 00282 SQCollectable *t = resurrected; 00283 //SQCollectable *nx = NULL; 00284 00285 _gc_chain = tchain; 00286 00287 SQArray *ret = NULL; 00288 if(resurrected) { 00289 ret = SQArray::Create(this,0); 00290 SQCollectable *rlast = NULL; 00291 while(t) { 00292 rlast = t; 00293 SQObjectType type = t->GetType(); 00294 if(type != OT_FUNCPROTO && type != OT_OUTER) { 00295 SQObject sqo; 00296 sqo._type = type; 00297 sqo._unVal.pRefCounted = t; 00298 ret->Append(sqo); 00299 } 00300 t = t->_next; 00301 n++; 00302 } 00303 00304 assert(rlast->_next == NULL); 00305 rlast->_next = _gc_chain; 00306 if(_gc_chain) 00307 { 00308 _gc_chain->_prev = rlast; 00309 } 00310 _gc_chain = resurrected; 00311 } 00312 00313 t = _gc_chain; 00314 while(t) { 00315 t->UnMark(); 00316 t = t->_next; 00317 } 00318 00319 if(ret) { 00320 SQObjectPtr temp = ret; 00321 vm->Push(temp); 00322 } 00323 else { 00324 vm->PushNull(); 00325 } 00326 return n; 00327 } 00328 00329 SQInteger SQSharedState::CollectGarbage(SQVM *vm) 00330 { 00331 SQInteger n = 0; 00332 SQCollectable *tchain = NULL; 00333 00334 RunMark(vm,&tchain); 00335 00336 SQCollectable *t = _gc_chain; 00337 SQCollectable *nx = NULL; 00338 if(t) { 00339 t->_uiRef++; 00340 while(t) { 00341 t->Finalize(); 00342 nx = t->_next; 00343 if(nx) nx->_uiRef++; 00344 if(--t->_uiRef == 0) 00345 t->Release(); 00346 t = nx; 00347 n++; 00348 } 00349 } 00350 00351 t = tchain; 00352 while(t) { 00353 t->UnMark(); 00354 t = t->_next; 00355 } 00356 _gc_chain = tchain; 00357 00358 return n; 00359 } 00360 #endif 00361 00362 #ifndef NO_GARBAGE_COLLECTOR 00363 void SQCollectable::AddToChain(SQCollectable **chain,SQCollectable *c) 00364 { 00365 c->_prev = NULL; 00366 c->_next = *chain; 00367 if(*chain) (*chain)->_prev = c; 00368 *chain = c; 00369 } 00370 00371 void SQCollectable::RemoveFromChain(SQCollectable **chain,SQCollectable *c) 00372 { 00373 if(c->_prev) c->_prev->_next = c->_next; 00374 else *chain = c->_next; 00375 if(c->_next) 00376 c->_next->_prev = c->_prev; 00377 c->_next = NULL; 00378 c->_prev = NULL; 00379 } 00380 #endif 00381 00382 SQChar* SQSharedState::GetScratchPad(SQInteger size) 00383 { 00384 SQInteger newsize; 00385 if(size>0) { 00386 if(_scratchpadsize < size) { 00387 newsize = size + (size>>1); 00388 _scratchpad = (SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize); 00389 _scratchpadsize = newsize; 00390 00391 }else if(_scratchpadsize >= (size<<5)) { 00392 newsize = _scratchpadsize >> 1; 00393 _scratchpad = (SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize); 00394 _scratchpadsize = newsize; 00395 } 00396 } 00397 return _scratchpad; 00398 } 00399 00400 RefTable::RefTable() 00401 { 00402 AllocNodes(4); 00403 } 00404 00405 void RefTable::Finalize() 00406 { 00407 RefNode *nodes = _nodes; 00408 for(SQUnsignedInteger n = 0; n < _numofslots; n++) { 00409 nodes->obj.Null(); 00410 nodes++; 00411 } 00412 } 00413 00414 RefTable::~RefTable() 00415 { 00416 SQ_FREE(_buckets,(_numofslots * sizeof(RefNode *)) + (_numofslots * sizeof(RefNode))); 00417 } 00418 00419 #ifndef NO_GARBAGE_COLLECTOR 00420 void RefTable::Mark(SQCollectable **chain) 00421 { 00422 RefNode *nodes = (RefNode *)_nodes; 00423 for(SQUnsignedInteger n = 0; n < _numofslots; n++) { 00424 if(type(nodes->obj) != OT_NULL) { 00425 SQSharedState::MarkObject(nodes->obj,chain); 00426 } 00427 nodes++; 00428 } 00429 } 00430 #endif 00431 00432 void RefTable::AddRef(SQObject &obj) 00433 { 00434 SQHash mainpos; 00435 RefNode *prev; 00436 RefNode *ref = Get(obj,mainpos,&prev,true); 00437 ref->refs++; 00438 } 00439 00440 SQUnsignedInteger RefTable::GetRefCount(SQObject &obj) 00441 { 00442 SQHash mainpos; 00443 RefNode *prev; 00444 RefNode *ref = Get(obj,mainpos,&prev,true); 00445 return ref->refs; 00446 } 00447 00448 00449 SQBool RefTable::Release(SQObject &obj) 00450 { 00451 SQHash mainpos; 00452 RefNode *prev; 00453 RefNode *ref = Get(obj,mainpos,&prev,false); 00454 if(ref) { 00455 if(--ref->refs == 0) { 00456 SQObjectPtr o = ref->obj; 00457 if(prev) { 00458 prev->next = ref->next; 00459 } 00460 else { 00461 _buckets[mainpos] = ref->next; 00462 } 00463 ref->next = _freelist; 00464 _freelist = ref; 00465 _slotused--; 00466 ref->obj.Null(); 00467 //<<FIXME>>test for shrink? 00468 return SQTrue; 00469 } 00470 } 00471 else { 00472 assert(0); 00473 } 00474 return SQFalse; 00475 } 00476 00477 void RefTable::Resize(SQUnsignedInteger size) 00478 { 00479 RefNode **oldbucks = _buckets; 00480 RefNode *t = _nodes; 00481 SQUnsignedInteger oldnumofslots = _numofslots; 00482 AllocNodes(size); 00483 //rehash 00484 SQUnsignedInteger nfound = 0; 00485 for(SQUnsignedInteger n = 0; n < oldnumofslots; n++) { 00486 if(type(t->obj) != OT_NULL) { 00487 //add back; 00488 assert(t->refs != 0); 00489 RefNode *nn = Add(::HashObj(t->obj)&(_numofslots-1),t->obj); 00490 nn->refs = t->refs; 00491 t->obj.Null(); 00492 nfound++; 00493 } 00494 t++; 00495 } 00496 assert(nfound == oldnumofslots); 00497 SQ_FREE(oldbucks,(oldnumofslots * sizeof(RefNode *)) + (oldnumofslots * sizeof(RefNode))); 00498 } 00499 00500 RefTable::RefNode *RefTable::Add(SQHash mainpos,SQObject &obj) 00501 { 00502 RefNode *t = _buckets[mainpos]; 00503 RefNode *newnode = _freelist; 00504 newnode->obj = obj; 00505 _buckets[mainpos] = newnode; 00506 _freelist = _freelist->next; 00507 newnode->next = t; 00508 assert(newnode->refs == 0); 00509 _slotused++; 00510 return newnode; 00511 } 00512 00513 RefTable::RefNode *RefTable::Get(SQObject &obj,SQHash &mainpos,RefNode **prev,bool add) 00514 { 00515 RefNode *ref; 00516 mainpos = ::HashObj(obj)&(_numofslots-1); 00517 *prev = NULL; 00518 for (ref = _buckets[mainpos]; ref; ) { 00519 if(_rawval(ref->obj) == _rawval(obj) && type(ref->obj) == type(obj)) 00520 break; 00521 *prev = ref; 00522 ref = ref->next; 00523 } 00524 if(ref == NULL && add) { 00525 if(_numofslots == _slotused) { 00526 assert(_freelist == 0); 00527 Resize(_numofslots*2); 00528 mainpos = ::HashObj(obj)&(_numofslots-1); 00529 } 00530 ref = Add(mainpos,obj); 00531 } 00532 return ref; 00533 } 00534 00535 void RefTable::AllocNodes(SQUnsignedInteger size) 00536 { 00537 RefNode **bucks; 00538 RefNode *nodes; 00539 bucks = (RefNode **)SQ_MALLOC((size * sizeof(RefNode *)) + (size * sizeof(RefNode))); 00540 nodes = (RefNode *)&bucks[size]; 00541 RefNode *temp = nodes; 00542 SQUnsignedInteger n; 00543 for(n = 0; n < size - 1; n++) { 00544 bucks[n] = NULL; 00545 temp->refs = 0; 00546 new (&temp->obj) SQObjectPtr; 00547 temp->next = temp+1; 00548 temp++; 00549 } 00550 bucks[n] = NULL; 00551 temp->refs = 0; 00552 new (&temp->obj) SQObjectPtr; 00553 temp->next = NULL; 00554 _freelist = nodes; 00555 _nodes = nodes; 00556 _buckets = bucks; 00557 _slotused = 0; 00558 _numofslots = size; 00559 } 00560 ////////////////////////////////////////////////////////////////////////// 00561 //SQStringTable 00562 /* 00563 * The following code is based on Lua 4.0 (Copyright 1994-2002 Tecgraf, PUC-Rio.) 00564 * http://www.lua.org/copyright.html#4 00565 * http://www.lua.org/source/4.0.1/src_lstring.c.html 00566 */ 00567 00568 SQStringTable::SQStringTable(SQSharedState *ss) 00569 { 00570 _sharedstate = ss; 00571 AllocNodes(4); 00572 _slotused = 0; 00573 } 00574 00575 SQStringTable::~SQStringTable() 00576 { 00577 SQ_FREE(_strings,sizeof(SQString*)*_numofslots); 00578 _strings = NULL; 00579 } 00580 00581 void SQStringTable::AllocNodes(SQInteger size) 00582 { 00583 _numofslots = size; 00584 _strings = (SQString**)SQ_MALLOC(sizeof(SQString*)*_numofslots); 00585 memset(_strings,0,sizeof(SQString*)*_numofslots); 00586 } 00587 00588 SQString *SQStringTable::Add(const SQChar *news,SQInteger len) 00589 { 00590 if(len<0) 00591 len = (SQInteger)scstrlen(news); 00592 SQHash newhash = ::_hashstr(news,len); 00593 SQHash h = newhash&(_numofslots-1); 00594 SQString *s; 00595 for (s = _strings[h]; s; s = s->_next){ 00596 if(s->_len == len && (!memcmp(news,s->_val,rsl(len)))) 00597 return s; //found 00598 } 00599 00600 SQString *t = (SQString *)SQ_MALLOC(rsl(len)+sizeof(SQString)); 00601 new (t) SQString; 00602 t->_sharedstate = _sharedstate; 00603 memcpy(t->_val,news,rsl(len)); 00604 t->_val[len] = _SC('\0'); 00605 t->_len = len; 00606 t->_hash = newhash; 00607 t->_next = _strings[h]; 00608 _strings[h] = t; 00609 _slotused++; 00610 if (_slotused > _numofslots) /* too crowded? */ 00611 Resize(_numofslots*2); 00612 return t; 00613 } 00614 00615 void SQStringTable::Resize(SQInteger size) 00616 { 00617 SQInteger oldsize=_numofslots; 00618 SQString **oldtable=_strings; 00619 AllocNodes(size); 00620 for (SQInteger i=0; i<oldsize; i++){ 00621 SQString *p = oldtable[i]; 00622 while(p){ 00623 SQString *next = p->_next; 00624 SQHash h = p->_hash&(_numofslots-1); 00625 p->_next = _strings[h]; 00626 _strings[h] = p; 00627 p = next; 00628 } 00629 } 00630 SQ_FREE(oldtable,oldsize*sizeof(SQString*)); 00631 } 00632 00633 void SQStringTable::Remove(SQString *bs) 00634 { 00635 SQString *s; 00636 SQString *prev=NULL; 00637 SQHash h = bs->_hash&(_numofslots - 1); 00638 00639 for (s = _strings[h]; s; ){ 00640 if(s == bs){ 00641 if(prev) 00642 prev->_next = s->_next; 00643 else 00644 _strings[h] = s->_next; 00645 _slotused--; 00646 SQInteger slen = s->_len; 00647 s->~SQString(); 00648 SQ_FREE(s,sizeof(SQString) + rsl(slen)); 00649 return; 00650 } 00651 prev = s; 00652 s = s->_next; 00653 } 00654 assert(0);//if this fail something is wrong 00655 }
Generated on Tue Jul 12 2022 21:35:49 by
