ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/freezethaw.C
Revision: 1.38
Committed: Thu Apr 15 04:56:47 2010 UTC (14 years, 1 month ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.37: +6 -4 lines
Log Message:
raramraramraram

File Contents

# Content
1 /*
2 * This file is part of Deliantra, the Roguelike Realtime MMORPG.
3 *
4 * Copyright (©) 2006,2007,2008,2009,2010 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 object_thawer::object_thawer (const char *path)
110 : name (strdup (path))
111 {
112 av = 0;
113 text = 0;
114 line = 0;
115 linenum = 0;
116
117 kw = KW_ERROR;
118 kw_str = 0;
119 value = 0;
120
121 if (path)
122 {
123 CALL_BEGIN (1);
124 CALL_ARG_SV (newSVpv (path, 0));
125 CALL_CALL ("cf::object_thawer_load", G_ARRAY);
126
127 if (count == 2)
128 {
129 // second value - perl objects
130 {
131 SV *sv = POPs;
132 if (SvROK (sv))
133 av = (AV *)SvREFCNT_inc (SvRV (sv));
134 }
135
136 // first value - text part, pad with 3 zeroes
137 {
138 SV *sv = POPs;
139 STRLEN len;
140 char *sv_ = SvPVbyte (sv, len);
141 text = newSV (len + sizeof (thawer_eof));
142 SvCUR_set (text, len + sizeof (thawer_eof));
143 memcpy (SvPVX (text), sv_, len);
144 memcpy (SvPVX (text) + len, thawer_eof, sizeof (thawer_eof));
145
146 line = SvPVX (text);
147 next ();
148 }
149 }
150
151 CALL_END;
152 }
153 }
154
155 object_thawer::object_thawer (const char *data, AV *perlav)
156 : name (strdup ("(memory stream)"))
157 {
158 av = perlav;
159 text = newSVpv (data, 0);
160 sv_catpv (text, thawer_eof);
161 line = SvPVbyte_nolen (text);
162 next ();
163 }
164
165 void
166 object_thawer::get (attachable *obj, int oid)
167 {
168 if (!av || oid < 0) // this is actually an error of sorts
169 return;
170
171 SV **svp = av_fetch ((AV *)av, oid, 0);
172
173 if (!svp || !SvROK (*svp))
174 {
175 LOG (llevError, "trying to thaw duplicate or never-issued oid %d, ignoring.\n", oid);
176 return;
177 }
178
179 if (!SvROK (*svp))
180 {
181 LOG (llevError, "deserialised perl object is not an RV\n");
182 return;
183 }
184
185 HV *hv = (HV *)SvRV (*svp);
186
187 if (SvTYPE (hv) != SVt_PVHV)
188 {
189 LOG (llevError, "deserialised perl object is not a PVHV\n");
190 return;
191 }
192
193 if (obj->self)
194 {
195 // the hard way(?)
196
197 CALL_BEGIN (2);
198 CALL_ARG_SV (newRV_inc ((SV *)obj->self));
199 CALL_ARG_SV (newRV_inc ((SV *)hv));
200 PUTBACK;
201 call_method ("thawer_merge", G_DISCARD | G_EVAL);
202 SPAGAIN;
203 CALL_END;
204 }
205 else
206 {
207 // the easy way(?)
208
209 obj->self = hv;
210 SvRV_set (*svp, &PL_sv_undef);
211
212 sv_magicext ((SV *)obj->self, 0, PERL_MAGIC_ext, &attachable::vtbl, (char *)obj, 0);
213 }
214
215 obj->reattach ();
216 }
217
218 object_thawer::~object_thawer ()
219 {
220 if (text) SvREFCNT_dec (text);
221 if (av) SvREFCNT_dec (av);
222
223 resolve_delayed_derefs (false);
224
225 free ((void *)name);
226 }
227
228 void
229 object_thawer::parse_warn (const char *msg)
230 {
231 LOG (llevWarn, "%s:%d, \"%s %s\": %s\n",
232 this->name, linenum,
233 kw_str ? kw_str : "<null>",
234 value ? value : "<null>",
235 msg);
236 }
237
238 bool
239 object_thawer::parse_error (const char *type, const char *name, bool skip)
240 {
241 if (!type) type = "file section";
242 if (!name) name = "generic";
243
244 switch (kw)
245 {
246 case KW_EOF:
247 LOG (llevError, "%s:%d end of file while reading %s '%s', aborting load.\n",
248 this->name, linenum, type, name);
249 return false;
250
251 case KW_ERROR:
252 LOG (llevError, "%s:%d error while reading %s '%s', at '%s', aborting load.\n",
253 this->name, linenum,
254 type, name,
255 kw_str ? kw_str : "<file load>");
256 return false;
257
258 default:
259 LOG (llevError, "%s:%d unexpected line (%s %s) while reading %s '%s', %s.\n",
260 this->name, linenum,
261 kw_str ? kw_str : "<null>",
262 value ? value : "<null>",
263 type, name,
264 skip ? "skipping line" : "aborting load");
265 return skip;
266 }
267 }
268
269 void
270 object_thawer::next ()
271 {
272 if (!line)
273 {
274 kw = KW_ERROR;
275 return;
276 }
277
278 for (;;)
279 {
280 char *p = line;
281
282 if (expect_false (*p <= ' '))
283 {
284 // skip whitespace (only some files need this)
285 while (*p == ' ' || *p == '\t')
286 p++;
287
288 line = p;
289 }
290
291 if (!*p)
292 {
293 kw = KW_EOF;
294 break;
295 }
296
297 // parse keyword
298 while (*p > ' ')
299 p++;
300
301 int klen = p - line;
302
303 value_nn = "";
304 value = 0;
305
306 if (*p++ != '\n')
307 {
308 // parse value
309 while (*(unsigned char *)p <= ' ' && *p != '\n')
310 ++p;
311
312 value_nn = value = p;
313
314 while (*p != '\n')
315 p++;
316
317 *p++ = 0;
318 }
319
320 ++linenum;
321 line [klen] = 0;
322 keyword_idx *kw_idx = kw_lex::match (line, klen);
323
324 kw_str = line;
325 line = p;
326
327 if (kw_idx)
328 {
329 kw = kw_idx->index;
330 break;
331 }
332 else if (!*kw_str || *kw_str == '#')
333 ; // empty/comment line
334 else
335 {
336 kw = KW_ERROR;
337 break;
338 }
339 }
340 }
341
342 bool
343 object_thawer::next_line ()
344 {
345 if (!line)
346 {
347 kw = KW_ERROR;
348 return 0;
349 }
350
351 for (;;)
352 {
353 char *p = line;
354
355 if (expect_false (*p <= ' '))
356 {
357 // skip whitespace (only some files need this)
358 while (*p == ' ' || *p == '\t')
359 p++;
360
361 line = p;
362 }
363
364 if (!*p)
365 {
366 kw = KW_EOF;
367 return 0;
368 }
369
370 kw = KW_value;
371 kw_str = p;
372 value_nn = p;
373 value = p;
374
375 // parse till newline
376 while (*p > '\n')
377 p++;
378
379 if (*p == '\n')
380 *p++ = 0;
381
382 ++linenum;
383
384 line = p;
385
386 if (*kw_str && *kw_str != '#')
387 break;
388
389 // empty/comment line -> skip it
390 }
391
392 return 1;
393 }
394
395 void
396 object_thawer::skip ()
397 {
398 shstr ml;
399
400 switch (kw)
401 {
402 case KW_msg: get_ml (KW_endmsg , ml); break;
403 case KW_lore: get_ml (KW_endlore , ml); break;
404 case KW_maplore: get_ml (KW_endmaplore, ml); break;
405 default: break;
406 }
407
408 next ();
409 }
410
411 void
412 object_thawer::skip_block ()
413 {
414 // must not stop at KW_ERROR, as those signify custom keys
415 while (kw != KW_EOF)
416 {
417 keyword w = kw;
418 skip ();
419
420 if (0 && (w == KW_map || w == KW_arch || w == KW_object || w == KW_region))
421 skip_block ();
422 else if (w == KW_end)
423 break;
424 }
425 }
426
427 void
428 object_thawer::get_ml (keyword kend, shstr &sh)
429 {
430 char kw[128];
431
432 int klen = keyword_len [kend];
433
434 kw [0] = '\n';
435 memcpy (kw + 1, keyword_str [kend], klen);
436 kw [klen + 1] = '\n';
437 kw [klen + 2] = 0;
438
439 ++linenum;
440
441 // first test for completely empty msg... "endXXX\n"
442 if (!strncmp (line, kw + 1, klen + 1))
443 {
444 sh = 0;
445
446 line += klen + 1;
447
448 return;
449 }
450 else
451 {
452 // multi-line strings are delimited by "\nendXXX\n" or "endXXX\n" (NULL)
453
454 char *end = strstr (line, kw);
455
456 if (!end)
457 {
458 sh = 0;
459 return;
460 }
461
462 *end = 0;
463 sh = line;
464
465 // count line numbers
466 while (line < end)
467 linenum += *line++ == '\n';
468
469 line += keyword_len [kend];
470
471 while (*line++ != '\n')
472 ;
473
474 ++linenum;
475 }
476 }
477
478 sint32
479 object_thawer::get_sint32 () const
480 {
481 const char *p = value_nn;
482
483 sint32 val = 0;
484 bool negate;
485
486 if (*p == '-')
487 {
488 negate = true;
489 ++p;
490 }
491 else
492 negate = false;
493
494 do
495 {
496 val *= 10;
497 val += *p++ - '0';
498 }
499 while (*p);
500
501 return negate ? -val : val;
502 }
503
504 void
505 object_thawer::delayed_deref (attachable *op, object_ptr &ptr, const char *ref)
506 {
507 op->refcnt_inc ();
508 delayed_ref r = { op, &ptr, ref ? strdup (ref) : 0 };
509 delrefs.push_back (r);
510 ptr = 0;
511 }
512
513 void
514 object_thawer::resolve_delayed_derefs (bool deref)
515 {
516 while (!delrefs.empty ())
517 {
518 delayed_ref r = delrefs.back ();
519 delrefs.pop_back ();
520
521 if (deref)
522 *r.ptr = object::deref (r.ref);
523
524 free ((void *)r.ref);
525 r.op->refcnt_dec ();
526 }
527 }