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