1 /*
2     SuperCollider real time audio synthesis system
3     Copyright (c) 2002 James McCartney. All rights reserved.
4     http://www.audiosynth.com
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
19 */
20 /*
21 
22 An object archiving system for SuperCollider.
23 
24 */
25 
26 
27 #pragma once
28 
29 #include "PyrObject.h"
30 #include "SC_AllocPool.h"
31 
32 #include "PyrKernel.h"
33 #include "PyrPrimitive.h"
34 #include "PyrSymbol.h"
35 #include "VMGlobals.h"
36 #include "GC.h"
37 #include "ReadWriteMacros.h"
38 #include "SCBase.h"
39 
40 const int32 kArchHdrSize = 12;
41 const int32 kObjectArrayInitialCapacity = 32;
42 
43 template <class S> class PyrArchiver {
44 public:
PyrArchiver(VMGlobals * inG)45     PyrArchiver(VMGlobals* inG):
46         g(inG),
47         mObjectArray(mInitialObjectArray),
48         mNumObjects(0),
49         mObjectArrayCapacity(kObjectArrayInitialCapacity),
50         mReadArchiveVersion(0) {}
51 
~PyrArchiver()52     ~PyrArchiver() {
53         if (mObjectArray != mInitialObjectArray) {
54             g->allocPool->Free(mObjectArray);
55         }
56     }
57 
setStream(S s)58     void setStream(S s) { mStream.SetStream(s); }
59 
calcArchiveSize()60     int32 calcArchiveSize() {
61         PyrSlot* slot;
62         int32 size = kArchHdrSize;
63         if (mNumObjects == 0) {
64             size += sizeOfElem(&mTopSlot) + 1;
65         } else {
66             // object table size
67             for (int i = 0; i < mNumObjects; ++i) {
68                 PyrObject* obj = mObjectArray[i];
69                 size += slotRawSymbol(&obj->classptr->name)->length + 1; // class name symbol
70                 size += sizeof(int32); // size
71                 if (obj->obj_format <= obj_slot) {
72                     size += obj->size; // tags
73                     slot = obj->slots;
74                     for (int j = 0; j < obj->size; ++j, ++slot) {
75                         size += sizeOfElem(slot);
76                     }
77                 } else if (obj->obj_format == obj_symbol) {
78                     PyrSymbol** symbol = ((PyrSymbolArray*)obj)->symbols;
79                     for (int j = 0; j < obj->size; ++j, ++symbol) {
80                         size += (**symbol).length + 1;
81                     }
82                 } else {
83                     size += obj->size * gFormatElemSize[obj->obj_format];
84                 }
85             }
86         }
87         return size;
88     }
89 
prepareToWriteArchive(PyrSlot * objectSlot)90     long prepareToWriteArchive(PyrSlot* objectSlot) {
91         long err = errNone;
92 
93         try {
94             slotCopy(&mTopSlot, objectSlot);
95             if (IsObj(objectSlot))
96                 constructObjectArray(slotRawObject(objectSlot));
97         } catch (std::exception& ex) {
98             error(ex.what());
99             err = errFailed;
100         }
101         return err;
102     }
103 
writeArchive()104     long writeArchive() {
105         long err = errNone;
106 
107         try {
108             writeArchiveHeader();
109 
110             if (mNumObjects == 0) {
111                 writeSlot(&mTopSlot);
112             } else {
113                 for (int i = 0; i < mNumObjects; ++i) {
114                     writeObjectHeader(mObjectArray[i]);
115                 }
116                 for (int i = 0; i < mNumObjects; ++i) {
117                     writeSlots(mObjectArray[i]);
118                 }
119             }
120         } catch (std::exception& ex) {
121             error(ex.what());
122             err = errFailed;
123         }
124         return err;
125     }
126 
readArchive(PyrSlot * objectSlot)127     long readArchive(PyrSlot* objectSlot) {
128         // postfl("->readArchive\n");
129         long err = errNone;
130 
131 
132         SetNil(objectSlot);
133 
134         try {
135             readArchiveHeader();
136             // postfl("readObjectHeaders %d\n", mNumObjects);
137             if (mNumObjects == 0) {
138                 readSlot(objectSlot);
139             } else {
140                 for (int i = 0; i < mNumObjects; ++i) {
141                     mObjectArray[i] = readObjectHeader();
142                 }
143                 // postfl("readSlots\n");
144                 for (int i = 0; i < mNumObjects; ++i) {
145                     readSlots(mObjectArray[i]);
146                 }
147                 // postfl("done reading\n");
148                 // postfl("SetObject\n");
149                 SetObject(objectSlot, mObjectArray[0]);
150             }
151         } catch (std::exception& ex) {
152             error(ex.what());
153             err = errFailed;
154         } catch (...) {
155             err = errFailed;
156         }
157         // postfl("<-readArchive\n");
158         return err;
159     }
160 
161 private:
writeArchiveHeader()162     void writeArchiveHeader() {
163         mStream.writeInt32_be(('!' << 24) | ('S' << 16) | ('C' << 8) | 'a' /*'!SCa'*/);
164         mStream.writeInt32_be(2); // file version
165         mStream.writeInt32_be(mNumObjects);
166     }
167 
readArchiveHeader()168     void readArchiveHeader() {
169         int32 magicNumber = mStream.readInt32_be();
170         if (magicNumber != (('!' << 24) | ('S' << 16) | ('C' << 8) | 'a' /*'!SCa'*/)) {
171             throw std::runtime_error("not an SC archive.\n");
172         }
173         mReadArchiveVersion = mStream.readInt32_be(); // file version
174         mNumObjects = mStream.readInt32_be();
175         // post("readArchiveHeader %d %d\n", mReadArchiveVersion, mNumObjects);
176 
177         if (mNumObjects > kObjectArrayInitialCapacity) {
178             mObjectArray = (PyrObject**)g->allocPool->Alloc(mNumObjects * sizeof(PyrObject*));
179             mObjectArrayCapacity = mNumObjects;
180         }
181     }
182 
recurse(PyrObject * obj,int n)183     void recurse(PyrObject* obj, int n) {
184         PyrSlot* slot = obj->slots;
185         for (int i = 0; i < n; ++i, ++slot) {
186             if (IsObj(slot))
187                 constructObjectArray(slotRawObject(slot));
188         }
189     }
190 
growObjectArray()191     void growObjectArray() {
192         int32 newObjectArrayCapacity = mObjectArrayCapacity << 1;
193 
194         int32 newSize = newObjectArrayCapacity * sizeof(PyrObject*);
195         PyrObject** newArray = (PyrObject**)g->allocPool->Alloc(newSize);
196         memcpy(newArray, mObjectArray, mNumObjects * sizeof(PyrObject*));
197         if (mObjectArray != mInitialObjectArray) {
198             g->allocPool->Free(mObjectArray);
199         }
200         mObjectArrayCapacity = newObjectArrayCapacity;
201         mObjectArray = newArray;
202     }
203 
putObject(PyrObject * obj)204     void putObject(PyrObject* obj) {
205         obj->SetMark();
206         obj->scratch1 = mNumObjects;
207 
208         // expand array if needed
209         if (mNumObjects >= mObjectArrayCapacity)
210             growObjectArray();
211 
212         // add to array
213         mObjectArray[mNumObjects++] = obj;
214     }
215 
constructObjectArray(PyrObject * obj)216     void constructObjectArray(PyrObject* obj) {
217         if (!obj->IsMarked()) {
218             if (isKindOf(obj, class_class)) {
219             } else if (isKindOf(obj, class_process)) {
220             } else if (isKindOf(obj, s_interpreter->u.classobj)) {
221             } else if (isKindOf(obj, class_method)) {
222                 throw std::runtime_error("cannot archive Methods.\n");
223             } else if (isKindOf(obj, class_thread)) {
224                 throw std::runtime_error("cannot archive Threads.\n");
225             } else if (isKindOf(obj, class_frame)) {
226                 throw std::runtime_error("cannot archive Frames.\n");
227             } else if (isKindOf(obj, class_func)) {
228                 // if (NotNil(&((PyrClosure*)obj)->block.uoblk->context)) {
229                 //	throw std::runtime_error("open Function can not be archived.\n");
230                 //}
231                 putObject(obj);
232                 recurse(obj, obj->size);
233             } else {
234                 if (isKindOf(obj, class_rawarray)) {
235                     putObject(obj);
236                 } else if (isKindOf(obj, class_array)) {
237                     putObject(obj);
238                     recurse(obj, obj->size);
239 
240                 } else {
241                     putObject(obj);
242                     recurse(obj, obj->size);
243                 }
244             }
245         }
246     }
247 
sizeOfElem(PyrSlot * slot)248     int32 sizeOfElem(PyrSlot* slot) {
249         // postfl("writeSlot %08X\n", GetTag(slot));
250         switch (GetTag(slot)) {
251         case tagObj:
252             if (isKindOf(slotRawObject(slot), class_class)) {
253                 return slotRawSymbol(&slotRawClass(slot)->name)->length + 1;
254             } else if (isKindOf(slotRawObject(slot), class_process)) {
255                 return 0;
256             } else if (isKindOf(slotRawObject(slot), class_frame)) {
257                 return 0;
258             } else if (isKindOf(slotRawObject(slot), s_interpreter->u.classobj)) {
259                 return 0;
260             } else {
261                 return sizeof(int32);
262             }
263             break;
264         case tagInt:
265             return sizeof(int32);
266         case tagSym:
267             return slotRawSymbol(slot)->length + 1;
268         case tagChar:
269             return sizeof(int32);
270         case tagNil:
271             return 0;
272         case tagFalse:
273             return 0;
274         case tagTrue:
275             return 0;
276         case tagPtr:
277             throw std::runtime_error("cannot archive RawPointers.");
278             return 0;
279         default:
280             return sizeof(double);
281         }
282     }
283 
readSymbolID()284     PyrSymbol* readSymbolID() {
285         char str[256];
286         mStream.readSymbol(str);
287         return getsym(str);
288     }
289 
290 
readObjectID()291     PyrObject* readObjectID() {
292         int32 objID = mStream.readInt32_be();
293         // postfl("readObjectID %d\n", objID);
294         return mObjectArray[objID];
295     }
296 
writeObjectHeader(PyrObject * obj)297     void writeObjectHeader(PyrObject* obj) {
298         obj->ClearMark();
299 
300         // postfl("writeObjectHeader %s\n", slotRawSymbol(&obj->classptr->name)->name);
301         mStream.writeSymbol(slotRawSymbol(&obj->classptr->name)->name);
302 
303         mStream.writeInt32_be(obj->size);
304     }
305 
readObjectHeader()306     PyrObject* readObjectHeader() {
307         PyrSymbol* classname = readSymbolID();
308         // post("readObjectHeader %s\n", classname->name);
309         PyrObject* obj;
310         int32 size = mStream.readInt32_be();
311         if (slotRawInt(&classname->u.classobj->classFlags) & classHasIndexableInstances) {
312             obj = instantiateObject(g->gc, classname->u.classobj, size, false, false);
313             obj->size = size;
314         } else {
315             obj = instantiateObject(g->gc, classname->u.classobj, 0, false, false);
316         }
317         return obj;
318     }
319 
writeSlots(PyrObject * obj)320     void writeSlots(PyrObject* obj) {
321         // postfl("  writeSlots %s\n", slotRawSymbol(&obj->classptr->name)->name);
322         if (isKindOf(obj, class_rawarray)) {
323             writeRawArray(obj);
324         } else if (isKindOf(obj, class_func)) {
325             PyrClosure* closure = (PyrClosure*)obj;
326             if (NotNil(&slotRawBlock(&closure->block)->contextDef)) {
327                 writeSlot(&closure->block);
328                 writeSlot(&closure->context);
329             } else {
330                 writeSlot(&closure->block);
331                 writeSlot(&o_nil);
332             }
333         } else {
334             for (int i = 0; i < obj->size; ++i) {
335                 writeSlot(obj->slots + i);
336             }
337         }
338     }
339 
readSlots(PyrObject * obj)340     void readSlots(PyrObject* obj) {
341         // postfl("readSlots\n");
342         if (isKindOf(obj, class_rawarray)) {
343             readRawArray(obj);
344         } else if (isKindOf(obj, class_func)) {
345             PyrClosure* closure = (PyrClosure*)obj;
346             readSlot(&closure->block);
347             readSlot(&closure->context);
348             if (IsNil(&closure->context)) {
349                 slotCopy(&closure->context, &slotRawInterpreter(&g->process->interpreter)->context);
350             }
351         } else {
352             for (int i = 0; i < obj->size; ++i) {
353                 readSlot(obj->slots + i);
354             }
355         }
356     }
357 
writeSlot(PyrSlot * slot)358     void writeSlot(PyrSlot* slot) {
359         PyrObject* obj;
360         // postfl("    writeSlot %08X\n", GetTag(slot));
361         switch (GetTag(slot)) {
362         case tagObj:
363             obj = slotRawObject(slot);
364             if (isKindOf(obj, class_class)) {
365                 mStream.writeInt8('C');
366                 mStream.writeSymbol(slotRawSymbol(&slotRawClass(slot)->name)->name);
367             } else if (isKindOf(obj, class_process)) {
368                 mStream.writeInt8('P');
369             } else if (isKindOf(obj, s_interpreter->u.classobj)) {
370                 mStream.writeInt8('R');
371             } else {
372                 mStream.writeInt8('o');
373                 mStream.writeInt32_be(obj->scratch1);
374             }
375             break;
376         case tagInt:
377             mStream.writeInt8('i');
378             mStream.writeInt32_be(slotRawInt(slot));
379             break;
380         case tagSym:
381             mStream.writeInt8('s');
382             mStream.writeSymbol(slotRawSymbol(slot)->name);
383             break;
384         case tagChar:
385             mStream.writeInt8('c');
386             mStream.writeInt32_be(slotRawInt(slot));
387             break;
388         case tagNil:
389             mStream.writeInt8('N');
390             break;
391         case tagFalse:
392             mStream.writeInt8('F');
393             break;
394         case tagTrue:
395             mStream.writeInt8('T');
396             break;
397         case tagPtr:
398             mStream.writeInt8('N');
399             break;
400         default:
401             mStream.writeInt8('f');
402             mStream.writeDouble_be(slotRawFloat(slot));
403             break;
404         }
405     }
406 
readSlot(PyrSlot * slot)407     void readSlot(PyrSlot* slot) {
408         char tag = mStream.readInt8();
409         switch (tag) {
410         case 'o':
411             SetObject(slot, readObjectID());
412             break;
413         case 'z':
414             SetObject(slot, (PyrObject*)(size_t)mStream.readInt32_be()); // FIXME: fix 64bit safety
415             break;
416         case 'C':
417             SetObject(slot, (PyrObject*)readSymbolID()->u.classobj);
418             break;
419         case 'P':
420             SetObject(slot, (PyrObject*)g->process);
421             break;
422         case 'R':
423             SetObject(slot, slotRawObject(&g->process->interpreter));
424             break;
425         case 'i':
426             SetInt(slot, mStream.readInt32_be());
427             break;
428         case 's':
429             SetSymbol(slot, readSymbolID());
430             break;
431         case 'c':
432             SetChar(slot, mStream.readInt32_be());
433             break;
434         case 'f':
435             SetFloat(slot, mStream.readDouble_be());
436             break;
437         case 'T':
438             SetTrue(slot);
439             break;
440         case 'F':
441             SetFalse(slot);
442             break;
443         case 'N':
444         default:
445             SetNil(slot);
446             break;
447         }
448     }
449 
writeRawArray(PyrObject * obj)450     void writeRawArray(PyrObject* obj) {
451         int32 size = obj->size;
452         // postfl("writeRawArray %d\n", size);
453         switch (obj->obj_format) {
454         case obj_char:
455         case obj_int8: {
456             char* data = (char*)obj->slots;
457             mStream.writeData(data, size);
458         } break;
459         case obj_int16: {
460             int16* data = (int16*)obj->slots;
461             for (int i = 0; i < size; ++i) {
462                 mStream.writeInt16_be(data[i]);
463             }
464         } break;
465         case obj_int32:
466         case obj_float: {
467             int32* data = (int32*)obj->slots;
468             for (int i = 0; i < size; ++i) {
469                 mStream.writeInt32_be(data[i]);
470             }
471         } break;
472         case obj_double: {
473             double* data = (double*)obj->slots;
474             for (int i = 0; i < size; ++i) {
475                 mStream.writeDouble_be(data[i]);
476             }
477         } break;
478         case obj_symbol: {
479             PyrSymbol** data = (PyrSymbol**)obj->slots;
480             for (int i = 0; i < size; ++i) {
481                 mStream.writeSymbol(data[i]->name);
482             }
483         } break;
484         }
485     }
486 
readRawArray(PyrObject * obj)487     void readRawArray(PyrObject* obj) {
488         // postfl("readRawArray\n");
489         int32 size = obj->size;
490         switch (obj->obj_format) {
491         case obj_char:
492         case obj_int8: {
493             int8* data = (int8*)obj->slots;
494             for (int i = 0; i < size; ++i) {
495                 data[i] = mStream.readInt8();
496             }
497         } break;
498         case obj_int16: {
499             int16* data = (int16*)obj->slots;
500             for (int i = 0; i < size; ++i) {
501                 data[i] = mStream.readInt16_be();
502             }
503         } break;
504         case obj_int32:
505         case obj_float: {
506             int32* data = (int32*)obj->slots;
507             for (int i = 0; i < size; ++i) {
508                 data[i] = mStream.readInt32_be();
509             }
510         } break;
511         case obj_double: {
512             double* data = (double*)obj->slots;
513             for (int i = 0; i < size; ++i) {
514                 data[i] = mStream.readDouble_be();
515             }
516         } break;
517         case obj_symbol: {
518             PyrSymbol** data = (PyrSymbol**)obj->slots;
519             for (int i = 0; i < size; ++i) {
520                 data[i] = readSymbolID();
521             }
522         } break;
523         }
524     }
525 
526     VMGlobals* g;
527 
528     PyrObject** mObjectArray;
529     int32 mNumObjects;
530     int32 mObjectArrayCapacity;
531     PyrSlot mTopSlot;
532 
533     SC_IOStream<S> mStream;
534     int32 mReadArchiveVersion;
535 
536     PyrObject* mInitialObjectArray[kObjectArrayInitialCapacity];
537 };
538