Commit | Line | Data |
---|---|---|
f8579182 MV |
1 | /* srfi-4.c --- Homogeneous numeric vector datatypes. |
2 | * | |
3 | * Copyright (C) 2001, 2004 Free Software Foundation, Inc. | |
4 | * | |
5 | * This library is free software; you can redistribute it and/or | |
6 | * modify it under the terms of the GNU Lesser General Public | |
7 | * License as published by the Free Software Foundation; either | |
8 | * version 2.1 of the License, or (at your option) any later version. | |
9 | * | |
10 | * This library is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | * Lesser General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU Lesser General Public | |
16 | * License along with this library; if not, write to the Free Software | |
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | */ | |
19 | ||
20 | #include <libguile.h> | |
21 | #include <string.h> | |
22 | #include <stdio.h> | |
23 | ||
24 | #include "libguile/srfi-4.h" | |
25 | #include "libguile/error.h" | |
26 | #include "libguile/read.h" | |
27 | #include "libguile/ports.h" | |
28 | #include "libguile/chars.h" | |
29 | ||
30 | /* Smob type code for homogeneous numeric vectors. */ | |
31 | int scm_tc16_uvec = 0; | |
32 | ||
33 | ||
34 | /* Accessor macros for the three components of a homogeneous numeric | |
35 | vector: | |
36 | - The type tag (one of the symbolic constants below). | |
37 | - The vector's length (counted in elements). | |
38 | - The address of the data area (holding the elements of the | |
39 | vector). */ | |
40 | #define SCM_UVEC_TYPE(u) (SCM_CELL_WORD_1(u)) | |
41 | #define SCM_UVEC_LENGTH(u) ((size_t)SCM_CELL_WORD_2(u)) | |
42 | #define SCM_UVEC_BASE(u) ((void *)SCM_CELL_WORD_3(u)) | |
43 | ||
44 | ||
45 | /* Symbolic constants encoding the various types of homogeneous | |
46 | numeric vectors. */ | |
47 | #define SCM_UVEC_U8 0 | |
48 | #define SCM_UVEC_S8 1 | |
49 | #define SCM_UVEC_U16 2 | |
50 | #define SCM_UVEC_S16 3 | |
51 | #define SCM_UVEC_U32 4 | |
52 | #define SCM_UVEC_S32 5 | |
53 | #define SCM_UVEC_U64 6 | |
54 | #define SCM_UVEC_S64 7 | |
55 | #define SCM_UVEC_F32 8 | |
56 | #define SCM_UVEC_F64 9 | |
57 | ||
58 | ||
59 | /* This array maps type tags to the size of the elements. */ | |
60 | static const int uvec_sizes[10] = { | |
61 | 1, 1, | |
62 | 2, 2, | |
63 | 4, 4, | |
64 | 8, 8, | |
65 | sizeof(float), sizeof(double) | |
66 | }; | |
67 | ||
68 | static const char *uvec_names[10] = { | |
69 | "u8vector", "s8vector", | |
70 | "u16vector", "s16vector", | |
71 | "u32vector", "s32vector", | |
72 | "u64vector", "s64vector", | |
73 | "f32vector", "f64vector" | |
74 | }; | |
75 | ||
76 | /* ================================================================ */ | |
77 | /* SMOB procedures. */ | |
78 | /* ================================================================ */ | |
79 | ||
80 | ||
81 | /* Smob print hook for homogeneous vectors. */ | |
82 | static int | |
83 | uvec_print (SCM uvec, SCM port, scm_print_state *pstate) | |
84 | { | |
85 | union { | |
86 | scm_t_uint8 *u8; | |
87 | scm_t_int8 *s8; | |
88 | scm_t_uint16 *u16; | |
89 | scm_t_int16 *s16; | |
90 | scm_t_uint32 *u32; | |
91 | scm_t_int32 *s32; | |
92 | #if SCM_HAVE_T_INT64 | |
93 | scm_t_uint64 *u64; | |
94 | scm_t_int64 *s64; | |
95 | #endif | |
96 | float *f32; | |
97 | double *f64; | |
98 | } np; | |
99 | ||
100 | size_t i = 0; | |
101 | const size_t uvlen = SCM_UVEC_LENGTH (uvec); | |
102 | char *tagstr; | |
103 | void *uptr = SCM_UVEC_BASE (uvec); | |
104 | ||
105 | switch (SCM_UVEC_TYPE (uvec)) | |
106 | { | |
107 | case SCM_UVEC_U8: tagstr = "u8"; np.u8 = (scm_t_uint8 *) uptr; break; | |
108 | case SCM_UVEC_S8: tagstr = "s8"; np.s8 = (scm_t_int8 *) uptr; break; | |
109 | case SCM_UVEC_U16: tagstr = "u16"; np.u16 = (scm_t_uint16 *) uptr; break; | |
110 | case SCM_UVEC_S16: tagstr = "s16"; np.s16 = (scm_t_int16 *) uptr; break; | |
111 | case SCM_UVEC_U32: tagstr = "u32"; np.u32 = (scm_t_uint32 *) uptr; break; | |
112 | case SCM_UVEC_S32: tagstr = "s32"; np.s32 = (scm_t_int32 *) uptr; break; | |
113 | #if SCM_HAVE_T_INT64 | |
114 | case SCM_UVEC_U64: tagstr = "u64"; np.u64 = (scm_t_uint64 *) uptr; break; | |
115 | case SCM_UVEC_S64: tagstr = "s64"; np.s64 = (scm_t_int64 *) uptr; break; | |
116 | #endif | |
117 | case SCM_UVEC_F32: tagstr = "f32"; np.f32 = (float *) uptr; break; | |
118 | case SCM_UVEC_F64: tagstr = "f64"; np.f64 = (double *) uptr; break; | |
119 | default: | |
120 | abort (); /* Sanity check. */ | |
121 | break; | |
122 | } | |
123 | ||
124 | scm_putc ('#', port); | |
125 | scm_puts (tagstr, port); | |
126 | scm_putc ('(', port); | |
127 | ||
128 | while (i < uvlen) | |
129 | { | |
130 | if (i != 0) scm_puts (" ", port); | |
131 | switch (SCM_UVEC_TYPE (uvec)) | |
132 | { | |
133 | case SCM_UVEC_U8: scm_uintprint (*np.u8, 10, port); np.u8++; break; | |
134 | case SCM_UVEC_S8: scm_intprint (*np.s8, 10, port); np.s8++; break; | |
135 | case SCM_UVEC_U16: scm_uintprint (*np.u16, 10, port); np.u16++; break; | |
136 | case SCM_UVEC_S16: scm_intprint (*np.s16, 10, port); np.s16++; break; | |
137 | case SCM_UVEC_U32: scm_uintprint (*np.u32, 10, port); np.u32++; break; | |
138 | case SCM_UVEC_S32: scm_intprint (*np.s32, 10, port); np.s32++; break; | |
139 | #if SCM_HAVE_T_INT64 | |
140 | case SCM_UVEC_U64: scm_uintprint (*np.u64, 10, port); np.u64++; break; | |
141 | case SCM_UVEC_S64: scm_intprint (*np.s64, 10, port); np.s64++; break; | |
142 | #endif | |
143 | case SCM_UVEC_F32: scm_i_print_double (*np.f32, port); np.f32++; break; | |
144 | case SCM_UVEC_F64: scm_i_print_double (*np.f64, port); np.f64++; break; | |
145 | default: | |
146 | abort (); /* Sanity check. */ | |
147 | break; | |
148 | } | |
149 | i++; | |
150 | } | |
151 | scm_remember_upto_here_1 (uvec); | |
152 | scm_puts (")", port); | |
153 | return 1; | |
154 | } | |
155 | ||
156 | static SCM | |
157 | uvec_equalp (SCM a, SCM b) | |
158 | { | |
159 | SCM result = SCM_BOOL_T; | |
160 | if (SCM_UVEC_TYPE (a) != SCM_UVEC_TYPE (b)) | |
161 | result = SCM_BOOL_F; | |
162 | else if (SCM_UVEC_LENGTH (a) != SCM_UVEC_LENGTH (b)) | |
163 | result = SCM_BOOL_F; | |
164 | else if (memcmp (SCM_UVEC_BASE (a), SCM_UVEC_BASE (b), | |
165 | SCM_UVEC_LENGTH (a) * uvec_sizes[SCM_UVEC_TYPE(a)]) != 0) | |
166 | result = SCM_BOOL_F; | |
167 | ||
168 | scm_remember_upto_here_2 (a, b); | |
169 | return result; | |
170 | } | |
171 | ||
172 | /* Smob free hook for homogeneous numeric vectors. */ | |
173 | static size_t | |
174 | uvec_free (SCM uvec) | |
175 | { | |
176 | int type = SCM_UVEC_TYPE (uvec); | |
177 | scm_gc_free (SCM_UVEC_BASE (uvec), | |
178 | SCM_UVEC_LENGTH (uvec) * uvec_sizes[type], | |
179 | uvec_names[type]); | |
180 | return 0; | |
181 | } | |
182 | ||
183 | /* ================================================================ */ | |
184 | /* Utility procedures. */ | |
185 | /* ================================================================ */ | |
186 | ||
187 | static SCM_C_INLINE int | |
188 | is_uvec (int type, SCM obj) | |
189 | { | |
190 | return (SCM_SMOB_PREDICATE (scm_tc16_uvec, obj) | |
191 | && SCM_UVEC_TYPE (obj) == type); | |
192 | } | |
193 | ||
194 | static SCM_C_INLINE SCM | |
195 | uvec_p (int type, SCM obj) | |
196 | { | |
197 | return scm_from_bool (is_uvec (type, obj)); | |
198 | } | |
199 | ||
200 | static SCM_C_INLINE void | |
201 | uvec_assert (int type, SCM obj) | |
202 | { | |
203 | if (!is_uvec (type, obj)) | |
204 | scm_wrong_type_arg_msg (NULL, 0, obj, uvec_names[type]); | |
205 | } | |
206 | ||
207 | /* Create a new, uninitialized homogeneous numeric vector of type TYPE | |
208 | with space for LEN elements. */ | |
209 | static SCM | |
210 | alloc_uvec (int type, size_t c_len) | |
211 | { | |
212 | void *base = scm_gc_malloc (c_len * uvec_sizes[type], uvec_names[type]); | |
213 | SCM_RETURN_NEWSMOB3 (scm_tc16_uvec, type, c_len, (scm_t_bits) base); | |
214 | } | |
215 | ||
216 | /* GCC doesn't seem to want to optimize unused switch clauses away, | |
217 | so we use a big 'if' in the next two functions. | |
218 | */ | |
219 | ||
220 | static SCM_C_INLINE SCM | |
221 | uvec_fast_ref (int type, void *base, size_t c_idx) | |
222 | { | |
223 | if (type == SCM_UVEC_U8) | |
224 | return scm_from_uint8 (((scm_t_uint8*)base)[c_idx]); | |
225 | else if (type == SCM_UVEC_S8) | |
226 | return scm_from_int8 (((scm_t_int8*)base)[c_idx]); | |
227 | else if (type == SCM_UVEC_U16) | |
228 | return scm_from_uint16 (((scm_t_uint16*)base)[c_idx]); | |
229 | else if (type == SCM_UVEC_S16) | |
230 | return scm_from_int16 (((scm_t_int16*)base)[c_idx]); | |
231 | else if (type == SCM_UVEC_U32) | |
232 | return scm_from_uint32 (((scm_t_uint32*)base)[c_idx]); | |
233 | else if (type == SCM_UVEC_S32) | |
234 | return scm_from_int32 (((scm_t_int32*)base)[c_idx]); | |
235 | #if SCM_HAVE_T_INT64 | |
236 | else if (type == SCM_UVEC_U64) | |
237 | return scm_from_uint64 (((scm_t_uint64*)base)[c_idx]); | |
238 | else if (type == SCM_UVEC_S64) | |
239 | return scm_from_int64 (((scm_t_int64*)base)[c_idx]); | |
240 | #endif | |
241 | else if (type == SCM_UVEC_F32) | |
242 | return scm_from_double (((float*)base)[c_idx]); | |
243 | else if (type == SCM_UVEC_F64) | |
244 | return scm_from_double (((double*)base)[c_idx]); | |
245 | } | |
246 | ||
247 | static SCM_C_INLINE void | |
248 | uvec_fast_set_x (int type, void *base, size_t c_idx, SCM val) | |
249 | { | |
250 | if (type == SCM_UVEC_U8) | |
251 | (((scm_t_uint8*)base)[c_idx]) = scm_to_uint8 (val); | |
252 | else if (type == SCM_UVEC_S8) | |
253 | (((scm_t_int8*)base)[c_idx]) = scm_to_int8 (val); | |
254 | else if (type == SCM_UVEC_U16) | |
255 | (((scm_t_uint16*)base)[c_idx]) = scm_to_uint16 (val); | |
256 | else if (type == SCM_UVEC_S16) | |
257 | (((scm_t_int16*)base)[c_idx]) = scm_to_int16 (val); | |
258 | else if (type == SCM_UVEC_U32) | |
259 | (((scm_t_uint32*)base)[c_idx]) = scm_to_uint32 (val); | |
260 | else if (type == SCM_UVEC_S32) | |
261 | (((scm_t_int32*)base)[c_idx]) = scm_to_int32 (val); | |
262 | #if SCM_HAVE_T_INT64 | |
263 | else if (type == SCM_UVEC_U64) | |
264 | (((scm_t_uint64*)base)[c_idx]) = scm_to_uint64 (val); | |
265 | else if (type == SCM_UVEC_S64) | |
266 | (((scm_t_int64*)base)[c_idx]) = scm_to_int64 (val); | |
267 | #endif | |
268 | else if (type == SCM_UVEC_F32) | |
269 | (((float*)base)[c_idx]) = scm_to_double (val); | |
270 | else if (type == SCM_UVEC_F64) | |
271 | (((double*)base)[c_idx]) = scm_to_double (val); | |
272 | } | |
273 | ||
274 | static SCM_C_INLINE SCM | |
275 | make_uvec (int type, SCM len, SCM fill) | |
276 | { | |
277 | size_t c_len = scm_to_unsigned_integer (len, 0, SIZE_MAX / uvec_sizes[type]); | |
278 | SCM uvec = alloc_uvec (type, c_len); | |
279 | if (!SCM_UNBNDP (fill)) | |
280 | { | |
281 | size_t idx; | |
282 | void *base = SCM_UVEC_BASE (uvec); | |
283 | for (idx = 0; idx < c_len; idx++) | |
284 | uvec_fast_set_x (type, base, idx, fill); | |
285 | } | |
286 | return uvec; | |
287 | } | |
288 | ||
289 | static SCM_C_INLINE SCM | |
290 | uvec_length (int type, SCM uvec) | |
291 | { | |
292 | uvec_assert (type, uvec); | |
293 | return scm_from_size_t (SCM_UVEC_LENGTH (uvec)); | |
294 | } | |
295 | ||
296 | static SCM_C_INLINE SCM | |
297 | uvec_ref (int type, SCM uvec, SCM idx) | |
298 | { | |
299 | size_t c_idx; | |
300 | SCM res; | |
301 | ||
302 | uvec_assert (type, uvec); | |
303 | c_idx = scm_to_unsigned_integer (idx, 0, SCM_UVEC_LENGTH (uvec)-1); | |
304 | res = uvec_fast_ref (type, SCM_UVEC_BASE(uvec), c_idx); | |
305 | scm_remember_upto_here_1 (uvec); | |
306 | return res; | |
307 | } | |
308 | ||
309 | static SCM_C_INLINE SCM | |
310 | uvec_set_x (int type, SCM uvec, SCM idx, SCM val) | |
311 | { | |
312 | size_t c_idx; | |
313 | ||
314 | uvec_assert (type, uvec); | |
315 | c_idx = scm_to_unsigned_integer (idx, 0, SCM_UVEC_LENGTH (uvec)-1); | |
316 | uvec_fast_set_x (type, SCM_UVEC_BASE(uvec), c_idx, val); | |
317 | scm_remember_upto_here_1 (uvec); | |
318 | return SCM_UNSPECIFIED; | |
319 | } | |
320 | ||
321 | static SCM_C_INLINE SCM | |
322 | uvec_to_list (int type, SCM uvec) | |
323 | { | |
324 | size_t c_idx; | |
325 | void *base; | |
326 | SCM res = SCM_EOL; | |
327 | ||
328 | uvec_assert (type, uvec); | |
329 | c_idx = SCM_UVEC_LENGTH (uvec); | |
330 | base = SCM_UVEC_BASE (uvec); | |
331 | while (c_idx-- > 0) | |
332 | res = scm_cons (uvec_fast_ref (type, base, c_idx), res); | |
333 | scm_remember_upto_here_1 (uvec); | |
334 | return res; | |
335 | } | |
336 | ||
337 | static SCM_C_INLINE SCM | |
338 | list_to_uvec (int type, SCM list) | |
339 | { | |
340 | SCM uvec; | |
341 | void *base; | |
342 | long idx; | |
343 | long len = scm_ilength (list); | |
344 | if (len < 0) | |
345 | scm_wrong_type_arg_msg (NULL, 0, list, "proper list"); | |
346 | ||
347 | uvec = alloc_uvec (type, len); | |
348 | base = SCM_UVEC_BASE (uvec); | |
349 | idx = 0; | |
350 | while (scm_is_pair (list) && idx < len) | |
351 | { | |
352 | uvec_fast_set_x (type, base, idx, SCM_CAR (list)); | |
353 | list = SCM_CDR (list); | |
354 | idx++; | |
355 | } | |
356 | return uvec; | |
357 | } | |
358 | ||
359 | SCM | |
360 | scm_i_read_homogenous_vector (SCM port, char pfx) | |
361 | { | |
362 | /* We have read '#f', '#u', or '#s'. Next must be a decimal integer | |
363 | followed immediately by a list. | |
364 | */ | |
365 | ||
366 | int c; | |
367 | char tok[80]; | |
368 | int n_digs; | |
369 | SCM list; | |
370 | ||
371 | n_digs = 0; | |
372 | while ((c = scm_getc (port)) != EOF && '0' <= c && c <= '9' && n_digs < 80) | |
373 | tok[n_digs++] = c; | |
374 | ||
375 | if (c != EOF) | |
376 | scm_ungetc (c, port); | |
377 | ||
378 | if (n_digs == 0 && pfx == 'f') | |
379 | return SCM_BOOL_F; | |
380 | ||
381 | if (c != '(') | |
382 | scm_i_input_error (NULL, port, | |
383 | "#~a~a must be followed immediately by a '('", | |
384 | scm_list_2 (SCM_MAKE_CHAR (pfx), | |
385 | scm_from_locale_stringn (tok, n_digs))); | |
386 | ||
387 | list = scm_read (port); | |
388 | ||
389 | if (n_digs == 1 && strncmp (tok, "8", n_digs) == 0) | |
390 | { | |
391 | if (pfx == 'u') | |
392 | return scm_list_to_u8vector (list); | |
393 | else if (pfx == 's') | |
394 | return scm_list_to_s8vector (list); | |
395 | } | |
396 | else if (n_digs == 2 && strncmp (tok, "16", n_digs) == 0) | |
397 | { | |
398 | if (pfx == 'u') | |
399 | return scm_list_to_u16vector (list); | |
400 | else if (pfx == 's') | |
401 | return scm_list_to_s16vector (list); | |
402 | } | |
403 | else if (n_digs == 2 && strncmp (tok, "32", n_digs) == 0) | |
404 | { | |
405 | if (pfx == 'u') | |
406 | return scm_list_to_u32vector (list); | |
407 | else if (pfx == 's') | |
408 | return scm_list_to_s32vector (list); | |
409 | else if (pfx == 'f') | |
410 | return scm_list_to_f32vector (list); | |
411 | } | |
412 | else if (n_digs == 2 && strncmp (tok, "64", n_digs) == 0) | |
413 | { | |
414 | if (pfx == 'u') | |
415 | return scm_list_to_u64vector (list); | |
416 | else if (pfx == 's') | |
417 | return scm_list_to_s64vector (list); | |
418 | else if (pfx == 'f') | |
419 | return scm_list_to_f64vector (list); | |
420 | } | |
421 | ||
422 | scm_i_input_error (NULL, port, | |
423 | "unrecognized homogenous vector prefix #~a~a", | |
424 | scm_list_2 (SCM_MAKE_CHAR (pfx), | |
425 | scm_from_locale_stringn (tok, n_digs))); | |
426 | return SCM_BOOL_F; | |
427 | } | |
428 | ||
429 | /* ================================================================ */ | |
430 | /* Exported procedures. */ | |
431 | /* ================================================================ */ | |
432 | ||
433 | #define TYPE SCM_UVEC_U8 | |
434 | #define TAG u8 | |
435 | #include "libguile/srfi-4.i.c" | |
436 | ||
437 | #define TYPE SCM_UVEC_S8 | |
438 | #define TAG s8 | |
439 | #include "libguile/srfi-4.i.c" | |
440 | ||
441 | #define TYPE SCM_UVEC_U16 | |
442 | #define TAG u16 | |
443 | #include "libguile/srfi-4.i.c" | |
444 | ||
445 | #define TYPE SCM_UVEC_S16 | |
446 | #define TAG s16 | |
447 | #include "libguile/srfi-4.i.c" | |
448 | ||
449 | #define TYPE SCM_UVEC_U32 | |
450 | #define TAG u32 | |
451 | #include "libguile/srfi-4.i.c" | |
452 | ||
453 | #define TYPE SCM_UVEC_S32 | |
454 | #define TAG s32 | |
455 | #include "libguile/srfi-4.i.c" | |
456 | ||
457 | #define TYPE SCM_UVEC_U64 | |
458 | #define TAG u64 | |
459 | #include "libguile/srfi-4.i.c" | |
460 | ||
461 | #define TYPE SCM_UVEC_S64 | |
462 | #define TAG s64 | |
463 | #include "libguile/srfi-4.i.c" | |
464 | ||
465 | #define TYPE SCM_UVEC_F32 | |
466 | #define TAG f32 | |
467 | #include "libguile/srfi-4.i.c" | |
468 | ||
469 | #define TYPE SCM_UVEC_F64 | |
470 | #define TAG f64 | |
471 | #include "libguile/srfi-4.i.c" | |
472 | ||
473 | ||
474 | /* Create the smob type for homogeneous numeric vectors and install | |
475 | the primitives. */ | |
476 | void | |
477 | scm_init_srfi_4 (void) | |
478 | { | |
479 | scm_tc16_uvec = scm_make_smob_type ("uvec", 0); | |
480 | scm_set_smob_equalp (scm_tc16_uvec, uvec_equalp); | |
481 | scm_set_smob_free (scm_tc16_uvec, uvec_free); | |
482 | scm_set_smob_print (scm_tc16_uvec, uvec_print); | |
483 | #include "libguile/srfi-4.x" | |
484 | } | |
485 | ||
486 | /* End of srfi-4.c. */ |