*** empty log message ***
[bpt/guile.git] / libguile / srfi-4.c
CommitLineData
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. */
31int 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. */
60static const int uvec_sizes[10] = {
61 1, 1,
62 2, 2,
63 4, 4,
64 8, 8,
65 sizeof(float), sizeof(double)
66};
67
68static 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. */
82static int
83uvec_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
156static SCM
157uvec_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. */
173static size_t
174uvec_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
187static SCM_C_INLINE int
188is_uvec (int type, SCM obj)
189{
190 return (SCM_SMOB_PREDICATE (scm_tc16_uvec, obj)
191 && SCM_UVEC_TYPE (obj) == type);
192}
193
194static SCM_C_INLINE SCM
195uvec_p (int type, SCM obj)
196{
197 return scm_from_bool (is_uvec (type, obj));
198}
199
200static SCM_C_INLINE void
201uvec_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. */
209static SCM
210alloc_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
220static SCM_C_INLINE SCM
221uvec_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
247static SCM_C_INLINE void
248uvec_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
274static SCM_C_INLINE SCM
275make_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
289static SCM_C_INLINE SCM
290uvec_length (int type, SCM uvec)
291{
292 uvec_assert (type, uvec);
293 return scm_from_size_t (SCM_UVEC_LENGTH (uvec));
294}
295
296static SCM_C_INLINE SCM
297uvec_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
309static SCM_C_INLINE SCM
310uvec_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
321static SCM_C_INLINE SCM
322uvec_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
337static SCM_C_INLINE SCM
338list_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
359SCM
360scm_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. */
476void
477scm_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. */