1 /********************************************************************************
2 *                                                                               *
3 *                         M e t a C l a s s   O b j e c t                       *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 1997,2021 by Jeroen van der Zijp.   All Rights Reserved.        *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or modify          *
9 * it under the terms of the GNU Lesser General Public License as published by   *
10 * the Free Software Foundation; either version 3 of the License, or             *
11 * (at your option) any later version.                                           *
12 *                                                                               *
13 * This library is distributed in the hope that it will be useful,               *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of                *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                 *
16 * GNU Lesser General Public License for more details.                           *
17 *                                                                               *
18 * You should have received a copy of the GNU Lesser General Public License      *
19 * along with this program.  If not, see <http://www.gnu.org/licenses/>          *
20 ********************************************************************************/
21 #include "xincs.h"
22 #include "fxver.h"
23 #include "fxdefs.h"
24 #include "fxmath.h"
25 #include "FXElement.h"
26 #include "FXArray.h"
27 #include "FXMetaClass.h"
28 #include "FXObject.h"
29 
30 /*
31   Notes:
32 
33   - We need a table of all metaclasses, as we should be able to create any type
34     of object during deserialization.
35   - For MacOS/X support, we moved fxmalloc() and co. here; the reason is that
36     when FOX is loaded as a DLL into FXRuby, these symbols need to be resolvable
37     in order for the DLL startup code to run properly for the meta class
38     initializers; afterward everything's OK.
39   - For abstract classes, FXMetaClass contains nullObject() as manufacture function;
40     this will always return NULL as abstract classes can't be instantiated.
41   - Possibly store hash into FXMetaClass during construction.  Benefits are:
42       - No need to recompute it during destruction or growth of hash table.
43       - Quick equality test inside getMetaClassFromName().
44       - Very minor space penalty.
45 */
46 
47 #define TOPIC_CONSTRUCT 1000
48 
49 using namespace FX;
50 
51 namespace FX {
52 
53 /***********************  Memory Allocation Functions  *************************/
54 
55 // Allocate memory
fxmalloc(void ** ptr,FXuval size)56 FXbool fxmalloc(void** ptr,FXuval size){
57   *ptr=NULL;
58   if(size!=0){
59     if((*ptr=malloc(size))==NULL) return false;
60     }
61   return true;
62   }
63 
64 
65 // Allocate cleaned memory
fxcalloc(void ** ptr,FXuval size)66 FXbool fxcalloc(void** ptr,FXuval size){
67   *ptr=NULL;
68   if(size!=0){
69     if((*ptr=calloc(size,1))==NULL) return false;
70     }
71   return true;
72   }
73 
74 
75 // Resize memory
fxresize(void ** ptr,FXuval size)76 FXbool fxresize(void** ptr,FXuval size){
77   void *p=NULL;
78   if(size!=0){
79     if((p=realloc(*ptr,size))==NULL) return false;
80     }
81   else{
82     if(*ptr) free(*ptr);
83     }
84   *ptr=p;
85   return true;
86   }
87 
88 
89 // Allocate and initialize memory
fxmemdup(void ** ptr,const void * src,FXuval size)90 FXbool fxmemdup(void** ptr,const void* src,FXuval size){
91   *ptr=NULL;
92   if(size!=0 && src!=NULL){
93     if((*ptr=malloc(size))==NULL) return false;
94     memcpy(*ptr,src,size);
95     }
96   return true;
97   }
98 
99 
100 // String duplicate
fxstrdup(const FXchar * str)101 FXchar *fxstrdup(const FXchar* str){
102   FXchar *ptr=NULL;
103   if(str!=NULL){
104     FXint size=strlen(str)+1;
105     if((ptr=(FXchar*)malloc(size))!=NULL){
106       memcpy(ptr,str,size);
107       }
108     }
109   return ptr;
110   }
111 
112 
113 // Free memory, resets ptr to NULL afterward
fxfree(void ** ptr)114 void fxfree(void** ptr){
115   if(*ptr){
116     free(*ptr);
117     *ptr=NULL;
118     }
119   }
120 
121 
122 /*************************  FXMetaClass Implementation  ************************/
123 
124 // Empty but previously used hash table slot
125 #define EMPTY  ((FXMetaClass*)-1L)
126 
127 // Hash table of metaclasses initialized at load-time
128 const FXMetaClass** FXMetaClass::metaClassTable=NULL;
129 FXuint              FXMetaClass::metaClassSlots=0;
130 FXuint              FXMetaClass::metaClassCount=0;
131 
132 
133 // Compute FNV1a hash value of string
hashstring(const FXchar * str)134 static inline FXuint hashstring(const FXchar* str){
135   FXuint result=0x811C9DC5;
136   FXuchar c;
137   while((c=*str++)!='\0'){
138     result=(result^c)*0x01000193;
139     }
140   return result;
141   }
142 
143 
144 // Resize global hash table
resize(FXuint slots)145 void FXMetaClass::resize(FXuint slots){
146   const FXMetaClass **table;
147   const FXMetaClass *ptr;
148   FXuint p,x,s;
149   callocElms(table,slots);
150   for(s=0; s<metaClassSlots; ++s){
151     if((ptr=metaClassTable[s])!=NULL && ptr!=EMPTY){
152       p=hashstring(ptr->className);
153       x=(p<<1)|1;
154       while(table[p=(p+x)&(slots-1)]!=NULL){
155         }
156       table[p]=ptr;
157       }
158     }
159   freeElms(metaClassTable);
160   metaClassTable=table;
161   metaClassSlots=slots;
162   }
163 
164 
165 // Constructor adds metaclass to the table
FXMetaClass(const FXchar * name,FXObject * (fac)(),const FXMetaClass * base,const void * ass,FXuint nass,FXuint assz)166 FXMetaClass::FXMetaClass(const FXchar* name,FXObject *(fac)(),const FXMetaClass* base,const void* ass,FXuint nass,FXuint assz):className(name),manufacture(fac),baseClass(base),assoc(ass),nassocs(nass),assocsz(assz){
167   FXTRACE((TOPIC_CONSTRUCT,"FXMetaClass::FXMetaClass(%s)\n",className));
168   FXuint p=hashstring(className);
169   FXuint x=(p<<1)|1;
170   if((++metaClassCount<<1) > metaClassSlots){
171     resize(metaClassSlots?metaClassSlots<<1:1);
172     }
173   FXASSERT(metaClassSlots>=metaClassCount);
174   while(metaClassTable[p=(p+x)&(metaClassSlots-1)]!=NULL){
175     }
176   metaClassTable[p]=this;
177   }
178 
179 
180 // Create an object instance
makeInstance() const181 FXObject* FXMetaClass::makeInstance() const {
182   return (*manufacture)();
183   }
184 
185 
186 // Find function
search(FXSelector key) const187 const void* FXMetaClass::search(FXSelector key) const {
188   const FXObject::FXMapEntry* lst=(const FXObject::FXMapEntry*)assoc;
189   FXuint inc=assocsz;
190   FXuint n=nassocs;
191   while(n--){
192     if(__unlikely(key<=lst->keyhi) && __likely(lst->keylo<=key)) return lst;
193     lst=(const FXObject::FXMapEntry*) (((const FXchar*)lst)+inc);
194     }
195   return NULL;
196   }
197 
198 
199 // Test if subclass
isSubClassOf(const FXMetaClass * metaclass) const200 FXbool FXMetaClass::isSubClassOf(const FXMetaClass* metaclass) const {
201   const FXMetaClass* cls;
202   for(cls=this; cls; cls=cls->baseClass){
203     if(cls==metaclass) return true;
204     }
205   return false;
206   }
207 
208 
209 // Find the FXMetaClass belonging to class name
getMetaClassFromName(const FXchar * name)210 const FXMetaClass* FXMetaClass::getMetaClassFromName(const FXchar* name){
211   if(metaClassSlots && name){
212     FXuint p=hashstring(name);
213     FXuint x=(p<<1)|1;
214     while(metaClassTable[p=(p+x)&(metaClassSlots-1)]!=NULL){
215       if(metaClassTable[p]!=EMPTY && strcmp(metaClassTable[p]->className,name)==0){
216         return metaClassTable[p];
217         }
218       }
219     }
220   return NULL;
221   }
222 
223 
224 // Make NULL object; used for abstract classes that may not be instantiated
nullObject()225 FXObject* FXMetaClass::nullObject(){
226   return NULL;
227   }
228 
229 
230 // Destructor removes metaclass from the table
~FXMetaClass()231 FXMetaClass::~FXMetaClass(){
232   FXTRACE((TOPIC_CONSTRUCT,"FXMetaClass::~FXMetaClass(%s)\n",className));
233   FXuint p=hashstring(className);
234   FXuint x=(p<<1)|1;
235   while(metaClassTable[p=(p+x)&(metaClassSlots-1)]!=this){
236     if(metaClassTable[p]==NULL) return;
237     }
238   metaClassTable[p]=EMPTY;
239   if((--metaClassCount<<1) <= metaClassSlots){
240     resize(metaClassSlots>>1);
241     }
242   FXASSERT(metaClassSlots>=metaClassCount);
243   }
244 
245 }
246