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