ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/freezethaw.C
Revision: 1.44
Committed: Sat Apr 23 04:56:56 2011 UTC (13 years, 1 month ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.43: +1 -1 lines
Log Message:
update copyright to 2011

File Contents

# Content
1 /*
2 * This file is part of Deliantra, the Roguelike Realtime MMORPG.
3 *
4 * Copyright (©) 2006,2007,2008,2009,2010,2011 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
5 *
6 * Deliantra is free software: you can redistribute it and/or modify it under
7 * the terms of the Affero GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * 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 Affero GNU General Public License
17 * and the GNU General Public License along with this program. If not, see
18 * <http://www.gnu.org/licenses/>.
19 *
20 * The authors can be reached via e-mail to <support@deliantra.net>
21 */
22
23 #include "global.h" // bug in cfperl.h, doesn't include interface_class stuff
24 #include "logger.h"
25 #include "cfperl.h"
26 #include "kw_hash.h"
27
28 object_freezer::object_freezer ()
29 : dynbuf_text (128 * 1024, 64 * 1024)
30 {
31 av = newAV ();
32 }
33
34 object_freezer::~object_freezer ()
35 {
36 SvREFCNT_dec (av);
37 }
38
39 void
40 object_freezer::put_ (attachable *ext)
41 {
42 ext->optimise ();
43
44 if (ext->self)
45 {
46 int idx = AvFILLp ((AV *)av) + 1;
47 av_store (av, idx, newRV_inc ((SV *)ext->self));
48
49 put (KW(oid), sint32(idx));
50 }
51 }
52
53 bool
54 object_freezer::save (const char *path)
55 {
56 CALL_BEGIN (3);
57 CALL_ARG_SV (newSVpv (path, 0));
58 CALL_ARG_SV (newRV_noinc (newSVpvn ((char *)linearise (), size ())));
59 CALL_ARG_SV (newRV_inc ((SV *)av));
60 CALL_CALL ("cf::object_freezer_save", G_VOID | G_DISCARD);
61 CALL_END;
62
63 return 1;
64 }
65
66 char *
67 object_freezer::as_string ()
68 {
69 CALL_BEGIN (2);
70 CALL_ARG_SV (newRV_noinc (newSVpvn ((char *)linearise (), size ())));
71 CALL_ARG_SV (newRV_inc ((SV *)av));
72 CALL_CALL ("cf::object_freezer_as_string", G_SCALAR);
73
74 char *res = count > 0
75 ? strdup (SvPVX (POPs))
76 : strdup ("[fatal error]");
77
78 CALL_END;
79
80 return res;
81 }
82
83 #if 0
84 void
85 fprintf (object_freezer &freezer, const char *format, ...)
86 {
87 va_list ap;
88
89 va_start (ap, format);
90
91 int len = vsnprintf ((char *)freezer.force (1024), 1024, format, ap);
92
93 if (len >= 0)
94 freezer.alloc (len);
95
96 va_end (ap);
97 }
98
99 // XXX: function not returning an int
100 void
101 fputs (const char *s, object_freezer &freezer)
102 {
103 freezer.add (s);
104 }
105 #endif
106
107 static const char thawer_eof[] = "\n\n\n\0\0\0";
108
109 bool object_thawer::errors_are_fatal = true;
110
111 object_thawer::object_thawer (const char *data, AV *perlav)
112 : name (strdup ("(memory stream)"))
113 {
114 init ("(memory stream)");
115
116 av = perlav;
117 text = newSVpv (data, 0); sv_catpv (text, thawer_eof);
118 line = SvPVbyte_nolen (text);
119 next ();
120 }
121
122 object_thawer::object_thawer (const_utf8_string path)
123 {
124 init_from_file (path);
125 }
126
127 // convenience constructor
128 object_thawer::object_thawer (const_utf8_string dir, const_utf8_string file)
129 {
130 init_from_file (format ("%s/%s", dir, file));
131 }
132
133 void
134 object_thawer::init (const_utf8_string path)
135 {
136 name = strdup (path);
137 av = 0;
138 text = 0;
139 line = 0;
140 linenum = 0;
141
142 kw = KW_ERROR;
143 kw_str = 0;
144 value = 0;
145 }
146
147 void
148 object_thawer::init_from_file (const_utf8_string path)
149 {
150 init (path);
151
152 CALL_BEGIN (1);
153 CALL_ARG_SV (newSVpv (path, 0));
154 CALL_CALL ("cf::object_thawer_load", G_ARRAY);
155
156 if (count == 2)
157 {
158 // second value - perl objects
159 {
160 SV *sv = POPs;
161 if (SvROK (sv))
162 av = (AV *)SvREFCNT_inc (SvRV (sv));
163 }
164
165 // first value - text part, pad with 3 zeroes
166 {
167 SV *sv = POPs;
168 STRLEN len;
169 char *sv_ = SvPVbyte (sv, len);
170 text = newSV (len + sizeof (thawer_eof));
171 SvCUR_set (text, len + sizeof (thawer_eof));
172 memcpy (SvPVX (text), sv_, len);
173 memcpy (SvPVX (text) + len, thawer_eof, sizeof (thawer_eof));
174
175 line = SvPVX (text);
176 next ();
177 }
178 }
179
180 CALL_END;
181 }
182
183 void
184 object_thawer::get (attachable *obj, int oid)
185 {
186 if (!av || oid < 0) // this is actually an error of sorts
187 return;
188
189 SV **svp = av_fetch ((AV *)av, oid, 0);
190
191 if (!svp || !SvROK (*svp))
192 {
193 LOG (llevError, "trying to thaw duplicate or never-issued oid %d, ignoring.\n", oid);
194 return;
195 }
196
197 if (!SvROK (*svp))
198 {
199 LOG (llevError, "deserialised perl object is not an RV\n");
200 return;
201 }
202
203 HV *hv = (HV *)SvRV (*svp);
204
205 if (SvTYPE (hv) != SVt_PVHV)
206 {
207 LOG (llevError, "deserialised perl object is not a PVHV\n");
208 return;
209 }
210
211 if (obj->self)
212 {
213 // the hard way(?)
214
215 CALL_BEGIN (2);
216 CALL_ARG_SV (newRV_inc ((SV *)obj->self));
217 CALL_ARG_SV (newRV_inc ((SV *)hv));
218 PUTBACK;
219 call_method ("thawer_merge", G_DISCARD | G_EVAL);
220 SPAGAIN;
221 CALL_END;
222 }
223 else
224 {
225 // the easy way(?)
226
227 obj->self = hv;
228 SvRV_set (*svp, &PL_sv_undef);
229
230 sv_magicext ((SV *)obj->self, 0, PERL_MAGIC_ext, &attachable::vtbl, (char *)obj, 0);
231 }
232
233 obj->reattach ();
234 }
235
236 object_thawer::~object_thawer ()
237 {
238 if (text) SvREFCNT_dec (text);
239 if (av) SvREFCNT_dec (av);
240
241 resolve_delayed_derefs (false);
242
243 free ((void *)name);
244 }
245
246 static void
247 error_out ()
248 {
249 if (object_thawer::errors_are_fatal)
250 {
251 LOG (llevError, "(parse errors at this time are fatal, exiting)");
252 exit (1);
253 }
254 }
255
256 void
257 object_thawer::parse_warn (const char *msg) const
258 {
259 LOG (llevWarn, "%s:%d, \"%s %s\": %s\n",
260 this->name, linenum,
261 kw_str ? kw_str : "<null>",
262 value ? value : "<null>",
263 msg);
264 }
265
266 bool
267 object_thawer::parse_error (const char *type, const char *name, bool skip) const
268 {
269 if (!type) type = "file section";
270 if (!name) name = "generic";
271
272 switch (kw)
273 {
274 case KW_EOF:
275 LOG (llevError, "%s:%d end of file while reading %s '%s', aborting load.\n",
276 this->name, linenum, type, name);
277 error_out ();
278 return false;
279
280 case KW_ERROR:
281 LOG (llevError, "%s:%d error while reading %s '%s', at '%s', aborting load.\n",
282 this->name, linenum,
283 type, name,
284 kw_str ? kw_str : "<file load>");
285 error_out ();
286 return false;
287
288 default:
289 LOG (llevError, "%s:%d unexpected line (%s %s) while reading %s '%s', %s.\n",
290 this->name, linenum,
291 kw_str ? kw_str : "<null>",
292 value ? value : "<null>",
293 type, name,
294 skip ? "skipping line" : "aborting load");
295 error_out ();
296 return skip;
297 }
298 }
299
300 void
301 object_thawer::next ()
302 {
303 if (!line)
304 {
305 kw = KW_ERROR;
306 return;
307 }
308
309 for (;;)
310 {
311 char *p = line;
312
313 if (expect_false (*p <= ' '))
314 {
315 // skip whitespace (only some files need this)
316 while (*p == ' ' || *p == '\t')
317 p++;
318
319 line = p;
320 }
321
322 if (!*p)
323 {
324 kw = KW_EOF;
325 break;
326 }
327
328 // parse keyword
329 while (*p > ' ')
330 p++;
331
332 int klen = p - line;
333
334 value_nn = "";
335 value = 0;
336
337 if (*p++ != '\n')
338 {
339 // parse value
340 while (*(unsigned char *)p <= ' ' && *p != '\n')
341 ++p;
342
343 value_nn = value = p;
344
345 while (*p != '\n')
346 p++;
347
348 *p++ = 0;
349 }
350
351 ++linenum;
352 line [klen] = 0;
353 keyword_idx *kw_idx = kw_lex::match (line, klen);
354
355 kw_str = line;
356 line = p;
357
358 if (kw_idx)
359 {
360 kw = kw_idx->index;
361 break;
362 }
363 else if (!*kw_str || *kw_str == '#')
364 ; // empty/comment line
365 else
366 {
367 kw = KW_ERROR;
368 break;
369 }
370 }
371 }
372
373 bool
374 object_thawer::next_line ()
375 {
376 if (!line)
377 {
378 kw = KW_ERROR;
379 return 0;
380 }
381
382 for (;;)
383 {
384 char *p = line;
385
386 if (expect_false (*p <= ' '))
387 {
388 // skip whitespace (only some files need this)
389 while (*p == ' ' || *p == '\t')
390 p++;
391
392 line = p;
393 }
394
395 if (!*p)
396 {
397 kw = KW_EOF;
398 return 0;
399 }
400
401 kw = KW_value;
402 kw_str = p;
403 value_nn = p;
404 value = p;
405
406 // parse till newline
407 while (*p > '\n')
408 p++;
409
410 if (*p == '\n')
411 *p++ = 0;
412
413 ++linenum;
414
415 line = p;
416
417 if (*kw_str && *kw_str != '#')
418 break;
419
420 // empty/comment line -> skip it
421 }
422
423 return 1;
424 }
425
426 void
427 object_thawer::skip ()
428 {
429 shstr ml;
430
431 switch (kw)
432 {
433 case KW_msg: get_ml (KW_endmsg , ml); break;
434 case KW_lore: get_ml (KW_endlore , ml); break;
435 case KW_maplore: get_ml (KW_endmaplore, ml); break;
436 default: break;
437 }
438
439 next ();
440 }
441
442 void
443 object_thawer::skip_block ()
444 {
445 // must not stop at KW_ERROR, as those signify custom keys
446 while (kw != KW_EOF)
447 {
448 keyword w = kw;
449 skip ();
450
451 if (0 && (w == KW_map || w == KW_arch || w == KW_object || w == KW_region))
452 skip_block ();
453 else if (w == KW_end)
454 break;
455 }
456 }
457
458 void
459 object_thawer::get_ml (keyword kend, shstr &sh)
460 {
461 char kw[128];
462
463 int klen = keyword_len [kend];
464
465 kw [0] = '\n';
466 memcpy (kw + 1, keyword_str [kend], klen);
467 kw [klen + 1] = '\n';
468 kw [klen + 2] = 0;
469
470 ++linenum;
471
472 // first test for completely empty msg... "endXXX\n"
473 if (!strncmp (line, kw + 1, klen + 1))
474 {
475 sh = 0;
476
477 line += klen + 1;
478
479 return;
480 }
481 else
482 {
483 // multi-line strings are delimited by "\nendXXX\n" or "endXXX\n" (NULL)
484
485 char *end = strstr (line, kw);
486
487 if (!end)
488 {
489 sh = 0;
490 return;
491 }
492
493 *end = 0;
494 sh = line;
495
496 // count line numbers
497 while (line < end)
498 linenum += *line++ == '\n';
499
500 line += keyword_len [kend];
501
502 while (*line++ != '\n')
503 ;
504
505 ++linenum;
506 }
507 }
508
509 sint32
510 object_thawer::get_sint32 () const
511 {
512 const char *p = value_nn;
513
514 sint32 val = 0;
515 bool negate;
516
517 if (*p == '-')
518 {
519 negate = true;
520 ++p;
521 }
522 else
523 negate = false;
524
525 do
526 {
527 val *= 10;
528 val += *p++ - '0';
529 }
530 while (*p);
531
532 return negate ? -val : val;
533 }
534
535 void
536 object_thawer::delayed_deref (attachable *op, object_ptr &ptr, const char *ref)
537 {
538 op->refcnt_inc ();
539 delayed_ref r = { op, &ptr, ref ? strdup (ref) : 0 };
540 delrefs.push_back (r);
541 ptr = 0;
542 }
543
544 void
545 object_thawer::resolve_delayed_derefs (bool deref)
546 {
547 while (!delrefs.empty ())
548 {
549 delayed_ref r = delrefs.back ();
550 delrefs.pop_back ();
551
552 if (deref)
553 *r.ptr = object::deref (r.ref);
554
555 free ((void *)r.ref);
556 r.op->refcnt_dec ();
557 }
558 }