1/* struct::graph - critcl - layer 1 definitions 2 * (c) Graph functions 3 */ 4 5#include <arc.h> 6#include <attr.h> 7#include <graph.h> 8#include <node.h> 9#include <objcmd.h> 10#include <util.h> 11 12/* .................................................. */ 13 14static void swap (G* dst, G* src); 15static G* dup (G* src); 16 17/* .................................................. */ 18 19G* 20g_new (void) 21{ 22 G* g = ALLOC (G); 23 24 g->nodes.map = ALLOC (Tcl_HashTable); 25 g->arcs.map = ALLOC (Tcl_HashTable); 26 27 Tcl_InitHashTable (g->nodes.map, TCL_STRING_KEYS); 28 Tcl_InitHashTable (g->arcs.map, TCL_STRING_KEYS); 29 30 g->nodes.first = NULL; 31 g->nodes.n = 0; 32 g->arcs.first = NULL; 33 g->arcs.n = 0; 34 35 g->attr = NULL; 36 37 g->cmd = NULL; 38 g->ncounter = 0; 39 g->acounter = 0; 40 41 return g; 42} 43 44/* .................................................. */ 45 46void 47g_delete (G* g) 48{ 49 /* Delete a graph in toto. Deletes all arcs first, then all nodes. This 50 * also handles the nodes/arcs lists. Then the name -> node/arc mapping, 51 * and the object name. 52 */ 53 54 while (g->arcs.first) { ga_delete ((GA*) g->arcs.first); } 55 while (g->nodes.first) { gn_delete ((GN*) g->nodes.first); } 56 57 Tcl_DeleteHashTable (g->arcs.map); 58 Tcl_DeleteHashTable (g->nodes.map); 59 60 ckfree ((char*) g->arcs.map); 61 ckfree ((char*) g->nodes.map); 62 63 g->arcs.map = NULL; 64 g->nodes.map = NULL; 65 66 g->cmd = NULL; 67 68 g_attr_delete (&g->attr); 69 ckfree ((char*) g); 70} 71 72/* .................................................. */ 73 74const char* 75g_newnodename (G* g) 76{ 77 int ok; 78 Tcl_HashEntry* he; 79 80 do { 81 g->ncounter ++; 82 sprintf (g->handle, "node%d", g->ncounter); 83 84 /* Check that there is no node using that name already */ 85 he = Tcl_FindHashEntry (g->nodes.map, g->handle); 86 ok = (he == NULL); 87 } while (!ok); 88 89 return g->handle; 90} 91 92/* .................................................. */ 93 94const char* 95g_newarcname (G* g) 96{ 97 int ok; 98 Tcl_HashEntry* he; 99 100 do { 101 g->acounter ++; 102 sprintf (g->handle, "arc%d", g->acounter); 103 104 /* Check that there is no node using that name already */ 105 he = Tcl_FindHashEntry (g->arcs.map, g->handle); 106 ok = (he == NULL); 107 } while (!ok); 108 109 return g->handle; 110} 111 112/* .................................................. */ 113 114/* 115 *--------------------------------------------------------------------------- 116 * 117 * g_ms_serialize -- 118 * 119 * Generates Tcl value from graph, serialized graph data. 120 * 121 * Results: 122 * A standard Tcl result code. 123 * 124 * Side effects: 125 * Only internal, memory allocation changes ... 126 * 127 *--------------------------------------------------------------------------- 128 */ 129 130Tcl_Obj* 131g_ms_serialize (Tcl_Interp* interp, Tcl_Obj* go, G* g, int oc, Tcl_Obj* const* ov) 132{ 133 Tcl_Obj* ser; 134 Tcl_Obj* empty; 135 136 int lc = 1 + 3 * (oc ? oc : g->nodes.n); 137 Tcl_Obj** lv = NALLOC (lc, Tcl_Obj*); 138 139 Tcl_HashTable cn; 140 int k, new; 141 GN* n; 142 143 /* Enumerate the nodes for the references used in arcs. FUTURE, TODO: Skip 144 * this step if there are no arcs! We cannot skip testing the validity of 145 * the nodes however, if the set is explicit. In that case we also check 146 * and remove duplicates. */ 147 148 Tcl_InitHashTable (&cn, TCL_ONE_WORD_KEYS); 149 150 if (oc) { 151 /* Enumerate the specified nodes, remove duplicates along the way */ 152 Tcl_HashEntry* he; 153 int i, j, new; 154 155 j = 0; 156 for (i=0; i < oc; i++) { 157 ASSERT_BOUNDS(i, oc); 158 n = gn_get_node (g, ov[i], interp, go); 159 if (!n) { 160 goto abort; 161 } 162 if (Tcl_FindHashEntry (&cn, (char*) n)) continue; 163 ASSERT_BOUNDS(j, lc-1); 164 he = Tcl_CreateHashEntry (&cn, (char*) n, &new); 165 lv [j] = n->base.name; 166 Tcl_SetHashValue (he, (ClientData) j); 167 j += 3; 168 } 169 lc = j + 1; 170 } else { 171 /* Enumerate all nodes */ 172 Tcl_HashEntry* he; 173 int j, new; 174 175 j = 0; 176 for (n = (GN*) g->nodes.first; 177 n != NULL; 178 n = (GN*) n->base.next) { 179 180 ASSERT_BOUNDS(j, lc-1); 181 he = Tcl_CreateHashEntry (&cn, (char*) n, &new); 182 lv [j] = n->base.name; 183 Tcl_SetHashValue (he, (ClientData) j); 184 j += 3; 185 } 186 lc = j + 1; 187 } 188 189 empty = Tcl_NewObj (); 190 Tcl_IncrRefCount (empty); 191 192 /* Fill in the arcs, attributes per node, and graph attributes */ 193 194 for (k=0; k < lc-1; k++) { 195 ASSERT_BOUNDS(k, lc-1); 196 n = gn_get_node (g, lv[k], NULL, NULL); 197 k ++; 198 199 ASSERT_BOUNDS(k, lc-1); 200 lv [k] = g_attr_serial (n->base.attr, empty); 201 k ++; 202 203 ASSERT_BOUNDS(k, lc-1); 204 lv [k] = gn_serial_arcs (n, empty, &cn); 205 } 206 207 ASSERT_BOUNDS(k, lc); 208 lv [k] = g_attr_serial (g->attr, empty); 209 210 /* Put everything together, release scratch space */ 211 212 ser = Tcl_NewListObj (lc, lv); 213 214 Tcl_DecrRefCount (empty); 215 Tcl_DeleteHashTable(&cn); 216 ckfree ((char*) lv); 217 218 return ser; 219 220 abort: 221 Tcl_DeleteHashTable(&cn); 222 ckfree ((char*) lv); 223 return NULL; 224} 225 226 227/* .................................................. */ 228 229int 230g_deserialize (G* dst, Tcl_Interp* interp, Tcl_Obj* src) 231{ 232 /* 233 * SV = { NODE ATTR/node ARCS ... ATTR/graph } 234 * 235 * using: 236 * ATTR/x = { key value ... } 237 * ARCS = { { NAME targetNODEref ATTR/arc } ... } 238 * 239 * Basic checks: 240 * - Is the input a list ? 241 * - Is its length a multiple of three modulo 1 ? 242 */ 243 244 int lc, i, j, k; 245 Tcl_Obj** lv; 246 int ac; 247 Tcl_Obj** av; 248 int axc, nref; 249 Tcl_Obj** axv; 250 int nodes; 251 G* new; 252 GN* n; 253 GN* ndst; 254 GA* a; 255 int code = TCL_ERROR; 256 257 if (Tcl_ListObjGetElements (interp, src, &lc, &lv) != TCL_OK) { 258 return TCL_ERROR; 259 } 260 if ((lc % 3) != 1) { 261 Tcl_AppendResult (interp, 262 "error in serialization: list length not 1 mod 3.", 263 NULL); 264 return TCL_ERROR; 265 } 266 267 nodes = (lc-1)/3; 268 269 /* Iteration 1. Check the overall structure of the incoming value (node 270 * attributes, arcs, arc attributes, graph attributes). 271 */ 272 273 if (!g_attr_serok (interp, lv[lc-1], "graph")) { 274 return TCL_ERROR; 275 } 276 277 for (i=0; i < (lc-1); ) { 278 /* Skip node name */ 279 ASSERT_BOUNDS (i, lc-1); 280 i ++ ; 281 /* Check node attributes */ 282 if (!g_attr_serok (interp, lv[i], "node")) { 283 return TCL_ERROR; 284 } 285 /* Go to the arc information block for the node */ 286 ASSERT_BOUNDS (i, lc-1); 287 i ++; 288 /* Check arc information */ 289 if (Tcl_ListObjGetElements (interp, lv[i], &ac, &av) != TCL_OK) { 290 return TCL_ERROR; 291 } 292 for (k=0; k < ac; k++) { 293 ASSERT_BOUNDS (k, ac); 294 /* Check each arc */ 295 if (Tcl_ListObjGetElements (interp, av[k], &axc, &axv) != TCL_OK) { 296 return TCL_ERROR; 297 } 298 if ((axc != 3) && (axc != 4)) { 299 Tcl_AppendResult (interp, 300 "error in serialization: arc information length not 3 or 4.", 301 NULL); 302 return TCL_ERROR; 303 } 304 /* Check arc attributes */ 305 if (!g_attr_serok (interp, axv[2], "arc")) { 306 return TCL_ERROR; 307 } 308 /* Check node reference for arc destination */ 309 if ((Tcl_GetIntFromObj (interp, axv[1], &nref) != TCL_OK) || 310 (nref % 3) || (nref < 0) || (nref >= lc)) { 311 Tcl_ResetResult (interp); 312 Tcl_AppendResult (interp, 313 "error in serialization: bad arc destination reference \"", 314 Tcl_GetString (axv[1]), 315 "\".", NULL); 316 return TCL_ERROR; 317 } 318 } 319 /* Go to the next node */ 320 ASSERT_BOUNDS (i, lc-1); 321 i ++; 322 } 323 324 /* We now know that the value is structurally sound, i.e. lists, of the 325 * specified lengths, fixed, and proper multiples, and that references are 326 * kept inside to the proper locations. We can now go over the information 327 * again and use it to build up a graph. At that time we can also do the 328 * more complex semantic checks (dup nodes, dup arcs). 329 * 330 * The information is collected directly into a graph structure. We have 331 * no better place where to put it. In case of problems we can tear it 332 * down again easily, and otherwise we can swap with the actual graph and 333 * then tear that one down, effectively replacing it with the new graph. 334 */ 335 336 new = g_new (); 337 338 /* I. Import the nodes */ 339 340 for (i=0; i < (lc-1); i += 3) { 341 ASSERT_BOUNDS (i, lc-1); 342 n = gn_get_node (new, lv[i], NULL, NULL); 343 if (n) { 344 Tcl_AppendResult (interp, 345 "error in serialization: duplicate node names.", 346 NULL); 347 goto done; 348 } 349 gn_new (new, Tcl_GetString (lv [i])); 350 } 351 352 /* II. Import the arcs */ 353 354 for (i=2; i < (lc-1); i += 3) { 355 ASSERT_BOUNDS (i, lc-1); 356 n = gn_get_node (new, lv[i-2], NULL, NULL); 357 Tcl_ListObjGetElements (interp, lv[i], &ac, &av); 358 359 for (k=0; k < ac; k++) { 360 ASSERT_BOUNDS (k, ac); 361 Tcl_ListObjGetElements (interp, av[k], &axc, &axv); 362 a = ga_get_arc (new, axv[0], NULL, NULL); 363 if (a) { 364 Tcl_AppendResult (interp, 365 "error in serialization: duplicate definition of arc \"", 366 Tcl_GetString (axv[0]),"\".", NULL); 367 goto done; 368 } 369 Tcl_GetIntFromObj (interp, axv[1], &nref); 370 ndst = gn_get_node (new, lv[nref], NULL, NULL); 371 a = ga_new (new, Tcl_GetString (axv[0]), n, ndst); 372 373 if (axc == 4) { 374 a->weight = axv[3]; 375 Tcl_IncrRefCount (a->weight); 376 } 377 } 378 } 379 380 /* III. Import the various attributes */ 381 382 for (i=0; i < (lc-1); ) { 383 ASSERT_BOUNDS (i, lc-1); 384 n = gn_get_node (new, lv[i], NULL, NULL); 385 /* Goto node attributes */ 386 i ++ ; 387 /* Import node attributes */ 388 ASSERT_BOUNDS (i, lc-1); 389 g_attr_deserial (&n->base.attr, lv[i]); 390 /* Go to the arc information block for the node */ 391 ASSERT_BOUNDS (i, lc-1); 392 i ++; 393 /* Check arc information */ 394 Tcl_ListObjGetElements (interp, lv[i], &ac, &av); 395 for (k=0; k < ac; k++) { 396 ASSERT_BOUNDS (k, ac); 397 Tcl_ListObjGetElements (interp, av[k], &axc, &axv); 398 a = ga_get_arc (new, axv[0], NULL, NULL); 399 g_attr_deserial (&a->base.attr, axv[2]); 400 } 401 /* Go to the next node */ 402 ASSERT_BOUNDS (i, lc-1); 403 i ++; 404 } 405 406 g_attr_deserial (&new->attr, lv[lc-1]); 407 408 /* swap dst <-> new. This puts the collected information into the graph 409 * associated with the command, and the old information is put into the 410 * scratch structure scheduled for destruction, making cleanup automatic. 411 */ 412 413 swap (dst, new); 414 code = TCL_OK; 415 416 done: 417 g_delete (new); 418 return code; 419} 420 421/* .................................................. */ 422 423int 424g_assign (G* dst, G* src) 425{ 426 G* new = dup (src); 427 swap (dst, new); 428 g_delete (new); 429 return TCL_OK; 430} 431 432/* 433 *--------------------------------------------------------------------------- 434 * 435 * g_ms_assign -- 436 * 437 * Copies the argument graph over into this one. Uses direct 438 * access to internal data structures for matching graph objects, and 439 * goes through a serialize/deserialize combination otherwise. 440 * 441 * Results: 442 * A standard Tcl result code. 443 * 444 * Side effects: 445 * Only internal, memory allocation changes ... 446 * 447 *--------------------------------------------------------------------------- 448 */ 449 450int 451g_ms_assign (Tcl_Interp* interp, G* g, Tcl_Obj* src) 452{ 453 Tcl_CmdInfo srcInfo; 454 455 if (!Tcl_GetCommandInfo(interp, Tcl_GetString (src), &srcInfo)) { 456 Tcl_AppendResult (interp, "invalid command name \"", 457 Tcl_GetString (src), "\"", NULL); 458 return TCL_ERROR; 459 } 460 461 if (srcInfo.objProc == g_objcmd) { 462 /* The source graph object is managed by this code also. We can 463 * retrieve and copy the data directly. 464 */ 465 466 G* gsrc = (G*) srcInfo.objClientData; 467 468 return g_assign (g, gsrc); 469 470 } else { 471 /* The source graph is not managed by this package. Use 472 * (de)serialization to transfer the information We do not invoke the 473 * command proc directly 474 */ 475 476 int res; 477 Tcl_Obj* ser; 478 Tcl_Obj* cmd [2]; 479 480 /* Phase 1: Obtain a serialization by invoking the relevant object 481 * method 482 */ 483 484 cmd [0] = src; 485 cmd [1] = Tcl_NewStringObj ("serialize", -1); 486 487 Tcl_IncrRefCount (cmd [0]); 488 Tcl_IncrRefCount (cmd [1]); 489 490 res = Tcl_EvalObjv (interp, 2, cmd, 0); 491 492 Tcl_DecrRefCount (cmd [0]); 493 Tcl_DecrRefCount (cmd [1]); 494 495 if (res != TCL_OK) { 496 return TCL_ERROR; 497 } 498 499 ser = Tcl_GetObjResult (interp); 500 Tcl_IncrRefCount (ser); 501 Tcl_ResetResult (interp); 502 503 /* Phase 2: Copy the serializtion into ourselves using the regular 504 * deserialization functionality 505 */ 506 507 res = g_deserialize (g, interp, ser); 508 Tcl_DecrRefCount (ser); 509 return res; 510 } 511} 512 513/* 514 *--------------------------------------------------------------------------- 515 * 516 * g_ms_set -- 517 * 518 * Copies this graph over into the argument graph. Uses direct access to 519 * internal data structures for matching graph objects, and goes through a 520 * serialize/deserialize combination otherwise. 521 * 522 * Results: 523 * A standard Tcl result code. 524 * 525 * Side effects: 526 * Only internal, memory allocation changes ... 527 * 528 *--------------------------------------------------------------------------- 529 */ 530 531int 532g_ms_set (Tcl_Interp* interp, Tcl_Obj* go, G* g, Tcl_Obj* dst) 533{ 534 Tcl_CmdInfo dstInfo; 535 536 if (!Tcl_GetCommandInfo(interp, Tcl_GetString (dst), &dstInfo)) { 537 Tcl_AppendResult (interp, "invalid command name \"", 538 Tcl_GetString (dst), "\"", NULL); 539 return TCL_ERROR; 540 } 541 542 if (dstInfo.objProc == g_objcmd) { 543 /* The destination graph object is managed by this code also We can 544 * retrieve and copy the data directly. 545 */ 546 547 G* gdest = (G*) dstInfo.objClientData; 548 549 return g_assign (gdest, g); 550 551 } else { 552 /* The destination graph is not managed by this package. Use 553 * (de)serialization to transfer the information We do not invoke the 554 * command proc directly. 555 */ 556 557 int res; 558 Tcl_Obj* ser; 559 Tcl_Obj* cmd [3]; 560 561 /* Phase 1: Obtain our serialization */ 562 563 ser = g_ms_serialize (interp, go, g, 0, NULL); 564 565 /* Phase 2: Copy into destination by invoking the regular 566 * deserialization method 567 */ 568 569 cmd [0] = dst; 570 cmd [1] = Tcl_NewStringObj ("deserialize", -1); 571 cmd [2] = ser; 572 573 Tcl_IncrRefCount (cmd [0]); 574 Tcl_IncrRefCount (cmd [1]); 575 Tcl_IncrRefCount (cmd [2]); 576 577 res = Tcl_EvalObjv (interp, 3, cmd, 0); 578 579 Tcl_DecrRefCount (cmd [0]); 580 Tcl_DecrRefCount (cmd [1]); 581 Tcl_DecrRefCount (cmd [2]); /* == ser, is gone now */ 582 583 if (res != TCL_OK) { 584 return TCL_ERROR; 585 } 586 587 Tcl_ResetResult (interp); 588 return TCL_OK; 589 } 590 return TCL_ERROR; 591} 592 593 594/* .................................................. */ 595 596static void 597swap (G* dst, G* src) 598{ 599 GC* c; 600 G tmp; 601 602 /* Swap the main information */ 603 604 tmp = *dst; 605 *dst = *src; 606 *src = tmp; 607 608 /* Swap the cmd right back, because this part of the dst structure has to 609 * be kept. 610 */ 611 612 tmp.cmd = dst->cmd; 613 dst->cmd = src->cmd; 614 src->cmd = tmp.cmd; 615 616 /* At last fix the node/arc ownership in both structures, or else g_delete 617 * will access and destroy the newly created information, and a future 618 * delete of the graph accesses long gone memory. 619 */ 620 621 for (c = src->nodes.first; c != NULL; c = c->next) { 622 c->graph = src; 623 } 624 for (c = src->arcs.first; c != NULL; c = c->next) { 625 c->graph = src; 626 } 627 628 for (c = dst->nodes.first; c != NULL; c = c->next) { 629 c->graph = dst; 630 } 631 for (c = dst->arcs.first; c != NULL; c = c->next) { 632 c->graph = dst; 633 } 634} 635 636/* .................................................. */ 637 638static G* 639dup (G* src) 640{ 641 G* new = g_new (); 642 GN* no; GN* n; 643 GA* ao; GA* a; 644 GC* c; 645 646 /* I. Duplicate nodes. NOTE. In the list of nodes in src we break the chain 647 * of prev references and use that to point from each src node to its 648 * duplicate. This is then used during the duplication of arcs (-> II.) to 649 * quickly locate the nodes to connect. After that is done the chain can 650 * and is restored. 651 */ 652#define ORIG base.prev 653 654 for (no = (GN*) src->nodes.first; 655 no != NULL; 656 no = (GN*) no->base.next) { 657 658 n = gn_new (new, Tcl_GetString(no->base.name)); 659 no->ORIG = (GC*) n; 660 g_attr_dup (&n->base.attr, no->base.attr); 661 } 662 663 /* II. Duplicate the arcs */ 664 665 for (ao = (GA*) src->arcs.first; 666 ao != NULL; 667 ao = (GA*) ao->base.next) { 668 a = ga_new (new, Tcl_GetString(ao->base.name), 669 (GN*) ao->start->n->ORIG, 670 (GN*) ao->end->n->ORIG); 671 g_attr_dup (&a->base.attr, ao->base.attr); 672 673 if (ao->weight) { 674 a->weight = ao->weight; 675 Tcl_IncrRefCount (a->weight); 676 } 677 } 678 679#undef ORIG 680 681 /* III. Re-chain the nodes in the original */ 682 683 c = src->nodes.first; 684 if (c) { 685 c->prev = NULL; 686 c = c->next; 687 688 for (; c != NULL; c = c->next) { 689 if (!c->next) break; 690 c->next->prev = c; 691 } 692 } 693 694 g_attr_dup (&new->attr, src->attr); 695 return new; 696} 697 698/* .................................................. */ 699 700/* 701 * Local Variables: 702 * mode: c 703 * c-basic-offset: 4 704 * fill-column: 78 705 * End: 706 */ 707