Optimize struct initialization and accessors for the common case.
[bpt/guile.git] / libguile / struct.c
CommitLineData
aa42c036 1/* Copyright (C) 1996,1997,1998,1999,2000,2001, 2003, 2004, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
0f2d19dd 2 *
73be1d9e 3 * This library is free software; you can redistribute it and/or
53befeb7
NJ
4 * modify it under the terms of the GNU Lesser General Public License
5 * as published by the Free Software Foundation; either version 3 of
6 * the License, or (at your option) any later version.
0f2d19dd 7 *
53befeb7
NJ
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
73be1d9e
MV
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
0f2d19dd 12 *
73be1d9e
MV
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
53befeb7
NJ
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 * 02110-1301 USA
73be1d9e 17 */
1bbd0b84 18
0f2d19dd 19\f
dbb605f5 20#ifdef HAVE_CONFIG_H
a6f7f57d
RB
21# include <config.h>
22#endif
0f2d19dd 23
66e78727 24#include <alloca.h>
aa42c036 25#include <assert.h>
66e78727 26
a0599745 27#include "libguile/_scm.h"
4e047c3e 28#include "libguile/async.h"
a0599745
MD
29#include "libguile/chars.h"
30#include "libguile/eval.h"
31#include "libguile/alist.h"
32#include "libguile/weaks.h"
33#include "libguile/hashtab.h"
34#include "libguile/ports.h"
35#include "libguile/strings.h"
27646f41 36#include "libguile/srfi-13.h"
a0599745
MD
37
38#include "libguile/validate.h"
39#include "libguile/struct.h"
0f2d19dd 40
d15ad007
LC
41#include "libguile/eq.h"
42
95b88819
GH
43#ifdef HAVE_STRING_H
44#include <string.h>
45#endif
46
1c44468d 47#include "libguile/bdw-gc.h"
5e67dc27 48
0f2d19dd
JB
49\f
50
b6cf4d02
AW
51/* A needlessly obscure test. */
52#define SCM_LAYOUT_TAILP(X) (((X) & 32) == 0) /* R, W or O */
53
0f2d19dd 54static SCM required_vtable_fields = SCM_BOOL_F;
b6cf4d02
AW
55static SCM required_applicable_fields = SCM_BOOL_F;
56static SCM required_applicable_with_setter_fields = SCM_BOOL_F;
57SCM scm_struct_table = SCM_BOOL_F;
db5ed685
AW
58SCM scm_applicable_struct_vtable_vtable;
59SCM scm_applicable_struct_with_setter_vtable_vtable;
60SCM scm_standard_vtable_vtable;
61
0f2d19dd
JB
62
63\f
a1ec6916 64SCM_DEFINE (scm_make_struct_layout, "make-struct-layout", 1, 0, 0,
1bbd0b84 65 (SCM fields),
b380b885 66 "Return a new structure layout object.\n\n"
7c31152f 67 "@var{fields} must be a string made up of pairs of characters\n"
b380b885
MD
68 "strung together. The first character of each pair describes a field\n"
69 "type, the second a field protection. Allowed types are 'p' for\n"
70 "GC-protected Scheme data, 'u' for unprotected binary data, and 's' for\n"
04323af4 71 "a field that points to the structure itself. Allowed protections\n"
b6cf4d02
AW
72 "are 'w' for mutable fields, 'h' for hidden fields, 'r' for read-only\n"
73 "fields, and 'o' for opaque fields.\n\n"
74 "Hidden fields are writable, but they will not consume an initializer arg\n"
75 "passed to @code{make-struct}. They are useful to add slots to a struct\n"
76 "in a way that preserves backward-compatibility with existing calls to\n"
77 "@code{make-struct}, especially for derived vtables.\n\n"
78 "The last field protection specification may be capitalized to indicate\n"
79 "that the field is a tail-array.")
1bbd0b84 80#define FUNC_NAME s_scm_make_struct_layout
0f2d19dd
JB
81{
82 SCM new_sym;
27646f41 83 scm_t_wchar c;
2ade72d7 84
7f991c7d
LC
85 SCM_VALIDATE_STRING (1, fields);
86
1bbd0b84 87 { /* scope */
1be6b49c 88 size_t len;
0f2d19dd
JB
89 int x;
90
cc95e00a 91 len = scm_i_string_length (fields);
2ade72d7
DH
92 if (len % 2 == 1)
93 SCM_MISC_ERROR ("odd length field specification: ~S",
1afff620 94 scm_list_1 (fields));
2ade72d7 95
0f2d19dd
JB
96 for (x = 0; x < len; x += 2)
97 {
27646f41 98 switch (c = scm_i_string_ref (fields, x))
0f2d19dd
JB
99 {
100 case 'u':
101 case 'p':
102#if 0
103 case 'i':
104 case 'd':
105#endif
106 case 's':
107 break;
108 default:
2ade72d7 109 SCM_MISC_ERROR ("unrecognized field type: ~S",
27646f41 110 scm_list_1 (SCM_MAKE_CHAR (c)));
0f2d19dd
JB
111 }
112
27646f41 113 switch (c = scm_i_string_ref (fields, x + 1))
0f2d19dd
JB
114 {
115 case 'w':
b6cf4d02 116 case 'h':
27646f41 117 if (scm_i_string_ref (fields, x) == 's')
2ade72d7 118 SCM_MISC_ERROR ("self fields not writable", SCM_EOL);
0f2d19dd
JB
119 case 'r':
120 case 'o':
121 break;
2c36c351
MD
122 case 'R':
123 case 'W':
124 case 'O':
27646f41 125 if (scm_i_string_ref (fields, x) == 's')
2ade72d7
DH
126 SCM_MISC_ERROR ("self fields not allowed in tail array",
127 SCM_EOL);
128 if (x != len - 2)
129 SCM_MISC_ERROR ("tail array field must be last field in layout",
130 SCM_EOL);
2c36c351 131 break;
0f2d19dd 132 default:
2ade72d7 133 SCM_MISC_ERROR ("unrecognized ref specification: ~S",
27646f41 134 scm_list_1 (SCM_MAKE_CHAR (c)));
0f2d19dd
JB
135 }
136#if 0
27646f41 137 if (scm_i_string_ref (fields, x, 'd'))
0f2d19dd 138 {
27646f41 139 if (!scm_i_string_ref (fields, x+2, '-'))
2ade72d7 140 SCM_MISC_ERROR ("missing dash field at position ~A",
e11e83f3 141 scm_list_1 (scm_from_int (x / 2)));
0f2d19dd
JB
142 x += 2;
143 goto recheck_ref;
144 }
145#endif
146 }
cc95e00a 147 new_sym = scm_string_to_symbol (fields);
0f2d19dd 148 }
8824ac88
MV
149 scm_remember_upto_here_1 (fields);
150 return new_sym;
0f2d19dd 151}
1bbd0b84 152#undef FUNC_NAME
0f2d19dd
JB
153
154\f
aa42c036
LC
155/* Check whether VTABLE instances have a simple layout (i.e., either only "pr"
156 or only "pw" fields) and update its flags accordingly. */
157static void
158set_vtable_layout_flags (SCM vtable)
159{
160 size_t len, field;
161 SCM layout;
162 const char *c_layout;
163 scm_t_bits flags = SCM_VTABLE_FLAG_SIMPLE;
164
165 layout = SCM_VTABLE_LAYOUT (vtable);
166 c_layout = scm_i_symbol_chars (layout);
167 len = scm_i_symbol_length (layout);
168
169 assert (len % 2 == 0);
170
171 /* Update FLAGS according to LAYOUT. */
172 for (field = 0;
173 field < len && flags & SCM_VTABLE_FLAG_SIMPLE;
174 field += 2)
175 {
176 if (c_layout[field] != 'p')
177 flags = 0;
178 else
179 switch (c_layout[field + 1])
180 {
181 case 'w':
182 case 'W':
183 if (!(flags & SCM_VTABLE_FLAG_SIMPLE_RW) && field > 0)
184 /* There's a mixture of `w' and `r' flags. */
185 flags = 0;
186 else
187 flags |= SCM_VTABLE_FLAG_SIMPLE_RW;
188 break;
189
190 case 'r':
191 case 'R':
192 if (flags & SCM_VTABLE_FLAG_SIMPLE_RW)
193 /* There's a mixture of `w' and `r' flags. */
194 flags = 0;
195 break;
196
197 default:
198 flags = 0;
199 }
200 }
201
202 if (flags & SCM_VTABLE_FLAG_SIMPLE)
203 {
204 /* VTABLE is simple so update its flags and record the size of its
205 instances. */
206 SCM_SET_VTABLE_FLAGS (vtable, flags);
207 SCM_STRUCT_DATA_SET (vtable, scm_vtable_index_size, len / 2);
208 }
209}
0f2d19dd 210
51f66c91
AW
211void
212scm_i_struct_inherit_vtable_magic (SCM vtable, SCM obj)
213#define FUNC_NAME "%inherit-vtable-magic"
214{
215 /* Verily, what is the deal here, you ask? Basically, we need to know a couple
216 of properties of structures at runtime. For example, "is this structure a
217 vtable of vtables (a metaclass)?"; also, "is this structure applicable?".
218 Both of these questions also imply a certain layout of the structure. So
219 instead of checking the layout at runtime, what we do is pre-verify the
220 layout -- so that at runtime we can just check the applicable flag and
221 dispatch directly to the Scheme procedure in slot 0.
222 */
223 SCM olayout;
224
225 /* verify that obj is a valid vtable */
226 if (scm_is_false (scm_symbol_p (SCM_VTABLE_LAYOUT (obj))))
227 scm_misc_error (FUNC_NAME, "invalid layout for new vtable",
228 scm_list_1 (SCM_VTABLE_LAYOUT (obj)));
229
aa42c036
LC
230 set_vtable_layout_flags (obj);
231
51f66c91
AW
232 /* if obj's vtable is compatible with the required vtable (class) layout, it
233 is a metaclass */
234 olayout = scm_symbol_to_string (SCM_VTABLE_LAYOUT (obj));
235 if (scm_is_true (scm_leq_p (scm_string_length (required_vtable_fields),
236 scm_string_length (olayout)))
237 && scm_is_true (scm_string_eq (olayout, required_vtable_fields,
238 scm_from_size_t (0),
239 scm_string_length (required_vtable_fields),
240 scm_from_size_t (0),
241 scm_string_length (required_vtable_fields))))
242 SCM_SET_VTABLE_FLAGS (obj, SCM_VTABLE_FLAG_VTABLE);
243
244 /* finally if obj is an applicable class, verify that its vtable is
245 compatible with the required applicable layout */
246 if (SCM_VTABLE_FLAG_IS_SET (vtable, SCM_VTABLE_FLAG_SETTER_VTABLE))
247 {
248 if (scm_is_false (scm_string_eq (olayout, required_applicable_with_setter_fields,
249 scm_from_size_t (0),
250 scm_from_size_t (4),
251 scm_from_size_t (0),
252 scm_from_size_t (4))))
253 scm_misc_error (FUNC_NAME, "invalid applicable-with-setter struct layout",
254 scm_list_1 (olayout));
255 SCM_SET_VTABLE_FLAGS (obj, SCM_VTABLE_FLAG_APPLICABLE | SCM_VTABLE_FLAG_SETTER);
256 }
257 else if (SCM_VTABLE_FLAG_IS_SET (vtable, SCM_VTABLE_FLAG_APPLICABLE_VTABLE))
258 {
259 if (scm_is_false (scm_string_eq (olayout, required_applicable_fields,
260 scm_from_size_t (0),
261 scm_from_size_t (2),
262 scm_from_size_t (0),
263 scm_from_size_t (2))))
264 scm_misc_error (FUNC_NAME, "invalid applicable struct layout",
265 scm_list_1 (olayout));
266 SCM_SET_VTABLE_FLAGS (obj, SCM_VTABLE_FLAG_APPLICABLE);
267 }
268}
269#undef FUNC_NAME
0f2d19dd 270
1cc91f1b 271
f7620510 272static void
66e78727
AW
273scm_struct_init (SCM handle, SCM layout, size_t n_tail,
274 size_t n_inits, scm_t_bits *inits)
0f2d19dd 275{
aa42c036
LC
276 SCM vtable;
277 scm_t_bits *mem;
278
279 vtable = SCM_STRUCT_VTABLE (handle);
280 mem = SCM_STRUCT_DATA (handle);
281
282 if (SCM_UNPACK (vtable) != 0
283 && SCM_VTABLE_FLAG_IS_SET (vtable, SCM_VTABLE_FLAG_SIMPLE)
284 && n_tail == 0
285 && n_inits == SCM_STRUCT_DATA_REF (vtable, scm_vtable_index_size))
286 /* The fast path: HANDLE has N_INITS "p" fields. */
287 memcpy (mem, inits, n_inits * sizeof (SCM));
288 else
0f2d19dd 289 {
aa42c036
LC
290 scm_t_wchar prot = 0;
291 int n_fields = scm_i_symbol_length (layout) / 2;
292 int tailp = 0;
293 int i;
294 size_t inits_idx = 0;
295
296 i = -2;
297 while (n_fields)
2c36c351 298 {
aa42c036 299 if (!tailp)
2c36c351 300 {
aa42c036
LC
301 i += 2;
302 prot = scm_i_symbol_ref (layout, i+1);
303 if (SCM_LAYOUT_TAILP (prot))
304 {
305 tailp = 1;
306 prot = prot == 'R' ? 'r' : prot == 'W' ? 'w' : 'o';
307 *mem++ = (scm_t_bits)n_tail;
308 n_fields += n_tail - 1;
309 if (n_fields == 0)
310 break;
311 }
2c36c351 312 }
aa42c036 313 switch (scm_i_symbol_ref (layout, i))
0f2d19dd 314 {
aa42c036
LC
315 case 'u':
316 if ((prot != 'r' && prot != 'w') || inits_idx == n_inits)
317 *mem = 0;
318 else
319 {
320 *mem = scm_to_ulong (SCM_PACK (inits[inits_idx]));
321 inits_idx++;
322 }
323 break;
324
325 case 'p':
326 if ((prot != 'r' && prot != 'w') || inits_idx == n_inits)
327 *mem = SCM_UNPACK (SCM_BOOL_F);
328 else
329 {
330 *mem = inits[inits_idx];
331 inits_idx++;
332 }
333
334 break;
335
336 case 's':
337 *mem = SCM_UNPACK (handle);
338 break;
0f2d19dd 339 }
0f2d19dd 340
aa42c036
LC
341 n_fields--;
342 mem++;
0f2d19dd 343 }
0f2d19dd
JB
344 }
345}
346
347
a1ec6916 348SCM_DEFINE (scm_struct_p, "struct?", 1, 0, 0,
1bbd0b84 349 (SCM x),
0233bfc1 350 "Return @code{#t} iff @var{x} is a structure object, else\n"
942e5b91 351 "@code{#f}.")
1bbd0b84 352#define FUNC_NAME s_scm_struct_p
0f2d19dd 353{
7888309b 354 return scm_from_bool(SCM_STRUCTP (x));
0f2d19dd 355}
1bbd0b84 356#undef FUNC_NAME
0f2d19dd 357
a1ec6916 358SCM_DEFINE (scm_struct_vtable_p, "struct-vtable?", 1, 0, 0,
1bbd0b84 359 (SCM x),
0233bfc1 360 "Return @code{#t} iff @var{x} is a vtable structure.")
1bbd0b84 361#define FUNC_NAME s_scm_struct_vtable_p
0f2d19dd 362{
b6cf4d02
AW
363 return scm_from_bool
364 (SCM_STRUCTP (x)
365 && SCM_STRUCT_VTABLE_FLAG_IS_SET (x, SCM_VTABLE_FLAG_VTABLE));
0f2d19dd 366}
1bbd0b84 367#undef FUNC_NAME
0f2d19dd 368
14d1400f 369
51f66c91
AW
370/* Finalization: invoke the finalizer of the struct pointed to by PTR. */
371static void
372struct_finalizer_trampoline (GC_PTR ptr, GC_PTR unused_data)
373{
374 SCM obj = PTR2SCM (ptr);
375 scm_t_struct_finalize finalize = SCM_STRUCT_FINALIZER (obj);
376
377 if (finalize)
378 finalize (obj);
379}
380
14d1400f
JB
381/* All struct data must be allocated at an address whose bottom three
382 bits are zero. This is because the tag for a struct lives in the
383 bottom three bits of the struct's car, and the upper bits point to
384 the data of its vtable, which is a struct itself. Thus, if the
385 address of that data doesn't end in three zeros, tagging it will
386 destroy the pointer.
387
b6cf4d02
AW
388 I suppose we should make it clear here that, the data must be 8-byte aligned,
389 *within* the struct, and the struct itself should be 8-byte aligned. In
390 practice we ensure this because the data starts two words into a struct.
14d1400f 391
b6cf4d02
AW
392 This function allocates an 8-byte aligned block of memory, whose first word
393 points to the given vtable data, then a data pointer, then n_words of data.
394 */
395SCM
96a44c1c 396scm_i_alloc_struct (scm_t_bits *vtable_data, int n_words)
14d1400f 397{
b6cf4d02
AW
398 scm_t_bits ret;
399 ret = (scm_t_bits)scm_gc_malloc (sizeof (scm_t_bits) * (n_words + 2), "struct");
b6cf4d02
AW
400 SCM_SET_CELL_WORD_0 (SCM_PACK (ret), (scm_t_bits)vtable_data | scm_tc3_struct);
401 SCM_SET_CELL_WORD_1 (SCM_PACK (ret),
402 (scm_t_bits)SCM_CELL_OBJECT_LOC (SCM_PACK (ret), 2));
5e67dc27 403
51f66c91
AW
404 /* vtable_data can be null when making a vtable vtable */
405 if (vtable_data && vtable_data[scm_vtable_index_instance_finalize])
406 {
407 /* Register a finalizer for the newly created instance. */
408 GC_finalization_proc prev_finalizer;
409 GC_PTR prev_finalizer_data;
410 GC_REGISTER_FINALIZER_NO_ORDER ((void*)ret,
411 struct_finalizer_trampoline,
412 NULL,
413 &prev_finalizer,
414 &prev_finalizer_data);
415 }
5e67dc27 416
51f66c91 417 return SCM_PACK (ret);
5e67dc27
LC
418}
419
5e67dc27 420\f
66e78727
AW
421SCM
422scm_c_make_structv (SCM vtable, size_t n_tail, size_t n_init, scm_t_bits *init)
423#define FUNC_NAME "make-struct"
0f2d19dd
JB
424{
425 SCM layout;
a55c2b68 426 size_t basic_size;
b6cf4d02 427 SCM obj;
0f2d19dd 428
34d19ef6 429 SCM_VALIDATE_VTABLE (1, vtable);
0f2d19dd 430
b6cf4d02 431 layout = SCM_VTABLE_LAYOUT (vtable);
cc95e00a 432 basic_size = scm_i_symbol_length (layout) / 2;
651f2cd2 433
66e78727 434 if (n_tail != 0)
651f2cd2
KR
435 {
436 SCM layout_str, last_char;
437
438 if (basic_size == 0)
439 {
440 bad_tail:
441 SCM_MISC_ERROR ("tail array not allowed unless layout ends R, W, or O", SCM_EOL);
442 }
443
444 layout_str = scm_symbol_to_string (layout);
445 last_char = scm_string_ref (layout_str,
446 scm_from_size_t (2 * basic_size - 1));
447 if (! SCM_LAYOUT_TAILP (SCM_CHAR (last_char)))
448 goto bad_tail;
449 }
cb823e63 450
96a44c1c 451 obj = scm_i_alloc_struct (SCM_STRUCT_DATA (vtable), basic_size + n_tail);
5e67dc27 452
66e78727 453 scm_struct_init (obj, layout, n_tail, n_init, init);
b6cf4d02 454
51f66c91
AW
455 /* only check things and inherit magic if the layout was passed as an initarg.
456 something of a hack, but it's for back-compatibility. */
b6cf4d02 457 if (SCM_VTABLE_FLAG_IS_SET (vtable, SCM_VTABLE_FLAG_VTABLE)
b6cf4d02 458 && scm_is_true (SCM_VTABLE_LAYOUT (obj)))
51f66c91 459 scm_i_struct_inherit_vtable_magic (vtable, obj);
651f2cd2 460
b6cf4d02 461 return obj;
0f2d19dd 462}
1bbd0b84 463#undef FUNC_NAME
0f2d19dd 464
66e78727
AW
465SCM
466scm_c_make_struct (SCM vtable, size_t n_tail, size_t n_init, scm_t_bits init, ...)
467{
468 va_list foo;
469 scm_t_bits *v;
470 size_t i;
471
472 v = alloca (sizeof (scm_t_bits) * n_init);
473
474 va_start (foo, init);
475 for (i = 0; i < n_init; i++)
476 {
477 v[i] = init;
478 init = va_arg (foo, scm_t_bits);
479 }
480 va_end (foo);
481
482 return scm_c_make_structv (vtable, n_tail, n_init, v);
483}
484
485SCM_DEFINE (scm_make_struct, "make-struct", 2, 0, 1,
486 (SCM vtable, SCM tail_array_size, SCM init),
487 "Create a new structure.\n\n"
488 "@var{type} must be a vtable structure (@pxref{Vtables}).\n\n"
489 "@var{tail-elts} must be a non-negative integer. If the layout\n"
490 "specification indicated by @var{type} includes a tail-array,\n"
491 "this is the number of elements allocated to that array.\n\n"
492 "The @var{init1}, @dots{} are optional arguments describing how\n"
493 "successive fields of the structure should be initialized. Only fields\n"
494 "with protection 'r' or 'w' can be initialized, except for fields of\n"
495 "type 's', which are automatically initialized to point to the new\n"
496 "structure itself. Fields with protection 'o' can not be initialized by\n"
497 "Scheme programs.\n\n"
498 "If fewer optional arguments than initializable fields are supplied,\n"
499 "fields of type 'p' get default value #f while fields of type 'u' are\n"
500 "initialized to 0.\n\n"
501 "For more information, see the documentation for @code{make-vtable-vtable}.")
502#define FUNC_NAME s_scm_make_struct
503{
504 size_t i, n_init;
505 long ilen;
506 scm_t_bits *v;
507
508 SCM_VALIDATE_VTABLE (1, vtable);
509 ilen = scm_ilength (init);
510 if (ilen < 0)
511 SCM_MISC_ERROR ("Rest arguments do not form a proper list.", SCM_EOL);
512
513 n_init = (size_t)ilen;
514
515 /* best to use alloca, but init could be big, so hack to avoid a possible
516 stack overflow */
517 if (n_init < 64)
518 v = alloca (n_init * sizeof(scm_t_bits));
519 else
520 v = scm_gc_malloc (n_init * sizeof(scm_t_bits), "struct");
521
522 for (i = 0; i < n_init; i++, init = SCM_CDR (init))
523 v[i] = SCM_UNPACK (SCM_CAR (init));
524
525 return scm_c_make_structv (vtable, scm_to_size_t (tail_array_size), n_init, v);
526}
527#undef FUNC_NAME
528
0f2d19dd
JB
529
530
a1ec6916 531SCM_DEFINE (scm_make_vtable_vtable, "make-vtable-vtable", 2, 0, 1,
04323af4 532 (SCM user_fields, SCM tail_array_size, SCM init),
b380b885 533 "Return a new, self-describing vtable structure.\n\n"
04323af4
MD
534 "@var{user-fields} is a string describing user defined fields of the\n"
535 "vtable beginning at index @code{vtable-offset-user}\n"
536 "(see @code{make-struct-layout}).\n\n"
b380b885
MD
537 "@var{tail-size} specifies the size of the tail-array (if any) of\n"
538 "this vtable.\n\n"
6386e25c 539 "@var{init1}, @dots{} are the optional initializers for the fields of\n"
04323af4
MD
540 "the vtable.\n\n"
541 "Vtables have one initializable system field---the struct printer.\n"
542 "This field comes before the user fields in the initializers passed\n"
543 "to @code{make-vtable-vtable} and @code{make-struct}, and thus works as\n"
544 "a third optional argument to @code{make-vtable-vtable} and a fourth to\n"
545 "@code{make-struct} when creating vtables:\n\n"
546 "If the value is a procedure, it will be called instead of the standard\n"
547 "printer whenever a struct described by this vtable is printed.\n"
548 "The procedure will be called with arguments STRUCT and PORT.\n\n"
549 "The structure of a struct is described by a vtable, so the vtable is\n"
550 "in essence the type of the struct. The vtable is itself a struct with\n"
551 "a vtable. This could go on forever if it weren't for the\n"
29b4f9fb 552 "vtable-vtables which are self-describing vtables, and thus terminate\n"
04323af4
MD
553 "the chain.\n\n"
554 "There are several potential ways of using structs, but the standard\n"
555 "one is to use three kinds of structs, together building up a type\n"
556 "sub-system: one vtable-vtable working as the root and one or several\n"
557 "\"types\", each with a set of \"instances\". (The vtable-vtable should be\n"
29b4f9fb 558 "compared to the class <class> which is the class of itself.)\n\n"
1e6808ea 559 "@lisp\n"
04323af4
MD
560 "(define ball-root (make-vtable-vtable \"pr\" 0))\n\n"
561 "(define (make-ball-type ball-color)\n"
562 " (make-struct ball-root 0\n"
563 " (make-struct-layout \"pw\")\n"
564 " (lambda (ball port)\n"
565 " (format port \"#<a ~A ball owned by ~A>\"\n"
566 " (color ball)\n"
567 " (owner ball)))\n"
568 " ball-color))\n"
569 "(define (color ball) (struct-ref (struct-vtable ball) vtable-offset-user))\n"
570 "(define (owner ball) (struct-ref ball 0))\n\n"
571 "(define red (make-ball-type 'red))\n"
572 "(define green (make-ball-type 'green))\n\n"
573 "(define (make-ball type owner) (make-struct type 0 owner))\n\n"
574 "(define ball (make-ball green 'Nisse))\n"
575 "ball @result{} #<a green ball owned by Nisse>\n"
9401323e 576 "@end lisp")
1bbd0b84 577#define FUNC_NAME s_scm_make_vtable_vtable
0f2d19dd
JB
578{
579 SCM fields;
580 SCM layout;
a55c2b68 581 size_t basic_size;
66e78727 582 size_t n_tail, i, n_init;
b6cf4d02 583 SCM obj;
66e78727
AW
584 long ilen;
585 scm_t_bits *v;
0f2d19dd 586
d1ca2c64 587 SCM_VALIDATE_STRING (1, user_fields);
66e78727
AW
588 ilen = scm_ilength (init);
589 if (ilen < 0)
590 SCM_MISC_ERROR ("Rest arguments do not form a proper list.", SCM_EOL);
591
592 n_init = (size_t)ilen + 1; /* + 1 for the layout */
593
594 /* best to use alloca, but init could be big, so hack to avoid a possible
595 stack overflow */
596 if (n_init < 64)
597 v = alloca (n_init * sizeof(scm_t_bits));
598 else
599 v = scm_gc_malloc (n_init * sizeof(scm_t_bits), "struct");
0f2d19dd 600
1afff620
KN
601 fields = scm_string_append (scm_list_2 (required_vtable_fields,
602 user_fields));
0f2d19dd 603 layout = scm_make_struct_layout (fields);
cc95e00a 604 basic_size = scm_i_symbol_length (layout) / 2;
66e78727
AW
605 n_tail = scm_to_size_t (tail_array_size);
606
607 i = 0;
608 v[i++] = SCM_UNPACK (layout);
609 for (; i < n_init; i++, init = SCM_CDR (init))
610 v[i] = SCM_UNPACK (SCM_CAR (init));
611
9de87eea 612 SCM_CRITICAL_SECTION_START;
96a44c1c 613 obj = scm_i_alloc_struct (NULL, basic_size + n_tail);
b6cf4d02
AW
614 /* magic magic magic */
615 SCM_SET_CELL_WORD_0 (obj, (scm_t_bits)SCM_STRUCT_DATA (obj) | scm_tc3_struct);
9de87eea 616 SCM_CRITICAL_SECTION_END;
66e78727 617 scm_struct_init (obj, layout, n_tail, n_init, v);
b6cf4d02
AW
618 SCM_SET_VTABLE_FLAGS (obj, SCM_VTABLE_FLAG_VTABLE);
619 return obj;
0f2d19dd 620}
1bbd0b84 621#undef FUNC_NAME
0f2d19dd 622
d15ad007 623
651f2cd2
KR
624SCM_DEFINE (scm_make_vtable, "make-vtable", 1, 1, 0,
625 (SCM fields, SCM printer),
626 "Create a vtable, for creating structures with the given\n"
627 "@var{fields}.\n"
628 "\n"
629 "The optional @var{printer} argument is a function to be called\n"
630 "@code{(@var{printer} struct port)} on the structures created.\n"
631 "It should look at @var{struct} and write to @var{port}.")
632#define FUNC_NAME s_scm_make_vtable
633{
634 if (SCM_UNBNDP (printer))
635 printer = SCM_BOOL_F;
636
db5ed685 637 return scm_make_struct (scm_standard_vtable_vtable, SCM_INUM0,
651f2cd2
KR
638 scm_list_2 (scm_make_struct_layout (fields),
639 printer));
640}
641#undef FUNC_NAME
642
643
d15ad007
LC
644/* Return true if S1 and S2 are equal structures, i.e., if their vtable and
645 contents are the same. Field protections are honored. Thus, it is an
646 error to test the equality of structures that contain opaque fields. */
647SCM
648scm_i_struct_equalp (SCM s1, SCM s2)
649#define FUNC_NAME "scm_i_struct_equalp"
650{
651 SCM vtable1, vtable2, layout;
652 size_t struct_size, field_num;
653
654 SCM_VALIDATE_STRUCT (1, s1);
655 SCM_VALIDATE_STRUCT (2, s2);
656
657 vtable1 = SCM_STRUCT_VTABLE (s1);
658 vtable2 = SCM_STRUCT_VTABLE (s2);
659
660 if (!scm_is_eq (vtable1, vtable2))
661 return SCM_BOOL_F;
662
663 layout = SCM_STRUCT_LAYOUT (s1);
664 struct_size = scm_i_symbol_length (layout) / 2;
665
666 for (field_num = 0; field_num < struct_size; field_num++)
667 {
668 SCM s_field_num;
669 SCM field1, field2;
670
671 /* We have to use `scm_struct_ref ()' here so that fields are accessed
672 consistently, notably wrt. field types and access rights. */
673 s_field_num = scm_from_size_t (field_num);
674 field1 = scm_struct_ref (s1, s_field_num);
675 field2 = scm_struct_ref (s2, s_field_num);
676
42ddb3cb
LC
677 /* Self-referencing fields (type `s') must be skipped to avoid infinite
678 recursion. */
679 if (!(scm_is_eq (field1, s1) && (scm_is_eq (field2, s2))))
680 if (scm_is_false (scm_equal_p (field1, field2)))
681 return SCM_BOOL_F;
d15ad007
LC
682 }
683
42ddb3cb
LC
684 /* FIXME: Tail elements should be tested for equality. */
685
d15ad007
LC
686 return SCM_BOOL_T;
687}
688#undef FUNC_NAME
689
690
0f2d19dd
JB
691\f
692
693
a1ec6916 694SCM_DEFINE (scm_struct_ref, "struct-ref", 2, 0, 0,
1bbd0b84 695 (SCM handle, SCM pos),
b6cf4d02 696 "Access the @var{n}th field of @var{struct}.\n\n"
b380b885
MD
697 "If the field is of type 'p', then it can be set to an arbitrary value.\n\n"
698 "If the field is of type 'u', then it can only be set to a non-negative\n"
699 "integer value small enough to fit in one machine word.")
1bbd0b84 700#define FUNC_NAME s_scm_struct_ref
0f2d19dd 701{
aa42c036
LC
702 SCM vtable, answer = SCM_UNDEFINED;
703 scm_t_bits *data;
a55c2b68 704 size_t p;
0f2d19dd 705
34d19ef6 706 SCM_VALIDATE_STRUCT (1, handle);
0f2d19dd 707
aa42c036 708 vtable = SCM_STRUCT_VTABLE (handle);
0f2d19dd 709 data = SCM_STRUCT_DATA (handle);
a55c2b68 710 p = scm_to_size_t (pos);
0f2d19dd 711
aa42c036
LC
712 if (SCM_LIKELY (SCM_VTABLE_FLAG_IS_SET (vtable, SCM_VTABLE_FLAG_SIMPLE)
713 && p < SCM_STRUCT_DATA_REF (vtable, scm_vtable_index_size)))
2c36c351 714 {
aa42c036
LC
715 /* The fast path: HANDLE is a struct with only "p" fields. */
716 answer = SCM_PACK (data[p]);
2c36c351 717 }
2c36c351 718 else
0f2d19dd 719 {
aa42c036
LC
720 SCM layout;
721 size_t layout_len, n_fields;
722 scm_t_wchar field_type = 0;
723
724 layout = SCM_STRUCT_LAYOUT (handle);
725 layout_len = scm_i_symbol_length (layout);
726 n_fields = layout_len / 2;
727
728 if (SCM_LAYOUT_TAILP (scm_i_symbol_ref (layout, layout_len - 1)))
729 n_fields += data[n_fields - 1];
730
731 SCM_ASSERT_RANGE (1, pos, p < n_fields);
732
733 if (p * 2 < layout_len)
734 {
735 scm_t_wchar ref;
736 field_type = scm_i_symbol_ref (layout, p * 2);
737 ref = scm_i_symbol_ref (layout, p * 2 + 1);
738 if ((ref != 'r') && (ref != 'w') && (ref != 'h'))
739 {
740 if ((ref == 'R') || (ref == 'W'))
741 field_type = 'u';
742 else
743 SCM_MISC_ERROR ("ref denied for field ~A", scm_list_1 (pos));
744 }
745 }
746 else if (scm_i_symbol_ref (layout, layout_len - 1) != 'O')
747 field_type = scm_i_symbol_ref(layout, layout_len - 2);
748 else
749 SCM_MISC_ERROR ("ref denied for field ~A", scm_list_1 (pos));
750
751 switch (field_type)
752 {
753 case 'u':
754 answer = scm_from_ulong (data[p]);
755 break;
0f2d19dd
JB
756
757#if 0
aa42c036
LC
758 case 'i':
759 answer = scm_from_long (data[p]);
760 break;
0f2d19dd 761
aa42c036
LC
762 case 'd':
763 answer = scm_make_real (*((double *)&(data[p])));
764 break;
0f2d19dd
JB
765#endif
766
aa42c036
LC
767 case 's':
768 case 'p':
769 answer = SCM_PACK (data[p]);
770 break;
0f2d19dd
JB
771
772
aa42c036
LC
773 default:
774 SCM_MISC_ERROR ("unrecognized field type: ~S",
775 scm_list_1 (SCM_MAKE_CHAR (field_type)));
776 }
0f2d19dd
JB
777 }
778
779 return answer;
780}
1bbd0b84 781#undef FUNC_NAME
0f2d19dd
JB
782
783
a1ec6916 784SCM_DEFINE (scm_struct_set_x, "struct-set!", 3, 0, 0,
1bbd0b84 785 (SCM handle, SCM pos, SCM val),
e3239868
DH
786 "Set the slot of the structure @var{handle} with index @var{pos}\n"
787 "to @var{val}. Signal an error if the slot can not be written\n"
788 "to.")
1bbd0b84 789#define FUNC_NAME s_scm_struct_set_x
0f2d19dd 790{
aa42c036
LC
791 SCM vtable;
792 scm_t_bits *data;
a55c2b68 793 size_t p;
0f2d19dd 794
34d19ef6 795 SCM_VALIDATE_STRUCT (1, handle);
0f2d19dd 796
aa42c036 797 vtable = SCM_STRUCT_VTABLE (handle);
0f2d19dd 798 data = SCM_STRUCT_DATA (handle);
a55c2b68 799 p = scm_to_size_t (pos);
0f2d19dd 800
aa42c036
LC
801 if (SCM_LIKELY (SCM_VTABLE_FLAG_IS_SET (vtable, SCM_VTABLE_FLAG_SIMPLE)
802 && SCM_VTABLE_FLAG_IS_SET (vtable, SCM_VTABLE_FLAG_SIMPLE_RW)
803 && p < SCM_STRUCT_DATA_REF (vtable, scm_vtable_index_size)))
804 /* The fast path: HANDLE is a struct with only "p" fields. */
805 data[p] = SCM_UNPACK (val);
806 else
807 {
808 SCM layout;
809 size_t layout_len, n_fields;
810 scm_t_wchar field_type = 0;
0f2d19dd 811
aa42c036
LC
812 layout = SCM_STRUCT_LAYOUT (handle);
813 layout_len = scm_i_symbol_length (layout);
814 n_fields = layout_len / 2;
0f2d19dd 815
aa42c036
LC
816 if (SCM_LAYOUT_TAILP (scm_i_symbol_ref (layout, layout_len - 1)))
817 n_fields += data[n_fields - 1];
818
819 SCM_ASSERT_RANGE (1, pos, p < n_fields);
820
821 if (p * 2 < layout_len)
822 {
823 char set_x;
824 field_type = scm_i_symbol_ref (layout, p * 2);
825 set_x = scm_i_symbol_ref (layout, p * 2 + 1);
826 if (set_x != 'w' && set_x != 'h')
827 SCM_MISC_ERROR ("set! denied for field ~A", scm_list_1 (pos));
828 }
829 else if (scm_i_symbol_ref (layout, layout_len - 1) == 'W')
830 field_type = scm_i_symbol_ref (layout, layout_len - 2);
831 else
1afff620 832 SCM_MISC_ERROR ("set! denied for field ~A", scm_list_1 (pos));
aa42c036
LC
833
834 switch (field_type)
835 {
836 case 'u':
837 data[p] = SCM_NUM2ULONG (3, val);
838 break;
0f2d19dd
JB
839
840#if 0
aa42c036
LC
841 case 'i':
842 data[p] = SCM_NUM2LONG (3, val);
843 break;
0f2d19dd 844
aa42c036
LC
845 case 'd':
846 *((double *)&(data[p])) = scm_num2dbl (val, (char *)SCM_ARG3);
847 break;
0f2d19dd
JB
848#endif
849
aa42c036
LC
850 case 'p':
851 data[p] = SCM_UNPACK (val);
852 break;
0f2d19dd 853
aa42c036
LC
854 case 's':
855 SCM_MISC_ERROR ("self fields immutable", SCM_EOL);
0f2d19dd 856
aa42c036
LC
857 default:
858 SCM_MISC_ERROR ("unrecognized field type: ~S",
859 scm_list_1 (SCM_MAKE_CHAR (field_type)));
860 }
0f2d19dd
JB
861 }
862
863 return val;
864}
1bbd0b84 865#undef FUNC_NAME
0f2d19dd
JB
866
867
a1ec6916 868SCM_DEFINE (scm_struct_vtable, "struct-vtable", 1, 0, 0,
1bbd0b84 869 (SCM handle),
b380b885 870 "Return the vtable structure that describes the type of @var{struct}.")
1bbd0b84 871#define FUNC_NAME s_scm_struct_vtable
0f2d19dd 872{
34d19ef6 873 SCM_VALIDATE_STRUCT (1, handle);
0f2d19dd
JB
874 return SCM_STRUCT_VTABLE (handle);
875}
1bbd0b84 876#undef FUNC_NAME
0f2d19dd
JB
877
878
a1ec6916 879SCM_DEFINE (scm_struct_vtable_tag, "struct-vtable-tag", 1, 0, 0,
1bbd0b84 880 (SCM handle),
e3239868 881 "Return the vtable tag of the structure @var{handle}.")
1bbd0b84 882#define FUNC_NAME s_scm_struct_vtable_tag
0f2d19dd 883{
34d19ef6 884 SCM_VALIDATE_VTABLE (1, handle);
b9bd8526 885 return scm_from_ulong (((unsigned long)SCM_STRUCT_DATA (handle)) >> 3);
98d5f601 886}
1bbd0b84 887#undef FUNC_NAME
98d5f601
MD
888
889/* {Associating names and classes with vtables}
890 *
891 * The name of a vtable should probably be stored as a slot. This is
892 * a backward compatible solution until agreement has been achieved on
893 * how to associate names with vtables.
894 */
895
c014a02e 896unsigned long
d587c9e8 897scm_struct_ihashq (SCM obj, unsigned long n, void *closure)
98d5f601 898{
ad196599
MD
899 /* The length of the hash table should be a relative prime it's not
900 necessary to shift down the address. */
f1267706 901 return SCM_UNPACK (obj) % n;
98d5f601
MD
902}
903
904SCM
905scm_struct_create_handle (SCM obj)
906{
907 SCM handle = scm_hash_fn_create_handle_x (scm_struct_table,
908 obj,
909 SCM_BOOL_F,
910 scm_struct_ihashq,
d587c9e8 911 (scm_t_assoc_fn) scm_sloppy_assq,
98d5f601 912 0);
7888309b 913 if (scm_is_false (SCM_CDR (handle)))
98d5f601
MD
914 SCM_SETCDR (handle, scm_cons (SCM_BOOL_F, SCM_BOOL_F));
915 return handle;
916}
917
a1ec6916 918SCM_DEFINE (scm_struct_vtable_name, "struct-vtable-name", 1, 0, 0,
1bbd0b84 919 (SCM vtable),
e3239868 920 "Return the name of the vtable @var{vtable}.")
1bbd0b84 921#define FUNC_NAME s_scm_struct_vtable_name
98d5f601 922{
34d19ef6 923 SCM_VALIDATE_VTABLE (1, vtable);
98d5f601
MD
924 return SCM_STRUCT_TABLE_NAME (SCM_CDR (scm_struct_create_handle (vtable)));
925}
1bbd0b84 926#undef FUNC_NAME
98d5f601 927
a1ec6916 928SCM_DEFINE (scm_set_struct_vtable_name_x, "set-struct-vtable-name!", 2, 0, 0,
1bbd0b84 929 (SCM vtable, SCM name),
e3239868 930 "Set the name of the vtable @var{vtable} to @var{name}.")
1bbd0b84 931#define FUNC_NAME s_scm_set_struct_vtable_name_x
98d5f601 932{
34d19ef6
HWN
933 SCM_VALIDATE_VTABLE (1, vtable);
934 SCM_VALIDATE_SYMBOL (2, name);
98d5f601
MD
935 SCM_SET_STRUCT_TABLE_NAME (SCM_CDR (scm_struct_create_handle (vtable)),
936 name);
937 return SCM_UNSPECIFIED;
0f2d19dd 938}
1bbd0b84 939#undef FUNC_NAME
0f2d19dd
JB
940
941
942\f
943
bafcafb2 944void
1bbd0b84 945scm_print_struct (SCM exp, SCM port, scm_print_state *pstate)
bafcafb2 946{
7888309b 947 if (scm_is_true (scm_procedure_p (SCM_STRUCT_PRINTER (exp))))
4bfdf158
MD
948 scm_printer_apply (SCM_STRUCT_PRINTER (exp), exp, port, pstate);
949 else
bafcafb2 950 {
a1ae1799
MD
951 SCM vtable = SCM_STRUCT_VTABLE (exp);
952 SCM name = scm_struct_vtable_name (vtable);
953 scm_puts ("#<", port);
7888309b 954 if (scm_is_true (name))
b6cf4d02
AW
955 {
956 scm_display (name, port);
957 scm_putc (' ', port);
958 }
a1ae1799 959 else
b6cf4d02
AW
960 {
961 if (SCM_VTABLE_FLAG_IS_SET (vtable, SCM_VTABLE_FLAG_VTABLE))
962 scm_puts ("vtable:", port);
963 else
964 scm_puts ("struct:", port);
965 scm_uintprint (SCM_UNPACK (vtable), 16, port);
966 scm_putc (' ', port);
967 scm_write (SCM_VTABLE_LAYOUT (vtable), port);
968 scm_putc (' ', port);
969 }
0345e278 970 scm_uintprint (SCM_UNPACK (exp), 16, port);
b6cf4d02
AW
971 /* hackety hack */
972 if (SCM_STRUCT_APPLICABLE_P (exp))
973 {
974 if (scm_is_true (SCM_STRUCT_PROCEDURE (exp)))
975 {
976 scm_puts (" proc: ", port);
977 if (scm_is_true (scm_procedure_p (SCM_STRUCT_PROCEDURE (exp))))
978 scm_write (SCM_STRUCT_PROCEDURE (exp), port);
979 else
980 scm_puts ("(not a procedure?)", port);
981 }
982 if (SCM_STRUCT_SETTER_P (exp))
983 {
984 scm_puts (" setter: ", port);
985 scm_write (SCM_STRUCT_SETTER (exp), port);
986 }
987 }
b7f3516f 988 scm_putc ('>', port);
bafcafb2 989 }
bafcafb2 990}
1cc91f1b 991
0f2d19dd
JB
992void
993scm_init_struct ()
0f2d19dd 994{
b6cf4d02
AW
995 GC_REGISTER_DISPLACEMENT (2*sizeof(scm_t_bits)); /* for the self data pointer */
996 GC_REGISTER_DISPLACEMENT (2*sizeof(scm_t_bits)
997 + scm_tc3_struct); /* for the vtable data pointer */
998
999 scm_struct_table = scm_make_weak_key_hash_table (scm_from_int (31));
1000 required_vtable_fields = scm_from_locale_string (SCM_VTABLE_BASE_LAYOUT);
1001 required_applicable_fields = scm_from_locale_string (SCM_APPLICABLE_BASE_LAYOUT);
1002 required_applicable_with_setter_fields = scm_from_locale_string (SCM_APPLICABLE_WITH_SETTER_BASE_LAYOUT);
651f2cd2 1003
db5ed685 1004 scm_standard_vtable_vtable =
b6cf4d02
AW
1005 scm_make_vtable_vtable (scm_nullstr, SCM_INUM0, SCM_EOL);
1006
1007 scm_applicable_struct_vtable_vtable =
db5ed685 1008 scm_make_struct (scm_standard_vtable_vtable, SCM_INUM0,
b6cf4d02
AW
1009 scm_list_1 (scm_make_struct_layout (required_vtable_fields)));
1010 SCM_SET_VTABLE_FLAGS (scm_applicable_struct_vtable_vtable,
1011 SCM_VTABLE_FLAG_APPLICABLE_VTABLE);
1012 scm_c_define ("<applicable-struct-vtable>", scm_applicable_struct_vtable_vtable);
1013
1014 scm_applicable_struct_with_setter_vtable_vtable =
db5ed685 1015 scm_make_struct (scm_standard_vtable_vtable, SCM_INUM0,
b6cf4d02
AW
1016 scm_list_1 (scm_make_struct_layout (required_vtable_fields)));
1017 SCM_SET_VTABLE_FLAGS (scm_applicable_struct_with_setter_vtable_vtable,
1018 SCM_VTABLE_FLAG_APPLICABLE_VTABLE | SCM_VTABLE_FLAG_SETTER_VTABLE);
1019 scm_c_define ("<applicable-struct-with-setter-vtable>", scm_applicable_struct_with_setter_vtable_vtable);
651f2cd2 1020
e11e83f3 1021 scm_c_define ("vtable-index-layout", scm_from_int (scm_vtable_index_layout));
86d31dfe 1022 scm_c_define ("vtable-index-printer",
b6cf4d02 1023 scm_from_int (scm_vtable_index_instance_printer));
e11e83f3 1024 scm_c_define ("vtable-offset-user", scm_from_int (scm_vtable_offset_user));
a0599745 1025#include "libguile/struct.x"
0f2d19dd 1026}
89e00824
ML
1027
1028/*
1029 Local Variables:
1030 c-file-style: "gnu"
1031 End:
1032*/