ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/freezethaw.C
Revision: 1.49
Committed: Wed Dec 5 19:03:27 2018 UTC (5 years, 5 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.48: +2 -2 lines
Log Message:
some bugfixes

File Contents

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