diff --git a/libguile/struct.c b/libguile/struct.c index 321f2f199..adebe6f9f 100644 --- a/libguile/struct.c +++ b/libguile/struct.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1996,1997,1998,1999,2000,2001, 2003, 2004, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. +/* Copyright (C) 1996,1997,1998,1999,2000,2001, 2003, 2004, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License @@ -22,6 +22,7 @@ #endif #include +#include #include "libguile/_scm.h" #include "libguile/async.h" @@ -151,6 +152,61 @@ SCM_DEFINE (scm_make_struct_layout, "make-struct-layout", 1, 0, 0, #undef FUNC_NAME +/* Check whether VTABLE instances have a simple layout (i.e., either only "pr" + or only "pw" fields) and update its flags accordingly. */ +static void +set_vtable_layout_flags (SCM vtable) +{ + size_t len, field; + SCM layout; + const char *c_layout; + scm_t_bits flags = SCM_VTABLE_FLAG_SIMPLE; + + layout = SCM_VTABLE_LAYOUT (vtable); + c_layout = scm_i_symbol_chars (layout); + len = scm_i_symbol_length (layout); + + assert (len % 2 == 0); + + /* Update FLAGS according to LAYOUT. */ + for (field = 0; + field < len && flags & SCM_VTABLE_FLAG_SIMPLE; + field += 2) + { + if (c_layout[field] != 'p') + flags = 0; + else + switch (c_layout[field + 1]) + { + case 'w': + case 'W': + if (!(flags & SCM_VTABLE_FLAG_SIMPLE_RW) && field > 0) + /* There's a mixture of `w' and `r' flags. */ + flags = 0; + else + flags |= SCM_VTABLE_FLAG_SIMPLE_RW; + break; + + case 'r': + case 'R': + if (flags & SCM_VTABLE_FLAG_SIMPLE_RW) + /* There's a mixture of `w' and `r' flags. */ + flags = 0; + break; + + default: + flags = 0; + } + } + + if (flags & SCM_VTABLE_FLAG_SIMPLE) + { + /* VTABLE is simple so update its flags and record the size of its + instances. */ + SCM_SET_VTABLE_FLAGS (vtable, flags); + SCM_STRUCT_DATA_SET (vtable, scm_vtable_index_size, len / 2); + } +} void scm_i_struct_inherit_vtable_magic (SCM vtable, SCM obj) @@ -171,6 +227,8 @@ scm_i_struct_inherit_vtable_magic (SCM vtable, SCM obj) scm_misc_error (FUNC_NAME, "invalid layout for new vtable", scm_list_1 (SCM_VTABLE_LAYOUT (obj))); + set_vtable_layout_flags (obj); + /* if obj's vtable is compatible with the required vtable (class) layout, it is a metaclass */ olayout = scm_symbol_to_string (SCM_VTABLE_LAYOUT (obj)); @@ -215,60 +273,74 @@ static void scm_struct_init (SCM handle, SCM layout, size_t n_tail, size_t n_inits, scm_t_bits *inits) { - scm_t_wchar prot = 0; - int n_fields = scm_i_symbol_length (layout) / 2; - int tailp = 0; - int i; - size_t inits_idx = 0; - scm_t_bits *mem = SCM_STRUCT_DATA (handle); + SCM vtable; + scm_t_bits *mem; - i = -2; - while (n_fields) + vtable = SCM_STRUCT_VTABLE (handle); + mem = SCM_STRUCT_DATA (handle); + + if (SCM_UNPACK (vtable) != 0 + && SCM_VTABLE_FLAG_IS_SET (vtable, SCM_VTABLE_FLAG_SIMPLE) + && n_tail == 0 + && n_inits == SCM_STRUCT_DATA_REF (vtable, scm_vtable_index_size)) + /* The fast path: HANDLE has N_INITS "p" fields. */ + memcpy (mem, inits, n_inits * sizeof (SCM)); + else { - if (!tailp) + scm_t_wchar prot = 0; + int n_fields = scm_i_symbol_length (layout) / 2; + int tailp = 0; + int i; + size_t inits_idx = 0; + + i = -2; + while (n_fields) { - i += 2; - prot = scm_i_symbol_ref (layout, i+1); - if (SCM_LAYOUT_TAILP (prot)) + if (!tailp) { - tailp = 1; - prot = prot == 'R' ? 'r' : prot == 'W' ? 'w' : 'o'; - *mem++ = (scm_t_bits)n_tail; - n_fields += n_tail - 1; - if (n_fields == 0) - break; + i += 2; + prot = scm_i_symbol_ref (layout, i+1); + if (SCM_LAYOUT_TAILP (prot)) + { + tailp = 1; + prot = prot == 'R' ? 'r' : prot == 'W' ? 'w' : 'o'; + *mem++ = (scm_t_bits)n_tail; + n_fields += n_tail - 1; + if (n_fields == 0) + break; + } } + switch (scm_i_symbol_ref (layout, i)) + { + case 'u': + if ((prot != 'r' && prot != 'w') || inits_idx == n_inits) + *mem = 0; + else + { + *mem = scm_to_ulong (SCM_PACK (inits[inits_idx])); + inits_idx++; + } + break; + + case 'p': + if ((prot != 'r' && prot != 'w') || inits_idx == n_inits) + *mem = SCM_UNPACK (SCM_BOOL_F); + else + { + *mem = inits[inits_idx]; + inits_idx++; + } + + break; + + case 's': + *mem = SCM_UNPACK (handle); + break; + } + + n_fields--; + mem++; } - switch (scm_i_symbol_ref (layout, i)) - { - case 'u': - if ((prot != 'r' && prot != 'w') || inits_idx == n_inits) - *mem = 0; - else - { - *mem = scm_to_ulong (SCM_PACK (inits[inits_idx])); - inits_idx++; - } - break; - - case 'p': - if ((prot != 'r' && prot != 'w') || inits_idx == n_inits) - *mem = SCM_UNPACK (SCM_BOOL_F); - else - { - *mem = inits[inits_idx]; - inits_idx++; - } - - break; - - case 's': - *mem = SCM_UNPACK (handle); - break; - } - - n_fields--; - mem++; } } @@ -627,71 +699,81 @@ SCM_DEFINE (scm_struct_ref, "struct-ref", 2, 0, 0, "integer value small enough to fit in one machine word.") #define FUNC_NAME s_scm_struct_ref { - SCM answer = SCM_UNDEFINED; - scm_t_bits * data; - SCM layout; - size_t layout_len; + SCM vtable, answer = SCM_UNDEFINED; + scm_t_bits *data; size_t p; - scm_t_bits n_fields; - scm_t_wchar field_type = 0; - SCM_VALIDATE_STRUCT (1, handle); - layout = SCM_STRUCT_LAYOUT (handle); + vtable = SCM_STRUCT_VTABLE (handle); data = SCM_STRUCT_DATA (handle); p = scm_to_size_t (pos); - layout_len = scm_i_symbol_length (layout); - n_fields = layout_len / 2; - if (SCM_LAYOUT_TAILP (scm_i_symbol_ref (layout, layout_len - 1))) - n_fields += data[n_fields - 1]; - - SCM_ASSERT_RANGE(1, pos, p < n_fields); - - if (p * 2 < layout_len) + if (SCM_LIKELY (SCM_VTABLE_FLAG_IS_SET (vtable, SCM_VTABLE_FLAG_SIMPLE) + && p < SCM_STRUCT_DATA_REF (vtable, scm_vtable_index_size))) { - scm_t_wchar ref; - field_type = scm_i_symbol_ref (layout, p * 2); - ref = scm_i_symbol_ref (layout, p * 2 + 1); - if ((ref != 'r') && (ref != 'w') && (ref != 'h')) - { - if ((ref == 'R') || (ref == 'W')) - field_type = 'u'; - else - SCM_MISC_ERROR ("ref denied for field ~A", scm_list_1 (pos)); - } + /* The fast path: HANDLE is a struct with only "p" fields. */ + answer = SCM_PACK (data[p]); } - else if (scm_i_symbol_ref (layout, layout_len - 1) != 'O') - field_type = scm_i_symbol_ref(layout, layout_len - 2); else - SCM_MISC_ERROR ("ref denied for field ~A", scm_list_1 (pos)); - - switch (field_type) { - case 'u': - answer = scm_from_ulong (data[p]); - break; + SCM layout; + size_t layout_len, n_fields; + scm_t_wchar field_type = 0; + + layout = SCM_STRUCT_LAYOUT (handle); + layout_len = scm_i_symbol_length (layout); + n_fields = layout_len / 2; + + if (SCM_LAYOUT_TAILP (scm_i_symbol_ref (layout, layout_len - 1))) + n_fields += data[n_fields - 1]; + + SCM_ASSERT_RANGE (1, pos, p < n_fields); + + if (p * 2 < layout_len) + { + scm_t_wchar ref; + field_type = scm_i_symbol_ref (layout, p * 2); + ref = scm_i_symbol_ref (layout, p * 2 + 1); + if ((ref != 'r') && (ref != 'w') && (ref != 'h')) + { + if ((ref == 'R') || (ref == 'W')) + field_type = 'u'; + else + SCM_MISC_ERROR ("ref denied for field ~A", scm_list_1 (pos)); + } + } + else if (scm_i_symbol_ref (layout, layout_len - 1) != 'O') + field_type = scm_i_symbol_ref(layout, layout_len - 2); + else + SCM_MISC_ERROR ("ref denied for field ~A", scm_list_1 (pos)); + + switch (field_type) + { + case 'u': + answer = scm_from_ulong (data[p]); + break; #if 0 - case 'i': - answer = scm_from_long (data[p]); - break; + case 'i': + answer = scm_from_long (data[p]); + break; - case 'd': - answer = scm_make_real (*((double *)&(data[p]))); - break; + case 'd': + answer = scm_make_real (*((double *)&(data[p]))); + break; #endif - case 's': - case 'p': - answer = SCM_PACK (data[p]); - break; + case 's': + case 'p': + answer = SCM_PACK (data[p]); + break; - default: - SCM_MISC_ERROR ("unrecognized field type: ~S", - scm_list_1 (SCM_MAKE_CHAR (field_type))); + default: + SCM_MISC_ERROR ("unrecognized field type: ~S", + scm_list_1 (SCM_MAKE_CHAR (field_type))); + } } return answer; @@ -706,65 +788,76 @@ SCM_DEFINE (scm_struct_set_x, "struct-set!", 3, 0, 0, "to.") #define FUNC_NAME s_scm_struct_set_x { - scm_t_bits * data; - SCM layout; - size_t layout_len; + SCM vtable; + scm_t_bits *data; size_t p; - int n_fields; - scm_t_wchar field_type = 0; SCM_VALIDATE_STRUCT (1, handle); - layout = SCM_STRUCT_LAYOUT (handle); + vtable = SCM_STRUCT_VTABLE (handle); data = SCM_STRUCT_DATA (handle); p = scm_to_size_t (pos); - layout_len = scm_i_symbol_length (layout); - n_fields = layout_len / 2; - if (SCM_LAYOUT_TAILP (scm_i_symbol_ref (layout, layout_len - 1))) - n_fields += data[n_fields - 1]; - - SCM_ASSERT_RANGE (1, pos, p < n_fields); - - if (p * 2 < layout_len) - { - char set_x; - field_type = scm_i_symbol_ref (layout, p * 2); - set_x = scm_i_symbol_ref (layout, p * 2 + 1); - if (set_x != 'w' && set_x != 'h') - SCM_MISC_ERROR ("set! denied for field ~A", scm_list_1 (pos)); - } - else if (scm_i_symbol_ref (layout, layout_len - 1) == 'W') - field_type = scm_i_symbol_ref (layout, layout_len - 2); + if (SCM_LIKELY (SCM_VTABLE_FLAG_IS_SET (vtable, SCM_VTABLE_FLAG_SIMPLE) + && SCM_VTABLE_FLAG_IS_SET (vtable, SCM_VTABLE_FLAG_SIMPLE_RW) + && p < SCM_STRUCT_DATA_REF (vtable, scm_vtable_index_size))) + /* The fast path: HANDLE is a struct with only "p" fields. */ + data[p] = SCM_UNPACK (val); else - SCM_MISC_ERROR ("set! denied for field ~A", scm_list_1 (pos)); - - switch (field_type) { - case 'u': - data[p] = SCM_NUM2ULONG (3, val); - break; + SCM layout; + size_t layout_len, n_fields; + scm_t_wchar field_type = 0; + + layout = SCM_STRUCT_LAYOUT (handle); + layout_len = scm_i_symbol_length (layout); + n_fields = layout_len / 2; + + if (SCM_LAYOUT_TAILP (scm_i_symbol_ref (layout, layout_len - 1))) + n_fields += data[n_fields - 1]; + + SCM_ASSERT_RANGE (1, pos, p < n_fields); + + if (p * 2 < layout_len) + { + char set_x; + field_type = scm_i_symbol_ref (layout, p * 2); + set_x = scm_i_symbol_ref (layout, p * 2 + 1); + if (set_x != 'w' && set_x != 'h') + SCM_MISC_ERROR ("set! denied for field ~A", scm_list_1 (pos)); + } + else if (scm_i_symbol_ref (layout, layout_len - 1) == 'W') + field_type = scm_i_symbol_ref (layout, layout_len - 2); + else + SCM_MISC_ERROR ("set! denied for field ~A", scm_list_1 (pos)); + + switch (field_type) + { + case 'u': + data[p] = SCM_NUM2ULONG (3, val); + break; #if 0 - case 'i': - data[p] = SCM_NUM2LONG (3, val); - break; + case 'i': + data[p] = SCM_NUM2LONG (3, val); + break; - case 'd': - *((double *)&(data[p])) = scm_num2dbl (val, (char *)SCM_ARG3); - break; + case 'd': + *((double *)&(data[p])) = scm_num2dbl (val, (char *)SCM_ARG3); + break; #endif - case 'p': - data[p] = SCM_UNPACK (val); - break; + case 'p': + data[p] = SCM_UNPACK (val); + break; - case 's': - SCM_MISC_ERROR ("self fields immutable", SCM_EOL); + case 's': + SCM_MISC_ERROR ("self fields immutable", SCM_EOL); - default: - SCM_MISC_ERROR ("unrecognized field type: ~S", - scm_list_1 (SCM_MAKE_CHAR (field_type))); + default: + SCM_MISC_ERROR ("unrecognized field type: ~S", + scm_list_1 (SCM_MAKE_CHAR (field_type))); + } } return val; diff --git a/libguile/struct.h b/libguile/struct.h index 537ef90ed..012d9b6e7 100644 --- a/libguile/struct.h +++ b/libguile/struct.h @@ -3,7 +3,7 @@ #ifndef SCM_STRUCT_H #define SCM_STRUCT_H -/* Copyright (C) 1995,1997,1999,2000,2001, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. +/* Copyright (C) 1995,1997,1999,2000,2001, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License @@ -41,12 +41,12 @@ /* All vtables have the following fields. */ #define SCM_VTABLE_BASE_LAYOUT \ "pr" /* layout */ \ - "uh" /* flags */ \ + "uh" /* flags */ \ "sr" /* self */ \ "uh" /* finalizer */ \ "pw" /* printer */ \ "ph" /* name (hidden from make-struct for back-compat reasons) */ \ - "uh" /* reserved */ \ + "uh" /* size */ \ "uh" /* reserved */ #define scm_vtable_index_layout 0 /* A symbol describing the physical arrangement of this type. */ @@ -55,7 +55,7 @@ #define scm_vtable_index_instance_finalize 3 /* Finalizer for instances of this struct type. */ #define scm_vtable_index_instance_printer 4 /* A printer for this struct type. */ #define scm_vtable_index_name 5 /* Name of this vtable. */ -#define scm_vtable_index_reserved_6 6 +#define scm_vtable_index_size 6 /* Number of fields, for simple structs. */ #define scm_vtable_index_reserved_7 7 #define scm_vtable_offset_user 8 /* Where do user fields start in the vtable? */ @@ -79,8 +79,8 @@ #define SCM_VTABLE_FLAG_APPLICABLE (1L << 2) /* instances of this vtable are applicable? */ #define SCM_VTABLE_FLAG_SETTER_VTABLE (1L << 3) /* instances of this vtable are applicable-with-setter vtables? */ #define SCM_VTABLE_FLAG_SETTER (1L << 4) /* instances of this vtable are applicable-with-setters? */ -#define SCM_VTABLE_FLAG_RESERVED_0 (1L << 5) -#define SCM_VTABLE_FLAG_RESERVED_1 (1L << 6) +#define SCM_VTABLE_FLAG_SIMPLE (1L << 5) /* instances of this vtable have only "pr" fields */ +#define SCM_VTABLE_FLAG_SIMPLE_RW (1L << 6) /* instances of this vtable have only "pw" fields */ #define SCM_VTABLE_FLAG_SMOB_0 (1L << 7) #define SCM_VTABLE_FLAG_GOOPS_0 (1L << 8) #define SCM_VTABLE_FLAG_GOOPS_1 (1L << 9) diff --git a/test-suite/tests/structs.test b/test-suite/tests/structs.test index 2c2ca0c9c..55e08075b 100644 --- a/test-suite/tests/structs.test +++ b/test-suite/tests/structs.test @@ -1,7 +1,7 @@ -;;;; structs.test --- Test suite for Guile's structures. -*- Scheme -*- -;;;; Ludovic Courtès , 2006-06-12. +;;;; structs.test --- Structures. -*- mode: scheme; coding: utf-8; -*- +;;;; Ludovic Courtès , 2006-06-12. ;;;; -;;;; Copyright (C) 2006, 2007, 2009 Free Software Foundation, Inc. +;;;; Copyright (C) 2006, 2007, 2009, 2010 Free Software Foundation, Inc. ;;;; ;;;; This library is free software; you can redistribute it and/or ;;;; modify it under the terms of the GNU Lesser General Public @@ -80,9 +80,33 @@ (pass-if "struct-set!" (let ((ball (make-ball green "Bob"))) (set-owner! ball "Bill") - (string=? (owner ball) "Bill")))) + (string=? (owner ball) "Bill"))) + (pass-if "struct-ref" + (let ((ball (make-ball red "Alice"))) + (equal? (struct-ref ball 0) "Alice"))) + (pass-if "struct-set!" + (let* ((v (make-vtable "pw")) + (s (make-struct v 0)) + (r (struct-set! s 0 'a))) + (eq? r + (struct-ref s 0) + 'a))) + + (pass-if-exception "struct-ref out-of-range" + exception:out-of-range + (let* ((v (make-vtable "prpr")) + (s (make-struct v 0 'a 'b))) + (struct-ref s 2))) + + (pass-if-exception "struct-set! out-of-range" + exception:out-of-range + (let* ((v (make-vtable "pwpw")) + (s (make-struct v 0 'a 'b))) + (struct-set! s 2 'c)))) + + (with-test-prefix "equal?" (pass-if "simple structs" @@ -153,8 +177,3 @@ (lambda (port) (display struct port))))) (equal? str "hello"))))) - - -;;; Local Variables: -;;; coding: latin-1 -;;; End: