1 /* Header for reduction machine.
2  */
3 
4 /*
5 
6     Copyright (C) 1991-2003 The National Gallery
7 
8     This program is free software; you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation; either version 2 of the License, or
11     (at your option) any later version.
12 
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17 
18     You should have received a copy of the GNU General Public License along
19     with this program; if not, write to the Free Software Foundation, Inc.,
20     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21 
22  */
23 
24 /*
25 
26     These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
27 
28  */
29 
30 /* Huge :-( this pushes sizeof(Reduce) up to 14MB. But we need to be able to
31  * loop down very long lists, eg. for a 65k x 3 LUT held as a Matrix. Drop
32  * this down when we represent matricies more sensibly.
33  */
34 #define SPINE_SIZE (80000)
35 
36 /* Reduction machine state. Not very opaque ... see mark_reduce()
37  */
38 struct _Reduce {
39 	/* Stack of heap nodes for spine.
40 	 */
41 	HeapNode *nstack[SPINE_SIZE];
42 
43 	/* Index of free element above node stack top.
44 	 */
45 	int sp;
46 
47 	/* Frame stack ... top of fstack is sp we block GET above.
48 	 */
49 	int fstack[SPINE_SIZE];
50 
51 	/* Writeback stack ... where the result of each frame goes.
52 	 */
53 	PElement wbstack[SPINE_SIZE];
54 
55 	/* Frame stack pointer.
56 	 */
57 	int fsp;
58 
59 	/* Heap we evaluate.
60 	 */
61 	Heap *heap;
62 
63 	/* Nested reductions ... need to be able to longjmp() out of stuff,
64 	 * and restore the machine state.
65 	 */
66 	int running;
67 	jmp_buf error[SPINE_SIZE];
68 	int sps[SPINE_SIZE];
69 	int fsps[SPINE_SIZE];
70 	int tsp[SPINE_SIZE];
71 };
72 
73 #define RSPUSH(RC,N) { \
74 	if( (RC)->sp == SPINE_SIZE ) { \
75 		error_top( _( "Stack overflow." ) ); \
76 		error_sub( _( "Spine stack overflow, runaway recursion?" ) ); \
77 		reduce_throw( (RC) ); \
78 	} \
79 	else \
80 		(RC)->nstack[(RC)->sp++]=(N); \
81 }
82 
83 /* Number of items in current frame.
84  */
85 #define RSFRAMESIZE(RC) ((RC)->sp - (RC)->fstack[(RC)->fsp - 1])
86 
87 /* Check for at least N args present.
88  */
89 #define RSCHECKARGS(RC,N) (RSFRAMESIZE(RC) >= (N))
90 
91 /* Frame is empty?
92  */
93 #define RSFRAMEEMPTY(RC) (RSFRAMESIZE(RC) == 0)
94 
95 /* Get offset from stack top, offset 0 is top item.
96  */
97 #define RSGET(RC,N) ((RC)->nstack[(RC)->sp - ((N) + 1)])
98 
99 /* Get the writeback for this frame.
100  */
101 #define RSGETWB(RC) ((RC)->wbstack[(RC)->fsp - 1])
102 
103 #define RSPUSHFRAME(RC,OUT) { \
104 	if( (RC)->fsp == SPINE_SIZE )  { \
105 		error_top( _( "Stack overflow." ) ); \
106 		error_sub( _( "Frame stack overflow, " \
107 			"expression too complex." ) ); \
108 		reduce_throw( (RC) ); \
109 	} \
110 	else { \
111 		(RC)->wbstack[(RC)->fsp] = *out; \
112 		(RC)->fstack[(RC)->fsp] = (RC)->sp; \
113 		(RC)->fsp++; \
114 	} \
115 }
116 
117 #define RSPOPFRAME(RC) { \
118 	if( (RC)->fsp == 0 ) { \
119 		error_top( _( "Stack underflow." ) ); \
120 		error_sub( _( "Frame stack underflow, you've found a bug!" ) ); \
121 		reduce_throw( (RC) ); \
122 	} \
123 	else { \
124 		(RC)->fsp--; \
125 		(RC)->sp = (RC)->fstack[(RC)->fsp]; \
126 	} \
127 }
128 
129 #define RSPOP(RC,N) { \
130 	if( !RSCHECKARGS(RC,N) ) { \
131 		error_top( _( "Stack underflow." ) ); \
132 		error_sub( _( "Spine stack underflow, you've found a bug!" ) ); \
133 		reduce_throw( (RC) ); \
134 	} \
135 	else \
136 		(RC)->sp -= (N); \
137 }
138 
139 /* Pop this code before any calls to reduce_*() to init stuff and catch
140  * errors. Arg is function return value. The missing running decrement is done
141  * by throw().
142  */
143 #define REDUCE_CATCH_START( R ) \
144 { \
145 	rc->sps[rc->running] = rc->sp; \
146 	rc->fsps[rc->running] = rc->fsp; \
147 	rc->tsp[rc->running] = trace_get_mark(); \
148 	if( setjmp( rc->error[rc->running++] ) ) { \
149 		g_assert( rc->running >= 0 ); \
150 		rc->sp = rc->sps[rc->running]; \
151 		rc->fsp = rc->fsps[rc->running]; \
152 		trace_pop_to( rc->tsp[rc->running] ); \
153 		return( (R) ); \
154 	} \
155 }
156 
157 /* After any calls to reduce_*().
158  */
159 #define REDUCE_CATCH_STOP \
160 { \
161 	rc->running -= 1; \
162 	g_assert( rc->running >= 0 ); \
163 }
164 
165 /* Util.
166  */
167 void reduce_throw( Reduce *rc ) __attribute__((noreturn));
168 
169 typedef void *(*reduce_safe_pointer_fn)( Reduce *rc, PElement *,
170 	void *, void *, void *, void * );
171 void *reduce_safe_pointer( Reduce *rc, reduce_safe_pointer_fn fn,
172 	void *a, void *b, void *c, void *d );
173 
174 void reduce_get_list( Reduce *rc, PElement *list );
175 void reduce_error_typecheck( Reduce *rc,
176 	PElement *e, const char *name, const char *type );
177 typedef void *(*reduce_map_list_fn)( Reduce *rc,
178 	PElement *, void *, void * );
179 void *reduce_map_list( Reduce *rc,
180 	PElement *base, reduce_map_list_fn fn, void *a, void *b );
181 typedef void *(*reduce_map_dict_fn)( Reduce *,
182 	const char *, PElement *, void *a, void *b );
183 void *reduce_map_dict( Reduce *rc,
184 	PElement *base, reduce_map_dict_fn fn, void *a, void *b );
185 void reduce_clone_list( Reduce *rc, PElement *base, PElement *out );
186 int reduce_get_string( Reduce *rc, PElement *base, char *buf, int n );
187 int reduce_get_lstring( Reduce *rc, PElement *base, GSList **labels );
188 gboolean reduce_get_bool( Reduce *rc, PElement *base );
189 double reduce_get_real( Reduce *rc, PElement *base );
190 void reduce_get_class( Reduce *rc, PElement *base );
191 Imageinfo *reduce_get_image( Reduce *rc, PElement *base );
192 int reduce_get_realvec( Reduce *rc, PElement *base, double *buf, int n );
193 int reduce_get_imagevec( Reduce *rc, PElement *base, Imageinfo **buf, int n );
194 int reduce_get_matrix( Reduce *rc,
195 	PElement *base, double *buf, int n, int *xsize, int *ysize );
196 void reduce_get_matrix_size( Reduce *rc,
197 	PElement *base, int *xsize, int *ysize );
198 gboolean reduce_is_elist( Reduce *rc, PElement *base );
199 gboolean reduce_is_list( Reduce *rc, PElement *base );
200 gboolean reduce_is_string( Reduce *rc, PElement *base );
201 gboolean reduce_is_finitestring( Reduce *rc, PElement *base );
202 gboolean reduce_is_realvec( Reduce *rc, PElement *base );
203 gboolean reduce_is_imagevec( Reduce *rc, PElement *base );
204 gboolean reduce_is_matrix( Reduce *rc, PElement *base );
205 gboolean reduce_is_class( Reduce *rc, PElement *klass );
206 int reduce_list_length( Reduce *rc, PElement *base );
207 int reduce_list_length_max( Reduce *rc, PElement *base, int max_length );
208 void reduce_list_index( Reduce *rc, PElement *base, int n, PElement *out );
209 gboolean reduce_is_instanceof_exact( Reduce *rc,
210 	const char *name, PElement *instance );
211 gboolean reduce_is_instanceof( Reduce *rc,
212 	const char *name, PElement *instance );
213 
214 /* Main.
215  */
216 extern Reduce *reduce_context;
217 extern int reduce_total_recomputations;
218 
219 void reduce_destroy( Reduce *rc );
220 Reduce *reduce_new( void );
221 gboolean reduce_regenerate( Expr *expr, PElement *out );
222 gboolean reduce_regenerate_member( Expr *expr, PElement *ths, PElement *out );
223 
224 void reduce_spine( Reduce *rc, PElement *out );
225 void reduce_spine_strict( Reduce *rc, PElement *out );
226 
227 gboolean reduce_pelement( Reduce *, ReduceFunction fn, PElement *out );
228 
229 /* Register and unregister values.
230  */
231 void reduce_register( Symbol *sym );
232 void reduce_unregister( Symbol *sym );
233