Merge branch 'bdw-gc-static-alloc'
[bpt/guile.git] / libguile / bytevectors.c
1 /* Copyright (C) 2009 Free Software Foundation, Inc.
2 *
3 * This library is free software; you can redistribute it and/or
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.
7 *
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
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
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 * 02110-1301 USA
17 */
18
19
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #include <alloca.h>
25 #include <assert.h>
26
27 #include <gmp.h>
28
29 #include "libguile/_scm.h"
30 #include "libguile/extensions.h"
31 #include "libguile/bytevectors.h"
32 #include "libguile/strings.h"
33 #include "libguile/validate.h"
34 #include "libguile/ieee-754.h"
35 #include "libguile/arrays.h"
36 #include "libguile/array-handle.h"
37 #include "libguile/uniform.h"
38 #include "libguile/srfi-4.h"
39
40 #include <byteswap.h>
41 #include <striconveh.h>
42 #include <uniconv.h>
43 #include <unistr.h>
44
45 #ifdef HAVE_LIMITS_H
46 # include <limits.h>
47 #else
48 /* Assuming 32-bit longs. */
49 # define ULONG_MAX 4294967295UL
50 #endif
51
52 #include <string.h>
53
54
55 \f
56 /* Utilities. */
57
58 /* Convenience macros. These are used by the various templates (macros) that
59 are parameterized by integer signedness. */
60 #define INT8_T_signed scm_t_int8
61 #define INT8_T_unsigned scm_t_uint8
62 #define INT16_T_signed scm_t_int16
63 #define INT16_T_unsigned scm_t_uint16
64 #define INT32_T_signed scm_t_int32
65 #define INT32_T_unsigned scm_t_uint32
66 #define is_signed_int8(_x) (((_x) >= -128L) && ((_x) <= 127L))
67 #define is_unsigned_int8(_x) ((_x) <= 255UL)
68 #define is_signed_int16(_x) (((_x) >= -32768L) && ((_x) <= 32767L))
69 #define is_unsigned_int16(_x) ((_x) <= 65535UL)
70 #define is_signed_int32(_x) (((_x) >= -2147483648L) && ((_x) <= 2147483647L))
71 #define is_unsigned_int32(_x) ((_x) <= 4294967295UL)
72 #define SIGNEDNESS_signed 1
73 #define SIGNEDNESS_unsigned 0
74
75 #define INT_TYPE(_size, _sign) INT ## _size ## _T_ ## _sign
76 #define INT_SWAP(_size) bswap_ ## _size
77 #define INT_VALID_P(_size, _sign) is_ ## _sign ## _int ## _size
78 #define SIGNEDNESS(_sign) SIGNEDNESS_ ## _sign
79
80
81 #define INTEGER_ACCESSOR_PROLOGUE(_len, _sign) \
82 size_t c_len, c_index; \
83 _sign char *c_bv; \
84 \
85 SCM_VALIDATE_BYTEVECTOR (1, bv); \
86 c_index = scm_to_uint (index); \
87 \
88 c_len = SCM_BYTEVECTOR_LENGTH (bv); \
89 c_bv = (_sign char *) SCM_BYTEVECTOR_CONTENTS (bv); \
90 \
91 if (SCM_UNLIKELY (c_index + ((_len) >> 3UL) - 1 >= c_len)) \
92 scm_out_of_range (FUNC_NAME, index);
93
94 /* Template for fixed-size integer access (only 8, 16 or 32-bit). */
95 #define INTEGER_REF(_len, _sign) \
96 SCM result; \
97 \
98 INTEGER_ACCESSOR_PROLOGUE (_len, _sign); \
99 SCM_VALIDATE_SYMBOL (3, endianness); \
100 \
101 { \
102 INT_TYPE (_len, _sign) c_result; \
103 \
104 memcpy (&c_result, &c_bv[c_index], (_len) / 8); \
105 if (!scm_is_eq (endianness, scm_i_native_endianness)) \
106 c_result = INT_SWAP (_len) (c_result); \
107 \
108 result = SCM_I_MAKINUM (c_result); \
109 } \
110 \
111 return result;
112
113 /* Template for fixed-size integer access using the native endianness. */
114 #define INTEGER_NATIVE_REF(_len, _sign) \
115 SCM result; \
116 \
117 INTEGER_ACCESSOR_PROLOGUE (_len, _sign); \
118 \
119 { \
120 INT_TYPE (_len, _sign) c_result; \
121 \
122 memcpy (&c_result, &c_bv[c_index], (_len) / 8); \
123 result = SCM_I_MAKINUM (c_result); \
124 } \
125 \
126 return result;
127
128 /* Template for fixed-size integer modification (only 8, 16 or 32-bit). */
129 #define INTEGER_SET(_len, _sign) \
130 INTEGER_ACCESSOR_PROLOGUE (_len, _sign); \
131 SCM_VALIDATE_SYMBOL (3, endianness); \
132 \
133 { \
134 _sign long c_value; \
135 INT_TYPE (_len, _sign) c_value_short; \
136 \
137 if (SCM_UNLIKELY (!SCM_I_INUMP (value))) \
138 scm_wrong_type_arg (FUNC_NAME, 3, value); \
139 \
140 c_value = SCM_I_INUM (value); \
141 if (SCM_UNLIKELY (!INT_VALID_P (_len, _sign) (c_value))) \
142 scm_out_of_range (FUNC_NAME, value); \
143 \
144 c_value_short = (INT_TYPE (_len, _sign)) c_value; \
145 if (!scm_is_eq (endianness, scm_i_native_endianness)) \
146 c_value_short = INT_SWAP (_len) (c_value_short); \
147 \
148 memcpy (&c_bv[c_index], &c_value_short, (_len) / 8); \
149 } \
150 \
151 return SCM_UNSPECIFIED;
152
153 /* Template for fixed-size integer modification using the native
154 endianness. */
155 #define INTEGER_NATIVE_SET(_len, _sign) \
156 INTEGER_ACCESSOR_PROLOGUE (_len, _sign); \
157 \
158 { \
159 _sign long c_value; \
160 INT_TYPE (_len, _sign) c_value_short; \
161 \
162 if (SCM_UNLIKELY (!SCM_I_INUMP (value))) \
163 scm_wrong_type_arg (FUNC_NAME, 3, value); \
164 \
165 c_value = SCM_I_INUM (value); \
166 if (SCM_UNLIKELY (!INT_VALID_P (_len, _sign) (c_value))) \
167 scm_out_of_range (FUNC_NAME, value); \
168 \
169 c_value_short = (INT_TYPE (_len, _sign)) c_value; \
170 \
171 memcpy (&c_bv[c_index], &c_value_short, (_len) / 8); \
172 } \
173 \
174 return SCM_UNSPECIFIED;
175
176
177 \f
178 /* Bytevector type. */
179
180 #define SCM_BYTEVECTOR_HEADER_BYTES \
181 (SCM_BYTEVECTOR_HEADER_SIZE * sizeof (SCM))
182
183 #define SCM_BYTEVECTOR_SET_LENGTH(_bv, _len) \
184 SCM_SET_CELL_WORD_1 ((_bv), (scm_t_bits) (_len))
185
186 #define SCM_BYTEVECTOR_SET_ELEMENT_TYPE(bv, hint) \
187 SCM_SET_BYTEVECTOR_FLAGS ((bv), (hint))
188 #define SCM_BYTEVECTOR_TYPE_SIZE(var) \
189 (scm_i_array_element_type_sizes[SCM_BYTEVECTOR_ELEMENT_TYPE (var)]/8)
190 #define SCM_BYTEVECTOR_TYPED_LENGTH(var) \
191 SCM_BYTEVECTOR_LENGTH (var) / SCM_BYTEVECTOR_TYPE_SIZE (var)
192
193 /* The empty bytevector. */
194 SCM scm_null_bytevector = SCM_UNSPECIFIED;
195
196
197 static inline SCM
198 make_bytevector (size_t len, scm_t_array_element_type element_type)
199 {
200 SCM ret;
201 size_t c_len;
202
203 if (SCM_UNLIKELY (element_type > SCM_ARRAY_ELEMENT_TYPE_LAST
204 || scm_i_array_element_type_sizes[element_type] < 8
205 || len >= (SCM_I_SIZE_MAX
206 / (scm_i_array_element_type_sizes[element_type]/8))))
207 /* This would be an internal Guile programming error */
208 abort ();
209
210 if (SCM_UNLIKELY (len == 0 && element_type == SCM_ARRAY_ELEMENT_TYPE_VU8
211 && SCM_BYTEVECTOR_P (scm_null_bytevector)))
212 ret = scm_null_bytevector;
213 else
214 {
215 c_len = len * (scm_i_array_element_type_sizes[element_type] / 8);
216
217 ret = PTR2SCM (scm_gc_malloc_pointerless (SCM_BYTEVECTOR_HEADER_BYTES + c_len,
218 SCM_GC_BYTEVECTOR));
219
220 SCM_SET_CELL_TYPE (ret, scm_tc7_bytevector);
221 SCM_BYTEVECTOR_SET_LENGTH (ret, c_len);
222 SCM_BYTEVECTOR_SET_ELEMENT_TYPE (ret, element_type);
223 }
224
225 return ret;
226 }
227
228 /* Return a bytevector of LEN elements of type ELEMENT_TYPE, with element
229 values taken from CONTENTS. */
230 static inline SCM
231 make_bytevector_from_buffer (size_t len, void *contents,
232 scm_t_array_element_type element_type)
233 {
234 SCM ret;
235
236 /* We actually never reuse storage from CONTENTS. Hans Boehm says in
237 <gc/gc.h> that realloc(3) "shouldn't have been invented" and he may well
238 be right. */
239 ret = make_bytevector (len, element_type);
240
241 if (len > 0)
242 {
243 size_t c_len;
244
245 c_len = len * (scm_i_array_element_type_sizes[element_type] / 8);
246 memcpy (SCM_BYTEVECTOR_CONTENTS (ret),
247 contents,
248 c_len);
249
250 scm_gc_free (contents, c_len, SCM_GC_BYTEVECTOR);
251 }
252
253 return ret;
254 }
255
256
257 /* Return a new bytevector of size LEN octets. */
258 SCM
259 scm_c_make_bytevector (size_t len)
260 {
261 return make_bytevector (len, SCM_ARRAY_ELEMENT_TYPE_VU8);
262 }
263
264 /* Return a new bytevector of size LEN elements. */
265 SCM
266 scm_i_make_typed_bytevector (size_t len, scm_t_array_element_type element_type)
267 {
268 return make_bytevector (len, element_type);
269 }
270
271 /* Return a bytevector of size LEN made up of CONTENTS. The area pointed to
272 by CONTENTS must have been allocated using `scm_gc_malloc ()'. */
273 SCM
274 scm_c_take_bytevector (signed char *contents, size_t len)
275 {
276 return make_bytevector_from_buffer (len, contents, SCM_ARRAY_ELEMENT_TYPE_VU8);
277 }
278
279 SCM
280 scm_c_take_typed_bytevector (signed char *contents, size_t len,
281 scm_t_array_element_type element_type)
282 {
283 return make_bytevector_from_buffer (len, contents, element_type);
284 }
285
286 /* Shrink BV to C_NEW_LEN (which is assumed to be smaller than its current
287 size) and return the new bytevector (possibly different from BV). */
288 SCM
289 scm_c_shrink_bytevector (SCM bv, size_t c_new_len)
290 {
291 SCM new_bv;
292 size_t c_len;
293
294 if (SCM_UNLIKELY (c_new_len % SCM_BYTEVECTOR_TYPE_SIZE (bv)))
295 /* This would be an internal Guile programming error */
296 abort ();
297
298 c_len = SCM_BYTEVECTOR_LENGTH (bv);
299 if (SCM_UNLIKELY (c_new_len > c_len))
300 abort ();
301
302 SCM_BYTEVECTOR_SET_LENGTH (bv, c_new_len);
303
304 /* Resize the existing buffer. */
305 new_bv = PTR2SCM (scm_gc_realloc (SCM2PTR (bv),
306 c_len + SCM_BYTEVECTOR_HEADER_BYTES,
307 c_new_len + SCM_BYTEVECTOR_HEADER_BYTES,
308 SCM_GC_BYTEVECTOR));
309
310 return new_bv;
311 }
312
313 int
314 scm_is_bytevector (SCM obj)
315 {
316 return SCM_BYTEVECTOR_P (obj);
317 }
318
319 size_t
320 scm_c_bytevector_length (SCM bv)
321 #define FUNC_NAME "scm_c_bytevector_length"
322 {
323 SCM_VALIDATE_BYTEVECTOR (1, bv);
324
325 return SCM_BYTEVECTOR_LENGTH (bv);
326 }
327 #undef FUNC_NAME
328
329 scm_t_uint8
330 scm_c_bytevector_ref (SCM bv, size_t index)
331 #define FUNC_NAME "scm_c_bytevector_ref"
332 {
333 size_t c_len;
334 const scm_t_uint8 *c_bv;
335
336 SCM_VALIDATE_BYTEVECTOR (1, bv);
337
338 c_len = SCM_BYTEVECTOR_LENGTH (bv);
339 c_bv = (scm_t_uint8 *) SCM_BYTEVECTOR_CONTENTS (bv);
340
341 if (SCM_UNLIKELY (index >= c_len))
342 scm_out_of_range (FUNC_NAME, scm_from_size_t (index));
343
344 return c_bv[index];
345 }
346 #undef FUNC_NAME
347
348 void
349 scm_c_bytevector_set_x (SCM bv, size_t index, scm_t_uint8 value)
350 #define FUNC_NAME "scm_c_bytevector_set_x"
351 {
352 size_t c_len;
353 scm_t_uint8 *c_bv;
354
355 SCM_VALIDATE_BYTEVECTOR (1, bv);
356
357 c_len = SCM_BYTEVECTOR_LENGTH (bv);
358 c_bv = (scm_t_uint8 *) SCM_BYTEVECTOR_CONTENTS (bv);
359
360 if (SCM_UNLIKELY (index >= c_len))
361 scm_out_of_range (FUNC_NAME, scm_from_size_t (index));
362
363 c_bv[index] = value;
364 }
365 #undef FUNC_NAME
366
367
368 \f
369 int
370 scm_i_print_bytevector (SCM bv, SCM port, scm_print_state *pstate SCM_UNUSED)
371 {
372 ssize_t ubnd, inc, i;
373 scm_t_array_handle h;
374
375 scm_array_get_handle (bv, &h);
376
377 scm_putc ('#', port);
378 scm_write (scm_array_handle_element_type (&h), port);
379 scm_putc ('(', port);
380 for (i = h.dims[0].lbnd, ubnd = h.dims[0].ubnd, inc = h.dims[0].inc;
381 i <= ubnd; i += inc)
382 {
383 if (i > 0)
384 scm_putc (' ', port);
385 scm_write (scm_array_handle_ref (&h, i), port);
386 }
387 scm_putc (')', port);
388
389 return 1;
390 }
391
392 \f
393 /* General operations. */
394
395 SCM_SYMBOL (scm_sym_big, "big");
396 SCM_SYMBOL (scm_sym_little, "little");
397
398 SCM scm_endianness_big, scm_endianness_little;
399
400 /* Host endianness (a symbol). */
401 SCM scm_i_native_endianness = SCM_UNSPECIFIED;
402
403 /* Byte-swapping. */
404 #ifndef bswap_24
405 # define bswap_24(_x) \
406 ((((_x) & 0xff0000) >> 16) | \
407 (((_x) & 0x00ff00)) | \
408 (((_x) & 0x0000ff) << 16))
409 #endif
410
411
412 SCM_DEFINE (scm_native_endianness, "native-endianness", 0, 0, 0,
413 (void),
414 "Return a symbol denoting the machine's native endianness.")
415 #define FUNC_NAME s_scm_native_endianness
416 {
417 return scm_i_native_endianness;
418 }
419 #undef FUNC_NAME
420
421 SCM_DEFINE (scm_bytevector_p, "bytevector?", 1, 0, 0,
422 (SCM obj),
423 "Return true if @var{obj} is a bytevector.")
424 #define FUNC_NAME s_scm_bytevector_p
425 {
426 return scm_from_bool (scm_is_bytevector (obj));
427 }
428 #undef FUNC_NAME
429
430 SCM_DEFINE (scm_make_bytevector, "make-bytevector", 1, 1, 0,
431 (SCM len, SCM fill),
432 "Return a newly allocated bytevector of @var{len} bytes, "
433 "optionally filled with @var{fill}.")
434 #define FUNC_NAME s_scm_make_bytevector
435 {
436 SCM bv;
437 unsigned c_len;
438 signed char c_fill = '\0';
439
440 SCM_VALIDATE_UINT_COPY (1, len, c_len);
441 if (fill != SCM_UNDEFINED)
442 {
443 int value;
444
445 value = scm_to_int (fill);
446 if (SCM_UNLIKELY ((value < -128) || (value > 255)))
447 scm_out_of_range (FUNC_NAME, fill);
448 c_fill = (signed char) value;
449 }
450
451 bv = make_bytevector (c_len, SCM_ARRAY_ELEMENT_TYPE_VU8);
452 if (fill != SCM_UNDEFINED)
453 {
454 unsigned i;
455 signed char *contents;
456
457 contents = SCM_BYTEVECTOR_CONTENTS (bv);
458 for (i = 0; i < c_len; i++)
459 contents[i] = c_fill;
460 }
461
462 return bv;
463 }
464 #undef FUNC_NAME
465
466 SCM_DEFINE (scm_bytevector_length, "bytevector-length", 1, 0, 0,
467 (SCM bv),
468 "Return the length (in bytes) of @var{bv}.")
469 #define FUNC_NAME s_scm_bytevector_length
470 {
471 return scm_from_uint (scm_c_bytevector_length (bv));
472 }
473 #undef FUNC_NAME
474
475 SCM_DEFINE (scm_bytevector_eq_p, "bytevector=?", 2, 0, 0,
476 (SCM bv1, SCM bv2),
477 "Return is @var{bv1} equals to @var{bv2}---i.e., if they "
478 "have the same length and contents.")
479 #define FUNC_NAME s_scm_bytevector_eq_p
480 {
481 SCM result = SCM_BOOL_F;
482 unsigned c_len1, c_len2;
483
484 SCM_VALIDATE_BYTEVECTOR (1, bv1);
485 SCM_VALIDATE_BYTEVECTOR (2, bv2);
486
487 c_len1 = SCM_BYTEVECTOR_LENGTH (bv1);
488 c_len2 = SCM_BYTEVECTOR_LENGTH (bv2);
489
490 if (c_len1 == c_len2)
491 {
492 signed char *c_bv1, *c_bv2;
493
494 c_bv1 = SCM_BYTEVECTOR_CONTENTS (bv1);
495 c_bv2 = SCM_BYTEVECTOR_CONTENTS (bv2);
496
497 result = scm_from_bool (!memcmp (c_bv1, c_bv2, c_len1));
498 }
499
500 return result;
501 }
502 #undef FUNC_NAME
503
504 SCM_DEFINE (scm_bytevector_fill_x, "bytevector-fill!", 2, 0, 0,
505 (SCM bv, SCM fill),
506 "Fill bytevector @var{bv} with @var{fill}, a byte.")
507 #define FUNC_NAME s_scm_bytevector_fill_x
508 {
509 unsigned c_len, i;
510 signed char *c_bv, c_fill;
511
512 SCM_VALIDATE_BYTEVECTOR (1, bv);
513 c_fill = scm_to_int8 (fill);
514
515 c_len = SCM_BYTEVECTOR_LENGTH (bv);
516 c_bv = SCM_BYTEVECTOR_CONTENTS (bv);
517
518 for (i = 0; i < c_len; i++)
519 c_bv[i] = c_fill;
520
521 return SCM_UNSPECIFIED;
522 }
523 #undef FUNC_NAME
524
525 SCM_DEFINE (scm_bytevector_copy_x, "bytevector-copy!", 5, 0, 0,
526 (SCM source, SCM source_start, SCM target, SCM target_start,
527 SCM len),
528 "Copy @var{len} bytes from @var{source} into @var{target}, "
529 "starting reading from @var{source_start} (a positive index "
530 "within @var{source}) and start writing at "
531 "@var{target_start}.")
532 #define FUNC_NAME s_scm_bytevector_copy_x
533 {
534 unsigned c_len, c_source_len, c_target_len;
535 unsigned c_source_start, c_target_start;
536 signed char *c_source, *c_target;
537
538 SCM_VALIDATE_BYTEVECTOR (1, source);
539 SCM_VALIDATE_BYTEVECTOR (3, target);
540
541 c_len = scm_to_uint (len);
542 c_source_start = scm_to_uint (source_start);
543 c_target_start = scm_to_uint (target_start);
544
545 c_source = SCM_BYTEVECTOR_CONTENTS (source);
546 c_target = SCM_BYTEVECTOR_CONTENTS (target);
547 c_source_len = SCM_BYTEVECTOR_LENGTH (source);
548 c_target_len = SCM_BYTEVECTOR_LENGTH (target);
549
550 if (SCM_UNLIKELY (c_source_start + c_len > c_source_len))
551 scm_out_of_range (FUNC_NAME, source_start);
552 if (SCM_UNLIKELY (c_target_start + c_len > c_target_len))
553 scm_out_of_range (FUNC_NAME, target_start);
554
555 memcpy (c_target + c_target_start,
556 c_source + c_source_start,
557 c_len);
558
559 return SCM_UNSPECIFIED;
560 }
561 #undef FUNC_NAME
562
563 SCM_DEFINE (scm_bytevector_copy, "bytevector-copy", 1, 0, 0,
564 (SCM bv),
565 "Return a newly allocated copy of @var{bv}.")
566 #define FUNC_NAME s_scm_bytevector_copy
567 {
568 SCM copy;
569 unsigned c_len;
570 signed char *c_bv, *c_copy;
571
572 SCM_VALIDATE_BYTEVECTOR (1, bv);
573
574 c_len = SCM_BYTEVECTOR_LENGTH (bv);
575 c_bv = SCM_BYTEVECTOR_CONTENTS (bv);
576
577 copy = make_bytevector (c_len, SCM_BYTEVECTOR_ELEMENT_TYPE (bv));
578 c_copy = SCM_BYTEVECTOR_CONTENTS (copy);
579 memcpy (c_copy, c_bv, c_len);
580
581 return copy;
582 }
583 #undef FUNC_NAME
584
585 SCM_DEFINE (scm_uniform_array_to_bytevector, "uniform-array->bytevector",
586 1, 0, 0, (SCM array),
587 "Return a newly allocated bytevector whose contents\n"
588 "will be copied from the uniform array @var{array}.")
589 #define FUNC_NAME s_scm_uniform_array_to_bytevector
590 {
591 SCM contents, ret;
592 size_t len, sz, byte_len;
593 scm_t_array_handle h;
594 const void *elts;
595
596 contents = scm_array_contents (array, SCM_BOOL_T);
597 if (scm_is_false (contents))
598 scm_wrong_type_arg_msg (FUNC_NAME, 0, array, "uniform contiguous array");
599
600 scm_array_get_handle (contents, &h);
601 assert (h.base == 0);
602
603 elts = h.elements;
604 len = h.dims->inc * (h.dims->ubnd - h.dims->lbnd + 1);
605 sz = scm_array_handle_uniform_element_bit_size (&h);
606 if (sz >= 8 && ((sz % 8) == 0))
607 byte_len = len * (sz / 8);
608 else if (sz < 8)
609 /* byte_len = ceil (len * sz / 8) */
610 byte_len = (len * sz + 7) / 8;
611 else
612 /* an internal guile error, really */
613 SCM_MISC_ERROR ("uniform elements larger than 8 bits must fill whole bytes", SCM_EOL);
614
615 ret = make_bytevector (byte_len, SCM_ARRAY_ELEMENT_TYPE_VU8);
616 memcpy (SCM_BYTEVECTOR_CONTENTS (ret), elts, byte_len);
617
618 scm_array_handle_release (&h);
619
620 return ret;
621 }
622 #undef FUNC_NAME
623
624 \f
625 /* Operations on bytes and octets. */
626
627 SCM_DEFINE (scm_bytevector_u8_ref, "bytevector-u8-ref", 2, 0, 0,
628 (SCM bv, SCM index),
629 "Return the octet located at @var{index} in @var{bv}.")
630 #define FUNC_NAME s_scm_bytevector_u8_ref
631 {
632 INTEGER_NATIVE_REF (8, unsigned);
633 }
634 #undef FUNC_NAME
635
636 SCM_DEFINE (scm_bytevector_s8_ref, "bytevector-s8-ref", 2, 0, 0,
637 (SCM bv, SCM index),
638 "Return the byte located at @var{index} in @var{bv}.")
639 #define FUNC_NAME s_scm_bytevector_s8_ref
640 {
641 INTEGER_NATIVE_REF (8, signed);
642 }
643 #undef FUNC_NAME
644
645 SCM_DEFINE (scm_bytevector_u8_set_x, "bytevector-u8-set!", 3, 0, 0,
646 (SCM bv, SCM index, SCM value),
647 "Return the octet located at @var{index} in @var{bv}.")
648 #define FUNC_NAME s_scm_bytevector_u8_set_x
649 {
650 INTEGER_NATIVE_SET (8, unsigned);
651 }
652 #undef FUNC_NAME
653
654 SCM_DEFINE (scm_bytevector_s8_set_x, "bytevector-s8-set!", 3, 0, 0,
655 (SCM bv, SCM index, SCM value),
656 "Return the octet located at @var{index} in @var{bv}.")
657 #define FUNC_NAME s_scm_bytevector_s8_set_x
658 {
659 INTEGER_NATIVE_SET (8, signed);
660 }
661 #undef FUNC_NAME
662
663 #undef OCTET_ACCESSOR_PROLOGUE
664
665
666 SCM_DEFINE (scm_bytevector_to_u8_list, "bytevector->u8-list", 1, 0, 0,
667 (SCM bv),
668 "Return a newly allocated list of octets containing the "
669 "contents of @var{bv}.")
670 #define FUNC_NAME s_scm_bytevector_to_u8_list
671 {
672 SCM lst, pair;
673 unsigned c_len, i;
674 unsigned char *c_bv;
675
676 SCM_VALIDATE_BYTEVECTOR (1, bv);
677
678 c_len = SCM_BYTEVECTOR_LENGTH (bv);
679 c_bv = (unsigned char *) SCM_BYTEVECTOR_CONTENTS (bv);
680
681 lst = scm_make_list (scm_from_uint (c_len), SCM_UNSPECIFIED);
682 for (i = 0, pair = lst;
683 i < c_len;
684 i++, pair = SCM_CDR (pair))
685 {
686 SCM_SETCAR (pair, SCM_I_MAKINUM (c_bv[i]));
687 }
688
689 return lst;
690 }
691 #undef FUNC_NAME
692
693 SCM_DEFINE (scm_u8_list_to_bytevector, "u8-list->bytevector", 1, 0, 0,
694 (SCM lst),
695 "Turn @var{lst}, a list of octets, into a bytevector.")
696 #define FUNC_NAME s_scm_u8_list_to_bytevector
697 {
698 SCM bv, item;
699 long c_len, i;
700 unsigned char *c_bv;
701
702 SCM_VALIDATE_LIST_COPYLEN (1, lst, c_len);
703
704 bv = make_bytevector (c_len, SCM_ARRAY_ELEMENT_TYPE_VU8);
705 c_bv = (unsigned char *) SCM_BYTEVECTOR_CONTENTS (bv);
706
707 for (i = 0; i < c_len; lst = SCM_CDR (lst), i++)
708 {
709 item = SCM_CAR (lst);
710
711 if (SCM_LIKELY (SCM_I_INUMP (item)))
712 {
713 long c_item;
714
715 c_item = SCM_I_INUM (item);
716 if (SCM_LIKELY ((c_item >= 0) && (c_item < 256)))
717 c_bv[i] = (unsigned char) c_item;
718 else
719 goto type_error;
720 }
721 else
722 goto type_error;
723 }
724
725 return bv;
726
727 type_error:
728 scm_wrong_type_arg (FUNC_NAME, 1, item);
729
730 return SCM_BOOL_F;
731 }
732 #undef FUNC_NAME
733
734 /* Compute the two's complement of VALUE (a positive integer) on SIZE octets
735 using (2^(SIZE * 8) - VALUE). */
736 static inline void
737 twos_complement (mpz_t value, size_t size)
738 {
739 unsigned long bit_count;
740
741 /* We expect BIT_COUNT to fit in a unsigned long thanks to the range
742 checking on SIZE performed earlier. */
743 bit_count = (unsigned long) size << 3UL;
744
745 if (SCM_LIKELY (bit_count < sizeof (unsigned long)))
746 mpz_ui_sub (value, 1UL << bit_count, value);
747 else
748 {
749 mpz_t max;
750
751 mpz_init (max);
752 mpz_ui_pow_ui (max, 2, bit_count);
753 mpz_sub (value, max, value);
754 mpz_clear (max);
755 }
756 }
757
758 static inline SCM
759 bytevector_large_ref (const char *c_bv, size_t c_size, int signed_p,
760 SCM endianness)
761 {
762 SCM result;
763 mpz_t c_mpz;
764 int c_endianness, negative_p = 0;
765
766 if (signed_p)
767 {
768 if (scm_is_eq (endianness, scm_sym_big))
769 negative_p = c_bv[0] & 0x80;
770 else
771 negative_p = c_bv[c_size - 1] & 0x80;
772 }
773
774 c_endianness = scm_is_eq (endianness, scm_sym_big) ? 1 : -1;
775
776 mpz_init (c_mpz);
777 mpz_import (c_mpz, 1 /* 1 word */, 1 /* word order doesn't matter */,
778 c_size /* word is C_SIZE-byte long */,
779 c_endianness,
780 0 /* nails */, c_bv);
781
782 if (signed_p && negative_p)
783 {
784 twos_complement (c_mpz, c_size);
785 mpz_neg (c_mpz, c_mpz);
786 }
787
788 result = scm_from_mpz (c_mpz);
789 mpz_clear (c_mpz); /* FIXME: Needed? */
790
791 return result;
792 }
793
794 static inline int
795 bytevector_large_set (char *c_bv, size_t c_size, int signed_p,
796 SCM value, SCM endianness)
797 {
798 mpz_t c_mpz;
799 int c_endianness, c_sign, err = 0;
800
801 c_endianness = scm_is_eq (endianness, scm_sym_big) ? 1 : -1;
802
803 mpz_init (c_mpz);
804 scm_to_mpz (value, c_mpz);
805
806 c_sign = mpz_sgn (c_mpz);
807 if (c_sign < 0)
808 {
809 if (SCM_LIKELY (signed_p))
810 {
811 mpz_neg (c_mpz, c_mpz);
812 twos_complement (c_mpz, c_size);
813 }
814 else
815 {
816 err = -1;
817 goto finish;
818 }
819 }
820
821 if (c_sign == 0)
822 /* Zero. */
823 memset (c_bv, 0, c_size);
824 else
825 {
826 size_t word_count, value_size;
827
828 value_size = (mpz_sizeinbase (c_mpz, 2) + (8 * c_size)) / (8 * c_size);
829 if (SCM_UNLIKELY (value_size > c_size))
830 {
831 err = -2;
832 goto finish;
833 }
834
835
836 mpz_export (c_bv, &word_count, 1 /* word order doesn't matter */,
837 c_size, c_endianness,
838 0 /* nails */, c_mpz);
839 if (SCM_UNLIKELY (word_count != 1))
840 /* Shouldn't happen since we already checked with VALUE_SIZE. */
841 abort ();
842 }
843
844 finish:
845 mpz_clear (c_mpz);
846
847 return err;
848 }
849
850 #define GENERIC_INTEGER_ACCESSOR_PROLOGUE(_sign) \
851 unsigned long c_len, c_index, c_size; \
852 char *c_bv; \
853 \
854 SCM_VALIDATE_BYTEVECTOR (1, bv); \
855 c_index = scm_to_ulong (index); \
856 c_size = scm_to_ulong (size); \
857 \
858 c_len = SCM_BYTEVECTOR_LENGTH (bv); \
859 c_bv = (char *) SCM_BYTEVECTOR_CONTENTS (bv); \
860 \
861 /* C_SIZE must have its 3 higher bits set to zero so that \
862 multiplying it by 8 yields a number that fits in an \
863 unsigned long. */ \
864 if (SCM_UNLIKELY ((c_size == 0) || (c_size >= (ULONG_MAX >> 3L)))) \
865 scm_out_of_range (FUNC_NAME, size); \
866 if (SCM_UNLIKELY (c_index + c_size > c_len)) \
867 scm_out_of_range (FUNC_NAME, index);
868
869
870 /* Template of an integer reference function. */
871 #define GENERIC_INTEGER_REF(_sign) \
872 SCM result; \
873 \
874 if (c_size < 3) \
875 { \
876 int swap; \
877 _sign int value; \
878 \
879 swap = !scm_is_eq (endianness, scm_i_native_endianness); \
880 switch (c_size) \
881 { \
882 case 1: \
883 { \
884 _sign char c_value8; \
885 memcpy (&c_value8, c_bv, 1); \
886 value = c_value8; \
887 } \
888 break; \
889 case 2: \
890 { \
891 INT_TYPE (16, _sign) c_value16; \
892 memcpy (&c_value16, c_bv, 2); \
893 if (swap) \
894 value = (INT_TYPE (16, _sign)) bswap_16 (c_value16); \
895 else \
896 value = c_value16; \
897 } \
898 break; \
899 default: \
900 abort (); \
901 } \
902 \
903 result = SCM_I_MAKINUM ((_sign int) value); \
904 } \
905 else \
906 result = bytevector_large_ref ((char *) c_bv, \
907 c_size, SIGNEDNESS (_sign), \
908 endianness); \
909 \
910 return result;
911
912 static inline SCM
913 bytevector_signed_ref (const char *c_bv, size_t c_size, SCM endianness)
914 {
915 GENERIC_INTEGER_REF (signed);
916 }
917
918 static inline SCM
919 bytevector_unsigned_ref (const char *c_bv, size_t c_size, SCM endianness)
920 {
921 GENERIC_INTEGER_REF (unsigned);
922 }
923
924
925 /* Template of an integer assignment function. */
926 #define GENERIC_INTEGER_SET(_sign) \
927 if (c_size < 3) \
928 { \
929 _sign int c_value; \
930 \
931 if (SCM_UNLIKELY (!SCM_I_INUMP (value))) \
932 goto range_error; \
933 \
934 c_value = SCM_I_INUM (value); \
935 switch (c_size) \
936 { \
937 case 1: \
938 if (SCM_LIKELY (INT_VALID_P (8, _sign) (c_value))) \
939 { \
940 _sign char c_value8; \
941 c_value8 = (_sign char) c_value; \
942 memcpy (c_bv, &c_value8, 1); \
943 } \
944 else \
945 goto range_error; \
946 break; \
947 \
948 case 2: \
949 if (SCM_LIKELY (INT_VALID_P (16, _sign) (c_value))) \
950 { \
951 int swap; \
952 INT_TYPE (16, _sign) c_value16; \
953 \
954 swap = !scm_is_eq (endianness, scm_i_native_endianness); \
955 \
956 if (swap) \
957 c_value16 = (INT_TYPE (16, _sign)) bswap_16 (c_value); \
958 else \
959 c_value16 = c_value; \
960 \
961 memcpy (c_bv, &c_value16, 2); \
962 } \
963 else \
964 goto range_error; \
965 break; \
966 \
967 default: \
968 abort (); \
969 } \
970 } \
971 else \
972 { \
973 int err; \
974 \
975 err = bytevector_large_set (c_bv, c_size, \
976 SIGNEDNESS (_sign), \
977 value, endianness); \
978 if (err) \
979 goto range_error; \
980 } \
981 \
982 return; \
983 \
984 range_error: \
985 scm_out_of_range (FUNC_NAME, value); \
986 return;
987
988 static inline void
989 bytevector_signed_set (char *c_bv, size_t c_size,
990 SCM value, SCM endianness,
991 const char *func_name)
992 #define FUNC_NAME func_name
993 {
994 GENERIC_INTEGER_SET (signed);
995 }
996 #undef FUNC_NAME
997
998 static inline void
999 bytevector_unsigned_set (char *c_bv, size_t c_size,
1000 SCM value, SCM endianness,
1001 const char *func_name)
1002 #define FUNC_NAME func_name
1003 {
1004 GENERIC_INTEGER_SET (unsigned);
1005 }
1006 #undef FUNC_NAME
1007
1008 #undef GENERIC_INTEGER_SET
1009 #undef GENERIC_INTEGER_REF
1010
1011
1012 SCM_DEFINE (scm_bytevector_uint_ref, "bytevector-uint-ref", 4, 0, 0,
1013 (SCM bv, SCM index, SCM endianness, SCM size),
1014 "Return the @var{size}-octet long unsigned integer at index "
1015 "@var{index} in @var{bv}.")
1016 #define FUNC_NAME s_scm_bytevector_uint_ref
1017 {
1018 GENERIC_INTEGER_ACCESSOR_PROLOGUE (unsigned);
1019
1020 return (bytevector_unsigned_ref (&c_bv[c_index], c_size, endianness));
1021 }
1022 #undef FUNC_NAME
1023
1024 SCM_DEFINE (scm_bytevector_sint_ref, "bytevector-sint-ref", 4, 0, 0,
1025 (SCM bv, SCM index, SCM endianness, SCM size),
1026 "Return the @var{size}-octet long unsigned integer at index "
1027 "@var{index} in @var{bv}.")
1028 #define FUNC_NAME s_scm_bytevector_sint_ref
1029 {
1030 GENERIC_INTEGER_ACCESSOR_PROLOGUE (signed);
1031
1032 return (bytevector_signed_ref (&c_bv[c_index], c_size, endianness));
1033 }
1034 #undef FUNC_NAME
1035
1036 SCM_DEFINE (scm_bytevector_uint_set_x, "bytevector-uint-set!", 5, 0, 0,
1037 (SCM bv, SCM index, SCM value, SCM endianness, SCM size),
1038 "Set the @var{size}-octet long unsigned integer at @var{index} "
1039 "to @var{value}.")
1040 #define FUNC_NAME s_scm_bytevector_uint_set_x
1041 {
1042 GENERIC_INTEGER_ACCESSOR_PROLOGUE (unsigned);
1043
1044 bytevector_unsigned_set (&c_bv[c_index], c_size, value, endianness,
1045 FUNC_NAME);
1046
1047 return SCM_UNSPECIFIED;
1048 }
1049 #undef FUNC_NAME
1050
1051 SCM_DEFINE (scm_bytevector_sint_set_x, "bytevector-sint-set!", 5, 0, 0,
1052 (SCM bv, SCM index, SCM value, SCM endianness, SCM size),
1053 "Set the @var{size}-octet long signed integer at @var{index} "
1054 "to @var{value}.")
1055 #define FUNC_NAME s_scm_bytevector_sint_set_x
1056 {
1057 GENERIC_INTEGER_ACCESSOR_PROLOGUE (signed);
1058
1059 bytevector_signed_set (&c_bv[c_index], c_size, value, endianness,
1060 FUNC_NAME);
1061
1062 return SCM_UNSPECIFIED;
1063 }
1064 #undef FUNC_NAME
1065
1066
1067 \f
1068 /* Operations on integers of arbitrary size. */
1069
1070 #define INTEGERS_TO_LIST(_sign) \
1071 SCM lst, pair; \
1072 size_t i, c_len, c_size; \
1073 \
1074 SCM_VALIDATE_BYTEVECTOR (1, bv); \
1075 SCM_VALIDATE_SYMBOL (2, endianness); \
1076 c_size = scm_to_uint (size); \
1077 \
1078 c_len = SCM_BYTEVECTOR_LENGTH (bv); \
1079 if (SCM_UNLIKELY (c_len == 0)) \
1080 lst = SCM_EOL; \
1081 else if (SCM_UNLIKELY (c_len < c_size)) \
1082 scm_out_of_range (FUNC_NAME, size); \
1083 else \
1084 { \
1085 const char *c_bv; \
1086 \
1087 c_bv = (char *) SCM_BYTEVECTOR_CONTENTS (bv); \
1088 \
1089 lst = scm_make_list (scm_from_uint (c_len / c_size), \
1090 SCM_UNSPECIFIED); \
1091 for (i = 0, pair = lst; \
1092 i <= c_len - c_size; \
1093 i += c_size, c_bv += c_size, pair = SCM_CDR (pair)) \
1094 { \
1095 SCM_SETCAR (pair, \
1096 bytevector_ ## _sign ## _ref (c_bv, c_size, \
1097 endianness)); \
1098 } \
1099 } \
1100 \
1101 return lst;
1102
1103 SCM_DEFINE (scm_bytevector_to_sint_list, "bytevector->sint-list",
1104 3, 0, 0,
1105 (SCM bv, SCM endianness, SCM size),
1106 "Return a list of signed integers of @var{size} octets "
1107 "representing the contents of @var{bv}.")
1108 #define FUNC_NAME s_scm_bytevector_to_sint_list
1109 {
1110 INTEGERS_TO_LIST (signed);
1111 }
1112 #undef FUNC_NAME
1113
1114 SCM_DEFINE (scm_bytevector_to_uint_list, "bytevector->uint-list",
1115 3, 0, 0,
1116 (SCM bv, SCM endianness, SCM size),
1117 "Return a list of unsigned integers of @var{size} octets "
1118 "representing the contents of @var{bv}.")
1119 #define FUNC_NAME s_scm_bytevector_to_uint_list
1120 {
1121 INTEGERS_TO_LIST (unsigned);
1122 }
1123 #undef FUNC_NAME
1124
1125 #undef INTEGER_TO_LIST
1126
1127
1128 #define INTEGER_LIST_TO_BYTEVECTOR(_sign) \
1129 SCM bv; \
1130 long c_len; \
1131 size_t c_size; \
1132 char *c_bv, *c_bv_ptr; \
1133 \
1134 SCM_VALIDATE_LIST_COPYLEN (1, lst, c_len); \
1135 SCM_VALIDATE_SYMBOL (2, endianness); \
1136 c_size = scm_to_uint (size); \
1137 \
1138 if (SCM_UNLIKELY ((c_size == 0) || (c_size >= (ULONG_MAX >> 3L)))) \
1139 scm_out_of_range (FUNC_NAME, size); \
1140 \
1141 bv = make_bytevector (c_len * c_size, SCM_ARRAY_ELEMENT_TYPE_VU8); \
1142 c_bv = (char *) SCM_BYTEVECTOR_CONTENTS (bv); \
1143 \
1144 for (c_bv_ptr = c_bv; \
1145 !scm_is_null (lst); \
1146 lst = SCM_CDR (lst), c_bv_ptr += c_size) \
1147 { \
1148 bytevector_ ## _sign ## _set (c_bv_ptr, c_size, \
1149 SCM_CAR (lst), endianness, \
1150 FUNC_NAME); \
1151 } \
1152 \
1153 return bv;
1154
1155
1156 SCM_DEFINE (scm_uint_list_to_bytevector, "uint-list->bytevector",
1157 3, 0, 0,
1158 (SCM lst, SCM endianness, SCM size),
1159 "Return a bytevector containing the unsigned integers "
1160 "listed in @var{lst} and encoded on @var{size} octets "
1161 "according to @var{endianness}.")
1162 #define FUNC_NAME s_scm_uint_list_to_bytevector
1163 {
1164 INTEGER_LIST_TO_BYTEVECTOR (unsigned);
1165 }
1166 #undef FUNC_NAME
1167
1168 SCM_DEFINE (scm_sint_list_to_bytevector, "sint-list->bytevector",
1169 3, 0, 0,
1170 (SCM lst, SCM endianness, SCM size),
1171 "Return a bytevector containing the signed integers "
1172 "listed in @var{lst} and encoded on @var{size} octets "
1173 "according to @var{endianness}.")
1174 #define FUNC_NAME s_scm_sint_list_to_bytevector
1175 {
1176 INTEGER_LIST_TO_BYTEVECTOR (signed);
1177 }
1178 #undef FUNC_NAME
1179
1180 #undef INTEGER_LIST_TO_BYTEVECTOR
1181
1182
1183 \f
1184 /* Operations on 16-bit integers. */
1185
1186 SCM_DEFINE (scm_bytevector_u16_ref, "bytevector-u16-ref",
1187 3, 0, 0,
1188 (SCM bv, SCM index, SCM endianness),
1189 "Return the unsigned 16-bit integer from @var{bv} at "
1190 "@var{index}.")
1191 #define FUNC_NAME s_scm_bytevector_u16_ref
1192 {
1193 INTEGER_REF (16, unsigned);
1194 }
1195 #undef FUNC_NAME
1196
1197 SCM_DEFINE (scm_bytevector_s16_ref, "bytevector-s16-ref",
1198 3, 0, 0,
1199 (SCM bv, SCM index, SCM endianness),
1200 "Return the signed 16-bit integer from @var{bv} at "
1201 "@var{index}.")
1202 #define FUNC_NAME s_scm_bytevector_s16_ref
1203 {
1204 INTEGER_REF (16, signed);
1205 }
1206 #undef FUNC_NAME
1207
1208 SCM_DEFINE (scm_bytevector_u16_native_ref, "bytevector-u16-native-ref",
1209 2, 0, 0,
1210 (SCM bv, SCM index),
1211 "Return the unsigned 16-bit integer from @var{bv} at "
1212 "@var{index} using the native endianness.")
1213 #define FUNC_NAME s_scm_bytevector_u16_native_ref
1214 {
1215 INTEGER_NATIVE_REF (16, unsigned);
1216 }
1217 #undef FUNC_NAME
1218
1219 SCM_DEFINE (scm_bytevector_s16_native_ref, "bytevector-s16-native-ref",
1220 2, 0, 0,
1221 (SCM bv, SCM index),
1222 "Return the unsigned 16-bit integer from @var{bv} at "
1223 "@var{index} using the native endianness.")
1224 #define FUNC_NAME s_scm_bytevector_s16_native_ref
1225 {
1226 INTEGER_NATIVE_REF (16, signed);
1227 }
1228 #undef FUNC_NAME
1229
1230 SCM_DEFINE (scm_bytevector_u16_set_x, "bytevector-u16-set!",
1231 4, 0, 0,
1232 (SCM bv, SCM index, SCM value, SCM endianness),
1233 "Store @var{value} in @var{bv} at @var{index} according to "
1234 "@var{endianness}.")
1235 #define FUNC_NAME s_scm_bytevector_u16_set_x
1236 {
1237 INTEGER_SET (16, unsigned);
1238 }
1239 #undef FUNC_NAME
1240
1241 SCM_DEFINE (scm_bytevector_s16_set_x, "bytevector-s16-set!",
1242 4, 0, 0,
1243 (SCM bv, SCM index, SCM value, SCM endianness),
1244 "Store @var{value} in @var{bv} at @var{index} according to "
1245 "@var{endianness}.")
1246 #define FUNC_NAME s_scm_bytevector_s16_set_x
1247 {
1248 INTEGER_SET (16, signed);
1249 }
1250 #undef FUNC_NAME
1251
1252 SCM_DEFINE (scm_bytevector_u16_native_set_x, "bytevector-u16-native-set!",
1253 3, 0, 0,
1254 (SCM bv, SCM index, SCM value),
1255 "Store the unsigned integer @var{value} at index @var{index} "
1256 "of @var{bv} using the native endianness.")
1257 #define FUNC_NAME s_scm_bytevector_u16_native_set_x
1258 {
1259 INTEGER_NATIVE_SET (16, unsigned);
1260 }
1261 #undef FUNC_NAME
1262
1263 SCM_DEFINE (scm_bytevector_s16_native_set_x, "bytevector-s16-native-set!",
1264 3, 0, 0,
1265 (SCM bv, SCM index, SCM value),
1266 "Store the signed integer @var{value} at index @var{index} "
1267 "of @var{bv} using the native endianness.")
1268 #define FUNC_NAME s_scm_bytevector_s16_native_set_x
1269 {
1270 INTEGER_NATIVE_SET (16, signed);
1271 }
1272 #undef FUNC_NAME
1273
1274
1275 \f
1276 /* Operations on 32-bit integers. */
1277
1278 /* Unfortunately, on 32-bit machines `SCM' is not large enough to hold
1279 arbitrary 32-bit integers. Thus we fall back to using the
1280 `large_{ref,set}' variants on 32-bit machines. */
1281
1282 #define LARGE_INTEGER_REF(_len, _sign) \
1283 INTEGER_ACCESSOR_PROLOGUE(_len, _sign); \
1284 SCM_VALIDATE_SYMBOL (3, endianness); \
1285 \
1286 return (bytevector_large_ref ((char *) c_bv + c_index, _len / 8, \
1287 SIGNEDNESS (_sign), endianness));
1288
1289 #define LARGE_INTEGER_SET(_len, _sign) \
1290 int err; \
1291 INTEGER_ACCESSOR_PROLOGUE (_len, _sign); \
1292 SCM_VALIDATE_SYMBOL (4, endianness); \
1293 \
1294 err = bytevector_large_set ((char *) c_bv + c_index, _len / 8, \
1295 SIGNEDNESS (_sign), value, endianness); \
1296 if (SCM_UNLIKELY (err)) \
1297 scm_out_of_range (FUNC_NAME, value); \
1298 \
1299 return SCM_UNSPECIFIED;
1300
1301 #define LARGE_INTEGER_NATIVE_REF(_len, _sign) \
1302 INTEGER_ACCESSOR_PROLOGUE(_len, _sign); \
1303 return (bytevector_large_ref ((char *) c_bv + c_index, _len / 8, \
1304 SIGNEDNESS (_sign), scm_i_native_endianness));
1305
1306 #define LARGE_INTEGER_NATIVE_SET(_len, _sign) \
1307 int err; \
1308 INTEGER_ACCESSOR_PROLOGUE (_len, _sign); \
1309 \
1310 err = bytevector_large_set ((char *) c_bv + c_index, _len / 8, \
1311 SIGNEDNESS (_sign), value, \
1312 scm_i_native_endianness); \
1313 if (SCM_UNLIKELY (err)) \
1314 scm_out_of_range (FUNC_NAME, value); \
1315 \
1316 return SCM_UNSPECIFIED;
1317
1318
1319 SCM_DEFINE (scm_bytevector_u32_ref, "bytevector-u32-ref",
1320 3, 0, 0,
1321 (SCM bv, SCM index, SCM endianness),
1322 "Return the unsigned 32-bit integer from @var{bv} at "
1323 "@var{index}.")
1324 #define FUNC_NAME s_scm_bytevector_u32_ref
1325 {
1326 #if SIZEOF_VOID_P > 4
1327 INTEGER_REF (32, unsigned);
1328 #else
1329 LARGE_INTEGER_REF (32, unsigned);
1330 #endif
1331 }
1332 #undef FUNC_NAME
1333
1334 SCM_DEFINE (scm_bytevector_s32_ref, "bytevector-s32-ref",
1335 3, 0, 0,
1336 (SCM bv, SCM index, SCM endianness),
1337 "Return the signed 32-bit integer from @var{bv} at "
1338 "@var{index}.")
1339 #define FUNC_NAME s_scm_bytevector_s32_ref
1340 {
1341 #if SIZEOF_VOID_P > 4
1342 INTEGER_REF (32, signed);
1343 #else
1344 LARGE_INTEGER_REF (32, signed);
1345 #endif
1346 }
1347 #undef FUNC_NAME
1348
1349 SCM_DEFINE (scm_bytevector_u32_native_ref, "bytevector-u32-native-ref",
1350 2, 0, 0,
1351 (SCM bv, SCM index),
1352 "Return the unsigned 32-bit integer from @var{bv} at "
1353 "@var{index} using the native endianness.")
1354 #define FUNC_NAME s_scm_bytevector_u32_native_ref
1355 {
1356 #if SIZEOF_VOID_P > 4
1357 INTEGER_NATIVE_REF (32, unsigned);
1358 #else
1359 LARGE_INTEGER_NATIVE_REF (32, unsigned);
1360 #endif
1361 }
1362 #undef FUNC_NAME
1363
1364 SCM_DEFINE (scm_bytevector_s32_native_ref, "bytevector-s32-native-ref",
1365 2, 0, 0,
1366 (SCM bv, SCM index),
1367 "Return the unsigned 32-bit integer from @var{bv} at "
1368 "@var{index} using the native endianness.")
1369 #define FUNC_NAME s_scm_bytevector_s32_native_ref
1370 {
1371 #if SIZEOF_VOID_P > 4
1372 INTEGER_NATIVE_REF (32, signed);
1373 #else
1374 LARGE_INTEGER_NATIVE_REF (32, signed);
1375 #endif
1376 }
1377 #undef FUNC_NAME
1378
1379 SCM_DEFINE (scm_bytevector_u32_set_x, "bytevector-u32-set!",
1380 4, 0, 0,
1381 (SCM bv, SCM index, SCM value, SCM endianness),
1382 "Store @var{value} in @var{bv} at @var{index} according to "
1383 "@var{endianness}.")
1384 #define FUNC_NAME s_scm_bytevector_u32_set_x
1385 {
1386 #if SIZEOF_VOID_P > 4
1387 INTEGER_SET (32, unsigned);
1388 #else
1389 LARGE_INTEGER_SET (32, unsigned);
1390 #endif
1391 }
1392 #undef FUNC_NAME
1393
1394 SCM_DEFINE (scm_bytevector_s32_set_x, "bytevector-s32-set!",
1395 4, 0, 0,
1396 (SCM bv, SCM index, SCM value, SCM endianness),
1397 "Store @var{value} in @var{bv} at @var{index} according to "
1398 "@var{endianness}.")
1399 #define FUNC_NAME s_scm_bytevector_s32_set_x
1400 {
1401 #if SIZEOF_VOID_P > 4
1402 INTEGER_SET (32, signed);
1403 #else
1404 LARGE_INTEGER_SET (32, signed);
1405 #endif
1406 }
1407 #undef FUNC_NAME
1408
1409 SCM_DEFINE (scm_bytevector_u32_native_set_x, "bytevector-u32-native-set!",
1410 3, 0, 0,
1411 (SCM bv, SCM index, SCM value),
1412 "Store the unsigned integer @var{value} at index @var{index} "
1413 "of @var{bv} using the native endianness.")
1414 #define FUNC_NAME s_scm_bytevector_u32_native_set_x
1415 {
1416 #if SIZEOF_VOID_P > 4
1417 INTEGER_NATIVE_SET (32, unsigned);
1418 #else
1419 LARGE_INTEGER_NATIVE_SET (32, unsigned);
1420 #endif
1421 }
1422 #undef FUNC_NAME
1423
1424 SCM_DEFINE (scm_bytevector_s32_native_set_x, "bytevector-s32-native-set!",
1425 3, 0, 0,
1426 (SCM bv, SCM index, SCM value),
1427 "Store the signed integer @var{value} at index @var{index} "
1428 "of @var{bv} using the native endianness.")
1429 #define FUNC_NAME s_scm_bytevector_s32_native_set_x
1430 {
1431 #if SIZEOF_VOID_P > 4
1432 INTEGER_NATIVE_SET (32, signed);
1433 #else
1434 LARGE_INTEGER_NATIVE_SET (32, signed);
1435 #endif
1436 }
1437 #undef FUNC_NAME
1438
1439
1440 \f
1441 /* Operations on 64-bit integers. */
1442
1443 /* For 64-bit integers, we use only the `large_{ref,set}' variant. */
1444
1445 SCM_DEFINE (scm_bytevector_u64_ref, "bytevector-u64-ref",
1446 3, 0, 0,
1447 (SCM bv, SCM index, SCM endianness),
1448 "Return the unsigned 64-bit integer from @var{bv} at "
1449 "@var{index}.")
1450 #define FUNC_NAME s_scm_bytevector_u64_ref
1451 {
1452 LARGE_INTEGER_REF (64, unsigned);
1453 }
1454 #undef FUNC_NAME
1455
1456 SCM_DEFINE (scm_bytevector_s64_ref, "bytevector-s64-ref",
1457 3, 0, 0,
1458 (SCM bv, SCM index, SCM endianness),
1459 "Return the signed 64-bit integer from @var{bv} at "
1460 "@var{index}.")
1461 #define FUNC_NAME s_scm_bytevector_s64_ref
1462 {
1463 LARGE_INTEGER_REF (64, signed);
1464 }
1465 #undef FUNC_NAME
1466
1467 SCM_DEFINE (scm_bytevector_u64_native_ref, "bytevector-u64-native-ref",
1468 2, 0, 0,
1469 (SCM bv, SCM index),
1470 "Return the unsigned 64-bit integer from @var{bv} at "
1471 "@var{index} using the native endianness.")
1472 #define FUNC_NAME s_scm_bytevector_u64_native_ref
1473 {
1474 LARGE_INTEGER_NATIVE_REF (64, unsigned);
1475 }
1476 #undef FUNC_NAME
1477
1478 SCM_DEFINE (scm_bytevector_s64_native_ref, "bytevector-s64-native-ref",
1479 2, 0, 0,
1480 (SCM bv, SCM index),
1481 "Return the unsigned 64-bit integer from @var{bv} at "
1482 "@var{index} using the native endianness.")
1483 #define FUNC_NAME s_scm_bytevector_s64_native_ref
1484 {
1485 LARGE_INTEGER_NATIVE_REF (64, signed);
1486 }
1487 #undef FUNC_NAME
1488
1489 SCM_DEFINE (scm_bytevector_u64_set_x, "bytevector-u64-set!",
1490 4, 0, 0,
1491 (SCM bv, SCM index, SCM value, SCM endianness),
1492 "Store @var{value} in @var{bv} at @var{index} according to "
1493 "@var{endianness}.")
1494 #define FUNC_NAME s_scm_bytevector_u64_set_x
1495 {
1496 LARGE_INTEGER_SET (64, unsigned);
1497 }
1498 #undef FUNC_NAME
1499
1500 SCM_DEFINE (scm_bytevector_s64_set_x, "bytevector-s64-set!",
1501 4, 0, 0,
1502 (SCM bv, SCM index, SCM value, SCM endianness),
1503 "Store @var{value} in @var{bv} at @var{index} according to "
1504 "@var{endianness}.")
1505 #define FUNC_NAME s_scm_bytevector_s64_set_x
1506 {
1507 LARGE_INTEGER_SET (64, signed);
1508 }
1509 #undef FUNC_NAME
1510
1511 SCM_DEFINE (scm_bytevector_u64_native_set_x, "bytevector-u64-native-set!",
1512 3, 0, 0,
1513 (SCM bv, SCM index, SCM value),
1514 "Store the unsigned integer @var{value} at index @var{index} "
1515 "of @var{bv} using the native endianness.")
1516 #define FUNC_NAME s_scm_bytevector_u64_native_set_x
1517 {
1518 LARGE_INTEGER_NATIVE_SET (64, unsigned);
1519 }
1520 #undef FUNC_NAME
1521
1522 SCM_DEFINE (scm_bytevector_s64_native_set_x, "bytevector-s64-native-set!",
1523 3, 0, 0,
1524 (SCM bv, SCM index, SCM value),
1525 "Store the signed integer @var{value} at index @var{index} "
1526 "of @var{bv} using the native endianness.")
1527 #define FUNC_NAME s_scm_bytevector_s64_native_set_x
1528 {
1529 LARGE_INTEGER_NATIVE_SET (64, signed);
1530 }
1531 #undef FUNC_NAME
1532
1533
1534 \f
1535 /* Operations on IEEE-754 numbers. */
1536
1537 /* There are two possible word endians, visible in glibc's <ieee754.h>.
1538 However, in R6RS, when the endianness is `little', little endian is
1539 assumed for both the byte order and the word order. This is clear from
1540 Section 2.1 of R6RS-lib (in response to
1541 http://www.r6rs.org/formal-comments/comment-187.txt). */
1542
1543
1544 /* Convert to/from a floating-point number with different endianness. This
1545 method is probably not the most efficient but it should be portable. */
1546
1547 static inline void
1548 float_to_foreign_endianness (union scm_ieee754_float *target,
1549 float source)
1550 {
1551 union scm_ieee754_float src;
1552
1553 src.f = source;
1554
1555 #ifdef WORDS_BIGENDIAN
1556 /* Assuming little endian for both byte and word order. */
1557 target->little_endian.negative = src.big_endian.negative;
1558 target->little_endian.exponent = src.big_endian.exponent;
1559 target->little_endian.mantissa = src.big_endian.mantissa;
1560 #else
1561 target->big_endian.negative = src.little_endian.negative;
1562 target->big_endian.exponent = src.little_endian.exponent;
1563 target->big_endian.mantissa = src.little_endian.mantissa;
1564 #endif
1565 }
1566
1567 static inline float
1568 float_from_foreign_endianness (const union scm_ieee754_float *source)
1569 {
1570 union scm_ieee754_float result;
1571
1572 #ifdef WORDS_BIGENDIAN
1573 /* Assuming little endian for both byte and word order. */
1574 result.big_endian.negative = source->little_endian.negative;
1575 result.big_endian.exponent = source->little_endian.exponent;
1576 result.big_endian.mantissa = source->little_endian.mantissa;
1577 #else
1578 result.little_endian.negative = source->big_endian.negative;
1579 result.little_endian.exponent = source->big_endian.exponent;
1580 result.little_endian.mantissa = source->big_endian.mantissa;
1581 #endif
1582
1583 return (result.f);
1584 }
1585
1586 static inline void
1587 double_to_foreign_endianness (union scm_ieee754_double *target,
1588 double source)
1589 {
1590 union scm_ieee754_double src;
1591
1592 src.d = source;
1593
1594 #ifdef WORDS_BIGENDIAN
1595 /* Assuming little endian for both byte and word order. */
1596 target->little_little_endian.negative = src.big_endian.negative;
1597 target->little_little_endian.exponent = src.big_endian.exponent;
1598 target->little_little_endian.mantissa0 = src.big_endian.mantissa0;
1599 target->little_little_endian.mantissa1 = src.big_endian.mantissa1;
1600 #else
1601 target->big_endian.negative = src.little_little_endian.negative;
1602 target->big_endian.exponent = src.little_little_endian.exponent;
1603 target->big_endian.mantissa0 = src.little_little_endian.mantissa0;
1604 target->big_endian.mantissa1 = src.little_little_endian.mantissa1;
1605 #endif
1606 }
1607
1608 static inline double
1609 double_from_foreign_endianness (const union scm_ieee754_double *source)
1610 {
1611 union scm_ieee754_double result;
1612
1613 #ifdef WORDS_BIGENDIAN
1614 /* Assuming little endian for both byte and word order. */
1615 result.big_endian.negative = source->little_little_endian.negative;
1616 result.big_endian.exponent = source->little_little_endian.exponent;
1617 result.big_endian.mantissa0 = source->little_little_endian.mantissa0;
1618 result.big_endian.mantissa1 = source->little_little_endian.mantissa1;
1619 #else
1620 result.little_little_endian.negative = source->big_endian.negative;
1621 result.little_little_endian.exponent = source->big_endian.exponent;
1622 result.little_little_endian.mantissa0 = source->big_endian.mantissa0;
1623 result.little_little_endian.mantissa1 = source->big_endian.mantissa1;
1624 #endif
1625
1626 return (result.d);
1627 }
1628
1629 /* Template macros to abstract over doubles and floats.
1630 XXX: Guile can only convert to/from doubles. */
1631 #define IEEE754_UNION(_c_type) union scm_ieee754_ ## _c_type
1632 #define IEEE754_TO_SCM(_c_type) scm_from_double
1633 #define IEEE754_FROM_SCM(_c_type) scm_to_double
1634 #define IEEE754_FROM_FOREIGN_ENDIANNESS(_c_type) \
1635 _c_type ## _from_foreign_endianness
1636 #define IEEE754_TO_FOREIGN_ENDIANNESS(_c_type) \
1637 _c_type ## _to_foreign_endianness
1638
1639
1640 /* FIXME: SCM_VALIDATE_REAL rejects integers, etc. grrr */
1641 #define VALIDATE_REAL(pos, v) \
1642 do { \
1643 SCM_ASSERT_TYPE (scm_is_true (scm_rational_p (v)), v, pos, FUNC_NAME, "real"); \
1644 } while (0)
1645
1646 /* Templace getters and setters. */
1647
1648 #define IEEE754_ACCESSOR_PROLOGUE(_type) \
1649 INTEGER_ACCESSOR_PROLOGUE (sizeof (_type) << 3UL, signed);
1650
1651 #define IEEE754_REF(_type) \
1652 _type c_result; \
1653 \
1654 IEEE754_ACCESSOR_PROLOGUE (_type); \
1655 SCM_VALIDATE_SYMBOL (3, endianness); \
1656 \
1657 if (scm_is_eq (endianness, scm_i_native_endianness)) \
1658 memcpy (&c_result, &c_bv[c_index], sizeof (c_result)); \
1659 else \
1660 { \
1661 IEEE754_UNION (_type) c_raw; \
1662 \
1663 memcpy (&c_raw, &c_bv[c_index], sizeof (c_raw)); \
1664 c_result = \
1665 IEEE754_FROM_FOREIGN_ENDIANNESS (_type) (&c_raw); \
1666 } \
1667 \
1668 return (IEEE754_TO_SCM (_type) (c_result));
1669
1670 #define IEEE754_NATIVE_REF(_type) \
1671 _type c_result; \
1672 \
1673 IEEE754_ACCESSOR_PROLOGUE (_type); \
1674 \
1675 memcpy (&c_result, &c_bv[c_index], sizeof (c_result)); \
1676 return (IEEE754_TO_SCM (_type) (c_result));
1677
1678 #define IEEE754_SET(_type) \
1679 _type c_value; \
1680 \
1681 IEEE754_ACCESSOR_PROLOGUE (_type); \
1682 VALIDATE_REAL (3, value); \
1683 SCM_VALIDATE_SYMBOL (4, endianness); \
1684 c_value = IEEE754_FROM_SCM (_type) (value); \
1685 \
1686 if (scm_is_eq (endianness, scm_i_native_endianness)) \
1687 memcpy (&c_bv[c_index], &c_value, sizeof (c_value)); \
1688 else \
1689 { \
1690 IEEE754_UNION (_type) c_raw; \
1691 \
1692 IEEE754_TO_FOREIGN_ENDIANNESS (_type) (&c_raw, c_value); \
1693 memcpy (&c_bv[c_index], &c_raw, sizeof (c_raw)); \
1694 } \
1695 \
1696 return SCM_UNSPECIFIED;
1697
1698 #define IEEE754_NATIVE_SET(_type) \
1699 _type c_value; \
1700 \
1701 IEEE754_ACCESSOR_PROLOGUE (_type); \
1702 VALIDATE_REAL (3, value); \
1703 c_value = IEEE754_FROM_SCM (_type) (value); \
1704 \
1705 memcpy (&c_bv[c_index], &c_value, sizeof (c_value)); \
1706 return SCM_UNSPECIFIED;
1707
1708
1709 /* Single precision. */
1710
1711 SCM_DEFINE (scm_bytevector_ieee_single_ref,
1712 "bytevector-ieee-single-ref",
1713 3, 0, 0,
1714 (SCM bv, SCM index, SCM endianness),
1715 "Return the IEEE-754 single from @var{bv} at "
1716 "@var{index}.")
1717 #define FUNC_NAME s_scm_bytevector_ieee_single_ref
1718 {
1719 IEEE754_REF (float);
1720 }
1721 #undef FUNC_NAME
1722
1723 SCM_DEFINE (scm_bytevector_ieee_single_native_ref,
1724 "bytevector-ieee-single-native-ref",
1725 2, 0, 0,
1726 (SCM bv, SCM index),
1727 "Return the IEEE-754 single from @var{bv} at "
1728 "@var{index} using the native endianness.")
1729 #define FUNC_NAME s_scm_bytevector_ieee_single_native_ref
1730 {
1731 IEEE754_NATIVE_REF (float);
1732 }
1733 #undef FUNC_NAME
1734
1735 SCM_DEFINE (scm_bytevector_ieee_single_set_x,
1736 "bytevector-ieee-single-set!",
1737 4, 0, 0,
1738 (SCM bv, SCM index, SCM value, SCM endianness),
1739 "Store real @var{value} in @var{bv} at @var{index} according to "
1740 "@var{endianness}.")
1741 #define FUNC_NAME s_scm_bytevector_ieee_single_set_x
1742 {
1743 IEEE754_SET (float);
1744 }
1745 #undef FUNC_NAME
1746
1747 SCM_DEFINE (scm_bytevector_ieee_single_native_set_x,
1748 "bytevector-ieee-single-native-set!",
1749 3, 0, 0,
1750 (SCM bv, SCM index, SCM value),
1751 "Store the real @var{value} at index @var{index} "
1752 "of @var{bv} using the native endianness.")
1753 #define FUNC_NAME s_scm_bytevector_ieee_single_native_set_x
1754 {
1755 IEEE754_NATIVE_SET (float);
1756 }
1757 #undef FUNC_NAME
1758
1759
1760 /* Double precision. */
1761
1762 SCM_DEFINE (scm_bytevector_ieee_double_ref,
1763 "bytevector-ieee-double-ref",
1764 3, 0, 0,
1765 (SCM bv, SCM index, SCM endianness),
1766 "Return the IEEE-754 double from @var{bv} at "
1767 "@var{index}.")
1768 #define FUNC_NAME s_scm_bytevector_ieee_double_ref
1769 {
1770 IEEE754_REF (double);
1771 }
1772 #undef FUNC_NAME
1773
1774 SCM_DEFINE (scm_bytevector_ieee_double_native_ref,
1775 "bytevector-ieee-double-native-ref",
1776 2, 0, 0,
1777 (SCM bv, SCM index),
1778 "Return the IEEE-754 double from @var{bv} at "
1779 "@var{index} using the native endianness.")
1780 #define FUNC_NAME s_scm_bytevector_ieee_double_native_ref
1781 {
1782 IEEE754_NATIVE_REF (double);
1783 }
1784 #undef FUNC_NAME
1785
1786 SCM_DEFINE (scm_bytevector_ieee_double_set_x,
1787 "bytevector-ieee-double-set!",
1788 4, 0, 0,
1789 (SCM bv, SCM index, SCM value, SCM endianness),
1790 "Store real @var{value} in @var{bv} at @var{index} according to "
1791 "@var{endianness}.")
1792 #define FUNC_NAME s_scm_bytevector_ieee_double_set_x
1793 {
1794 IEEE754_SET (double);
1795 }
1796 #undef FUNC_NAME
1797
1798 SCM_DEFINE (scm_bytevector_ieee_double_native_set_x,
1799 "bytevector-ieee-double-native-set!",
1800 3, 0, 0,
1801 (SCM bv, SCM index, SCM value),
1802 "Store the real @var{value} at index @var{index} "
1803 "of @var{bv} using the native endianness.")
1804 #define FUNC_NAME s_scm_bytevector_ieee_double_native_set_x
1805 {
1806 IEEE754_NATIVE_SET (double);
1807 }
1808 #undef FUNC_NAME
1809
1810
1811 #undef IEEE754_UNION
1812 #undef IEEE754_TO_SCM
1813 #undef IEEE754_FROM_SCM
1814 #undef IEEE754_FROM_FOREIGN_ENDIANNESS
1815 #undef IEEE754_TO_FOREIGN_ENDIANNESS
1816 #undef IEEE754_REF
1817 #undef IEEE754_NATIVE_REF
1818 #undef IEEE754_SET
1819 #undef IEEE754_NATIVE_SET
1820
1821 \f
1822 /* Operations on strings. */
1823
1824
1825 /* Produce a function that returns the length of a UTF-encoded string. */
1826 #define UTF_STRLEN_FUNCTION(_utf_width) \
1827 static inline size_t \
1828 utf ## _utf_width ## _strlen (const uint ## _utf_width ## _t *str) \
1829 { \
1830 size_t len = 0; \
1831 const uint ## _utf_width ## _t *ptr; \
1832 for (ptr = str; \
1833 *ptr != 0; \
1834 ptr++) \
1835 { \
1836 len++; \
1837 } \
1838 \
1839 return (len * ((_utf_width) / 8)); \
1840 }
1841
1842 UTF_STRLEN_FUNCTION (8)
1843
1844
1845 /* Return the length (in bytes) of STR, a UTF-(UTF_WIDTH) encoded string. */
1846 #define UTF_STRLEN(_utf_width, _str) \
1847 utf ## _utf_width ## _strlen (_str)
1848
1849 /* Return the "portable" name of the UTF encoding of size UTF_WIDTH and
1850 ENDIANNESS (Gnulib's `iconv_open' module guarantees the portability of the
1851 encoding name). */
1852 static inline void
1853 utf_encoding_name (char *name, size_t utf_width, SCM endianness)
1854 {
1855 strcpy (name, "UTF-");
1856 strcat (name, ((utf_width == 8)
1857 ? "8"
1858 : ((utf_width == 16)
1859 ? "16"
1860 : ((utf_width == 32)
1861 ? "32"
1862 : "??"))));
1863 strcat (name,
1864 ((scm_is_eq (endianness, scm_sym_big))
1865 ? "BE"
1866 : ((scm_is_eq (endianness, scm_sym_little))
1867 ? "LE"
1868 : "unknown")));
1869 }
1870
1871 /* Maximum length of a UTF encoding name. */
1872 #define MAX_UTF_ENCODING_NAME_LEN 16
1873
1874 /* Produce the body of a `string->utf' function. */
1875 #define STRING_TO_UTF(_utf_width) \
1876 SCM utf; \
1877 int err; \
1878 char c_utf_name[MAX_UTF_ENCODING_NAME_LEN]; \
1879 char *c_utf = NULL; \
1880 size_t c_strlen, c_utf_len = 0; \
1881 \
1882 SCM_VALIDATE_STRING (1, str); \
1883 if (endianness == SCM_UNDEFINED) \
1884 endianness = scm_sym_big; \
1885 else \
1886 SCM_VALIDATE_SYMBOL (2, endianness); \
1887 \
1888 utf_encoding_name (c_utf_name, (_utf_width), endianness); \
1889 \
1890 c_strlen = scm_i_string_length (str); \
1891 if (scm_i_is_narrow_string (str)) \
1892 { \
1893 err = mem_iconveh (scm_i_string_chars (str), c_strlen, \
1894 "ISO-8859-1", c_utf_name, \
1895 iconveh_question_mark, NULL, \
1896 &c_utf, &c_utf_len); \
1897 if (SCM_UNLIKELY (err)) \
1898 scm_syserror_msg (FUNC_NAME, "failed to convert string: ~A", \
1899 scm_list_1 (str), err); \
1900 } \
1901 else \
1902 { \
1903 const scm_t_wchar *wbuf = scm_i_string_wide_chars (str); \
1904 c_utf = u32_conv_to_encoding (c_utf_name, \
1905 iconveh_question_mark, \
1906 (scm_t_uint32 *) wbuf, \
1907 c_strlen, NULL, NULL, &c_utf_len); \
1908 if (SCM_UNLIKELY (c_utf == NULL)) \
1909 scm_syserror_msg (FUNC_NAME, "failed to convert string: ~A", \
1910 scm_list_1 (str), errno); \
1911 } \
1912 scm_dynwind_begin (0); \
1913 scm_dynwind_free (c_utf); \
1914 utf = make_bytevector (c_utf_len, SCM_ARRAY_ELEMENT_TYPE_VU8); \
1915 memcpy (SCM_BYTEVECTOR_CONTENTS (utf), c_utf, c_utf_len); \
1916 scm_dynwind_end (); \
1917 \
1918 return (utf);
1919
1920
1921
1922 SCM_DEFINE (scm_string_to_utf8, "string->utf8",
1923 1, 0, 0,
1924 (SCM str),
1925 "Return a newly allocated bytevector that contains the UTF-8 "
1926 "encoding of @var{str}.")
1927 #define FUNC_NAME s_scm_string_to_utf8
1928 {
1929 SCM utf;
1930 uint8_t *c_utf;
1931 size_t c_strlen, c_utf_len = 0;
1932
1933 SCM_VALIDATE_STRING (1, str);
1934
1935 c_strlen = scm_i_string_length (str);
1936 if (scm_i_is_narrow_string (str))
1937 c_utf = u8_conv_from_encoding ("ISO-8859-1", iconveh_question_mark,
1938 scm_i_string_chars (str), c_strlen,
1939 NULL, NULL, &c_utf_len);
1940 else
1941 {
1942 const scm_t_wchar *wbuf = scm_i_string_wide_chars (str);
1943 c_utf = u32_to_u8 ((const uint32_t *) wbuf, c_strlen, NULL, &c_utf_len);
1944 }
1945 if (SCM_UNLIKELY (c_utf == NULL))
1946 scm_syserror (FUNC_NAME);
1947 else
1948 {
1949 scm_dynwind_begin (0);
1950 scm_dynwind_free (c_utf);
1951
1952 utf = make_bytevector (c_utf_len, SCM_ARRAY_ELEMENT_TYPE_VU8);
1953 memcpy (SCM_BYTEVECTOR_CONTENTS (utf), c_utf, c_utf_len);
1954
1955 scm_dynwind_end ();
1956 }
1957
1958 return (utf);
1959 }
1960 #undef FUNC_NAME
1961
1962 SCM_DEFINE (scm_string_to_utf16, "string->utf16",
1963 1, 1, 0,
1964 (SCM str, SCM endianness),
1965 "Return a newly allocated bytevector that contains the UTF-16 "
1966 "encoding of @var{str}.")
1967 #define FUNC_NAME s_scm_string_to_utf16
1968 {
1969 STRING_TO_UTF (16);
1970 }
1971 #undef FUNC_NAME
1972
1973 SCM_DEFINE (scm_string_to_utf32, "string->utf32",
1974 1, 1, 0,
1975 (SCM str, SCM endianness),
1976 "Return a newly allocated bytevector that contains the UTF-32 "
1977 "encoding of @var{str}.")
1978 #define FUNC_NAME s_scm_string_to_utf32
1979 {
1980 STRING_TO_UTF (32);
1981 }
1982 #undef FUNC_NAME
1983
1984
1985 /* Produce the body of a function that converts a UTF-encoded bytevector to a
1986 string. */
1987 #define UTF_TO_STRING(_utf_width) \
1988 SCM str = SCM_BOOL_F; \
1989 int err; \
1990 char *c_str = NULL; \
1991 char c_utf_name[MAX_UTF_ENCODING_NAME_LEN]; \
1992 char *c_utf; \
1993 size_t c_strlen = 0, c_utf_len = 0; \
1994 \
1995 SCM_VALIDATE_BYTEVECTOR (1, utf); \
1996 if (endianness == SCM_UNDEFINED) \
1997 endianness = scm_sym_big; \
1998 else \
1999 SCM_VALIDATE_SYMBOL (2, endianness); \
2000 \
2001 c_utf_len = SCM_BYTEVECTOR_LENGTH (utf); \
2002 c_utf = (char *) SCM_BYTEVECTOR_CONTENTS (utf); \
2003 utf_encoding_name (c_utf_name, (_utf_width), endianness); \
2004 \
2005 err = mem_iconveh (c_utf, c_utf_len, \
2006 c_utf_name, "UTF-8", \
2007 iconveh_question_mark, NULL, \
2008 &c_str, &c_strlen); \
2009 if (SCM_UNLIKELY (err)) \
2010 scm_syserror_msg (FUNC_NAME, "failed to convert to string: ~A", \
2011 scm_list_1 (utf), err); \
2012 else \
2013 { \
2014 str = scm_from_stringn (c_str, c_strlen, "UTF-8", \
2015 SCM_FAILED_CONVERSION_ERROR); \
2016 free (c_str); \
2017 } \
2018 return (str);
2019
2020
2021 SCM_DEFINE (scm_utf8_to_string, "utf8->string",
2022 1, 0, 0,
2023 (SCM utf),
2024 "Return a newly allocate string that contains from the UTF-8-"
2025 "encoded contents of bytevector @var{utf}.")
2026 #define FUNC_NAME s_scm_utf8_to_string
2027 {
2028 SCM str;
2029 const char *c_utf;
2030 size_t c_utf_len = 0;
2031
2032 SCM_VALIDATE_BYTEVECTOR (1, utf);
2033
2034 c_utf_len = SCM_BYTEVECTOR_LENGTH (utf);
2035 c_utf = (char *) SCM_BYTEVECTOR_CONTENTS (utf);
2036 str = scm_from_stringn (c_utf, c_utf_len, "UTF-8",
2037 SCM_FAILED_CONVERSION_ERROR);
2038
2039 return (str);
2040 }
2041 #undef FUNC_NAME
2042
2043 SCM_DEFINE (scm_utf16_to_string, "utf16->string",
2044 1, 1, 0,
2045 (SCM utf, SCM endianness),
2046 "Return a newly allocate string that contains from the UTF-16-"
2047 "encoded contents of bytevector @var{utf}.")
2048 #define FUNC_NAME s_scm_utf16_to_string
2049 {
2050 UTF_TO_STRING (16);
2051 }
2052 #undef FUNC_NAME
2053
2054 SCM_DEFINE (scm_utf32_to_string, "utf32->string",
2055 1, 1, 0,
2056 (SCM utf, SCM endianness),
2057 "Return a newly allocate string that contains from the UTF-32-"
2058 "encoded contents of bytevector @var{utf}.")
2059 #define FUNC_NAME s_scm_utf32_to_string
2060 {
2061 UTF_TO_STRING (32);
2062 }
2063 #undef FUNC_NAME
2064
2065 \f
2066 /* Bytevectors as generalized vectors & arrays. */
2067
2068
2069 static SCM
2070 bytevector_ref_c32 (SCM bv, SCM idx)
2071 { /* FIXME add some checks */
2072 const float *contents = (const float*)SCM_BYTEVECTOR_CONTENTS (bv);
2073 size_t i = scm_to_size_t (idx);
2074 return scm_c_make_rectangular (contents[i/8], contents[i/8 + 1]);
2075 }
2076
2077 static SCM
2078 bytevector_ref_c64 (SCM bv, SCM idx)
2079 { /* FIXME add some checks */
2080 const double *contents = (const double*)SCM_BYTEVECTOR_CONTENTS (bv);
2081 size_t i = scm_to_size_t (idx);
2082 return scm_c_make_rectangular (contents[i/16], contents[i/16 + 1]);
2083 }
2084
2085 typedef SCM (*scm_t_bytevector_ref_fn)(SCM, SCM);
2086
2087 const scm_t_bytevector_ref_fn bytevector_ref_fns[SCM_ARRAY_ELEMENT_TYPE_LAST + 1] =
2088 {
2089 NULL, /* SCM */
2090 NULL, /* CHAR */
2091 NULL, /* BIT */
2092 scm_bytevector_u8_ref, /* VU8 */
2093 scm_bytevector_u8_ref, /* U8 */
2094 scm_bytevector_s8_ref,
2095 scm_bytevector_u16_native_ref,
2096 scm_bytevector_s16_native_ref,
2097 scm_bytevector_u32_native_ref,
2098 scm_bytevector_s32_native_ref,
2099 scm_bytevector_u64_native_ref,
2100 scm_bytevector_s64_native_ref,
2101 scm_bytevector_ieee_single_native_ref,
2102 scm_bytevector_ieee_double_native_ref,
2103 bytevector_ref_c32,
2104 bytevector_ref_c64
2105 };
2106
2107 static SCM
2108 bv_handle_ref (scm_t_array_handle *h, size_t index)
2109 {
2110 SCM byte_index;
2111 scm_t_bytevector_ref_fn ref_fn;
2112
2113 ref_fn = bytevector_ref_fns[h->element_type];
2114 byte_index =
2115 scm_from_size_t (index * scm_array_handle_uniform_element_size (h));
2116 return ref_fn (h->array, byte_index);
2117 }
2118
2119 static SCM
2120 bytevector_set_c32 (SCM bv, SCM idx, SCM val)
2121 { /* checks are unnecessary here */
2122 float *contents = (float*)SCM_BYTEVECTOR_CONTENTS (bv);
2123 size_t i = scm_to_size_t (idx);
2124 contents[i/8] = scm_c_real_part (val);
2125 contents[i/8 + 1] = scm_c_imag_part (val);
2126 return SCM_UNSPECIFIED;
2127 }
2128
2129 static SCM
2130 bytevector_set_c64 (SCM bv, SCM idx, SCM val)
2131 { /* checks are unnecessary here */
2132 double *contents = (double*)SCM_BYTEVECTOR_CONTENTS (bv);
2133 size_t i = scm_to_size_t (idx);
2134 contents[i/16] = scm_c_real_part (val);
2135 contents[i/16 + 1] = scm_c_imag_part (val);
2136 return SCM_UNSPECIFIED;
2137 }
2138
2139 typedef SCM (*scm_t_bytevector_set_fn)(SCM, SCM, SCM);
2140
2141 const scm_t_bytevector_set_fn bytevector_set_fns[SCM_ARRAY_ELEMENT_TYPE_LAST + 1] =
2142 {
2143 NULL, /* SCM */
2144 NULL, /* CHAR */
2145 NULL, /* BIT */
2146 scm_bytevector_u8_set_x, /* VU8 */
2147 scm_bytevector_u8_set_x, /* U8 */
2148 scm_bytevector_s8_set_x,
2149 scm_bytevector_u16_native_set_x,
2150 scm_bytevector_s16_native_set_x,
2151 scm_bytevector_u32_native_set_x,
2152 scm_bytevector_s32_native_set_x,
2153 scm_bytevector_u64_native_set_x,
2154 scm_bytevector_s64_native_set_x,
2155 scm_bytevector_ieee_single_native_set_x,
2156 scm_bytevector_ieee_double_native_set_x,
2157 bytevector_set_c32,
2158 bytevector_set_c64
2159 };
2160
2161 static void
2162 bv_handle_set_x (scm_t_array_handle *h, size_t index, SCM val)
2163 {
2164 SCM byte_index;
2165 scm_t_bytevector_set_fn set_fn;
2166
2167 set_fn = bytevector_set_fns[h->element_type];
2168 byte_index =
2169 scm_from_size_t (index * scm_array_handle_uniform_element_size (h));
2170 set_fn (h->array, byte_index, val);
2171 }
2172
2173 static void
2174 bytevector_get_handle (SCM v, scm_t_array_handle *h)
2175 {
2176 h->array = v;
2177 h->ndims = 1;
2178 h->dims = &h->dim0;
2179 h->dim0.lbnd = 0;
2180 h->dim0.ubnd = SCM_BYTEVECTOR_TYPED_LENGTH (v) - 1;
2181 h->dim0.inc = 1;
2182 h->element_type = SCM_BYTEVECTOR_ELEMENT_TYPE (v);
2183 h->elements = h->writable_elements = SCM_BYTEVECTOR_CONTENTS (v);
2184 }
2185
2186 \f
2187 /* Initialization. */
2188
2189 void
2190 scm_bootstrap_bytevectors (void)
2191 {
2192 /* This must be instantiated here because the generalized-vector API may
2193 want to access bytevectors even though `(rnrs bytevector)' hasn't been
2194 loaded. */
2195 scm_null_bytevector =
2196 scm_gc_protect_object (make_bytevector (0, SCM_ARRAY_ELEMENT_TYPE_VU8));
2197
2198 #ifdef WORDS_BIGENDIAN
2199 scm_i_native_endianness = scm_permanent_object (scm_from_locale_symbol ("big"));
2200 #else
2201 scm_i_native_endianness = scm_permanent_object (scm_from_locale_symbol ("little"));
2202 #endif
2203
2204 scm_c_register_extension ("libguile", "scm_init_bytevectors",
2205 (scm_t_extension_init_func) scm_init_bytevectors,
2206 NULL);
2207
2208 {
2209 scm_t_array_implementation impl;
2210
2211 impl.tag = scm_tc7_bytevector;
2212 impl.mask = 0x7f;
2213 impl.vref = bv_handle_ref;
2214 impl.vset = bv_handle_set_x;
2215 impl.get_handle = bytevector_get_handle;
2216 scm_i_register_array_implementation (&impl);
2217 scm_i_register_vector_constructor
2218 (scm_i_array_element_types[SCM_ARRAY_ELEMENT_TYPE_VU8],
2219 scm_make_bytevector);
2220 }
2221 }
2222
2223 void
2224 scm_init_bytevectors (void)
2225 {
2226 #include "libguile/bytevectors.x"
2227
2228 scm_endianness_big = scm_sym_big;
2229 scm_endianness_little = scm_sym_little;
2230 }