Section 2.4: Operations on 16-bit Integers.
[guile-r6rs-libs.git] / src / bytevector.c
bloba6fb352e5f8369b19a553ea4eb714a90571b4087
1 /* Guile-R6RS-Libs --- Implementation of R6RS standard libraries.
2 Copyright (C) 2007 Ludovic Courtès <ludovic.courtes@laas.fr>
4 Guile-R6RS-Libs is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
9 Guile-R6RS-Libs is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with Guile-R6RS-Libs; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
18 #include "config.h"
20 #include <libguile.h>
21 #include <gmp.h>
23 #include <byteswap.h>
25 #ifdef HAVE_LIMITS_H
26 # include <limits.h>
27 #else
28 /* Assuming 32-bit longs. */
29 # define ULONG_MAX 4294967295UL
30 #endif
32 #include <string.h>
35 /* Utilities. */
37 #ifdef __GNUC__
38 # define EXPECT __builtin_expect
39 #else
40 # define EXPECT(_expr, _value) (_expr)
41 #endif
43 #define EXPECT_TRUE(_expr) EXPECT ((_expr), 1)
44 #define EXPECT_FALSE(_expr) EXPECT ((_expr), 0)
46 /* Convenience macros. These are used by the various templates (macros) that
47 are parameterized by integer signedness. */
48 #define INT8_T_signed scm_t_int8
49 #define INT8_T_unsigned scm_t_uint8
50 #define INT16_T_signed scm_t_int16
51 #define INT16_T_unsigned scm_t_uint16
52 #define is_signed_int8(_x) (((_x) >= -128L) && ((_x) <= 127L))
53 #define is_unsigned_int8(_x) ((_x) <= 255UL)
54 #define is_signed_int16(_x) (((_x) >= -32768L) && ((_x) <= 32767L))
55 #define is_unsigned_int16(_x) ((_x) <= 65535UL)
57 #define INT_TYPE(_size, _sign) INT ## _size ## _T_ ## _sign
58 #define INT_SWAP(_size) bswap_ ## _size
59 #define INT_VALID_P(_size, _sign) is_ ## _sign ## _int ## _size
62 #define INTEGER_ACCESSOR_PROLOGUE(_len, _sign) \
63 unsigned c_len, c_index; \
64 _sign char *c_bv; \
66 SCM_VALIDATE_R6RS_BYTEVECTOR (1, bv); \
67 c_index = scm_to_uint (index); \
69 c_len = SCM_R6RS_BYTEVECTOR_LENGTH (bv); \
70 c_bv = (_sign char *) SCM_R6RS_BYTEVECTOR_CONTENTS (bv); \
72 if (EXPECT_FALSE (c_index + ((_len) >> 3UL) - 1 >= c_len)) \
73 scm_out_of_range (FUNC_NAME, index);
75 /* Template for fixed-size integer access (only 8, 16 or 32-bit). */
76 #define INTEGER_REF(_len, _sign) \
77 SCM result; \
79 INTEGER_ACCESSOR_PROLOGUE (_len, _sign); \
80 SCM_VALIDATE_SYMBOL (3, endianness); \
82 { \
83 INT_TYPE (_len, _sign) c_result; \
85 c_result = * (INT_TYPE (_len, _sign) *) &c_bv[c_index]; \
86 if (!scm_is_eq (endianness, native_endianness)) \
87 c_result = INT_SWAP (_len) (c_result); \
89 result = SCM_I_MAKINUM (c_result); \
90 } \
92 return result;
94 /* Template for fixed-size integer access using the native endianness. */
95 #define INTEGER_NATIVE_REF(_len, _sign) \
96 SCM result; \
98 INTEGER_ACCESSOR_PROLOGUE (_len, _sign); \
101 INT_TYPE (_len, _sign) c_result; \
103 c_result = * (INT_TYPE (_len, _sign) *) &c_bv[c_index]; \
104 result = SCM_I_MAKINUM (c_result); \
107 return result;
109 /* Template for fixed-size integer modification (only 8, 16 or 32-bit). */
110 #define INTEGER_SET(_len, _sign) \
111 INTEGER_ACCESSOR_PROLOGUE (_len, _sign); \
112 SCM_VALIDATE_SYMBOL (3, endianness); \
115 _sign long c_value; \
116 INT_TYPE (_len, _sign) c_value_short; \
118 if (EXPECT_FALSE (!SCM_I_INUMP (value))) \
119 scm_wrong_type_arg (FUNC_NAME, 3, value); \
121 c_value = SCM_I_INUM (value); \
122 if (EXPECT_FALSE (!INT_VALID_P (_len, _sign) (c_value))) \
123 scm_out_of_range (FUNC_NAME, value); \
125 c_value_short = (INT_TYPE (_len, _sign)) c_value; \
126 if (!scm_is_eq (endianness, native_endianness)) \
127 c_value_short = INT_SWAP (_len) (c_value_short); \
129 * (INT_TYPE (_len, _sign) *) &c_bv[c_index] = c_value_short; \
132 return SCM_UNSPECIFIED;
134 /* Template for fixed-size integer modification using the native
135 endianness. */
136 #define INTEGER_NATIVE_SET(_len, _sign) \
137 INTEGER_ACCESSOR_PROLOGUE (_len, _sign); \
140 _sign long c_value; \
141 INT_TYPE (_len, _sign) c_value_short; \
143 if (EXPECT_FALSE (!SCM_I_INUMP (value))) \
144 scm_wrong_type_arg (FUNC_NAME, 3, value); \
146 c_value = SCM_I_INUM (value); \
147 if (EXPECT_FALSE (!INT_VALID_P (_len, _sign) (c_value))) \
148 scm_out_of_range (FUNC_NAME, value); \
150 c_value_short = (INT_TYPE (_len, _sign)) c_value; \
152 * (INT_TYPE (_len, _sign) *) &c_bv[c_index] = c_value_short; \
155 return SCM_UNSPECIFIED;
159 /* Bytevector type. */
161 SCM_SMOB (scm_tc16_r6rs_bytevector, "r6rs-bytevector", 0);
163 #define SCM_VALIDATE_R6RS_BYTEVECTOR(_pos, _obj) \
164 SCM_VALIDATE_SMOB ((_pos), (_obj), r6rs_bytevector);
167 static inline SCM
168 make_bytevector (unsigned len, signed char *contents)
170 SCM_RETURN_NEWSMOB2 (scm_tc16_r6rs_bytevector, len, contents);
173 #define SCM_R6RS_BYTEVECTOR_LENGTH(_bv) \
174 ((unsigned) SCM_SMOB_DATA (_bv))
175 #define SCM_R6RS_BYTEVECTOR_CONTENTS(_bv) \
176 ((signed char *) SCM_SMOB_DATA_2 (_bv))
178 #define SCM_GC_BYTEVECTOR "r6rs-bytevector"
180 SCM_SMOB_FREE (scm_tc16_r6rs_bytevector, free_bytevector, bv)
182 unsigned c_len;
183 signed char *c_bv;
185 c_len = SCM_R6RS_BYTEVECTOR_LENGTH (bv);
186 c_bv = SCM_R6RS_BYTEVECTOR_CONTENTS (bv);
188 scm_gc_free (c_bv, c_len, SCM_GC_BYTEVECTOR);
190 return 0;
195 /* General operations. */
197 SCM_SYMBOL (scm_sym_big, "big");
198 SCM_SYMBOL (scm_sym_little, "little");
200 /* Host endianness (a symbol). */
201 static SCM native_endianness = SCM_UNSPECIFIED;
203 /* Byte-swapping. */
204 #ifndef bswap_24
205 # define bswap_24(_x) \
206 ((((_x) & 0xff0000) >> 16) | \
207 (((_x) & 0x00ff00)) | \
208 (((_x) & 0x0000ff) << 16))
209 #endif
212 SCM_DEFINE (scm_r6rs_native_endianness, "native-endianness", 0, 0, 0,
213 (void),
214 "Return a symbol denoting the machine's native endianness.")
215 #define FUNC_NAME s_scm_r6rs_native_endianness
217 return native_endianness;
219 #undef FUNC_NAME
221 SCM_DEFINE (scm_r6rs_bytevector_p, "bytevector?", 1, 0, 0,
222 (SCM obj),
223 "Return true if @var{obj} is a bytevector.")
224 #define FUNC_NAME s_scm_r6rs_bytevector_p
226 return (scm_from_bool (SCM_SMOB_PREDICATE (scm_tc16_r6rs_bytevector,
227 obj)));
229 #undef FUNC_NAME
231 SCM_DEFINE (scm_r6rs_make_bytevector, "make-bytevector", 1, 1, 0,
232 (SCM len, SCM fill),
233 "Return a newly allocated bytevector of @var{len} bytes, "
234 "optionally filled with @var{fill}.")
235 #define FUNC_NAME s_scm_r6rs_make_bytevector
237 unsigned c_len;
238 signed char c_fill;
239 signed char *contents;
241 SCM_VALIDATE_UINT_COPY (1, len, c_len);
242 if (fill != SCM_UNDEFINED)
244 int value;
246 value = scm_to_int (fill);
247 if (EXPECT_FALSE ((value < -128) || (value > 255)))
248 scm_out_of_range (FUNC_NAME, fill);
249 c_fill = (signed char) value;
252 contents = (signed char *) scm_gc_malloc (c_len, SCM_GC_BYTEVECTOR);
253 if (fill != SCM_UNDEFINED)
255 unsigned i;
257 for (i = 0; i < c_len; i++)
258 contents[i] = c_fill;
261 return (make_bytevector (c_len, contents));
263 #undef FUNC_NAME
265 SCM_DEFINE (scm_r6rs_bytevector_length, "bytevector-length", 1, 0, 0,
266 (SCM bv),
267 "Return the length (in bytes) of @var{bv}.")
268 #define FUNC_NAME s_scm_r6rs_bytevector_length
270 SCM_VALIDATE_R6RS_BYTEVECTOR (1, bv);
272 return (scm_from_uint (SCM_R6RS_BYTEVECTOR_LENGTH (bv)));
274 #undef FUNC_NAME
276 SCM_DEFINE (scm_r6rs_bytevector_eq_p, "bytevector=?", 2, 0, 0,
277 (SCM bv1, SCM bv2),
278 "Return is @var{bv1} equals to @var{bv2}---i.e., if they "
279 "have the same length and contents.")
280 #define FUNC_NAME s_scm_r6rs_bytevector_eq_p
282 SCM result = SCM_BOOL_F;
283 unsigned c_len1, c_len2;
285 SCM_VALIDATE_R6RS_BYTEVECTOR (1, bv1);
286 SCM_VALIDATE_R6RS_BYTEVECTOR (2, bv2);
288 c_len1 = SCM_R6RS_BYTEVECTOR_LENGTH (bv1);
289 c_len2 = SCM_R6RS_BYTEVECTOR_LENGTH (bv2);
291 if (c_len1 == c_len2)
293 signed char *c_bv1, *c_bv2;
295 c_bv1 = SCM_R6RS_BYTEVECTOR_CONTENTS (bv1);
296 c_bv2 = SCM_R6RS_BYTEVECTOR_CONTENTS (bv2);
298 result = scm_from_bool (!memcmp (c_bv1, c_bv2, c_len1));
301 return result;
303 #undef FUNC_NAME
305 SCM_DEFINE (scm_r6rs_bytevector_fill_x, "bytevector-fill!", 2, 0, 0,
306 (SCM bv, SCM fill),
307 "Fill bytevector @var{bv} with @var{fill}, a byte.")
308 #define FUNC_NAME s_scm_r6rs_bytevector_fill_x
310 unsigned c_len, i;
311 signed char *c_bv, c_fill;
313 SCM_VALIDATE_R6RS_BYTEVECTOR (1, bv);
314 c_fill = scm_to_int8 (fill);
316 c_len = SCM_R6RS_BYTEVECTOR_LENGTH (bv);
317 c_bv = SCM_R6RS_BYTEVECTOR_CONTENTS (bv);
319 for (i = 0; i < c_len; i++)
320 c_bv[i] = c_fill;
322 return SCM_UNSPECIFIED;
324 #undef FUNC_NAME
326 SCM_DEFINE (scm_r6rs_bytevector_copy_x, "bytevector-copy!", 5, 0, 0,
327 (SCM source, SCM source_start, SCM target, SCM target_start,
328 SCM len),
329 "Copy @var{len} bytes from @var{source} into @var{target}, "
330 "starting reading from @var{source_start} (a positive index "
331 "within @var{source}) and start writing at "
332 "@var{target_start}.")
333 #define FUNC_NAME s_scm_r6rs_bytevector_copy_x
335 unsigned c_len, c_source_len, c_target_len;
336 unsigned c_source_start, c_target_start;
337 signed char *c_source, *c_target;
339 SCM_VALIDATE_R6RS_BYTEVECTOR (1, source);
340 SCM_VALIDATE_R6RS_BYTEVECTOR (3, target);
342 c_len = scm_to_uint (len);
343 c_source_start = scm_to_uint (source_start);
344 c_target_start = scm_to_uint (target_start);
346 c_source = SCM_R6RS_BYTEVECTOR_CONTENTS (source);
347 c_target = SCM_R6RS_BYTEVECTOR_CONTENTS (target);
348 c_source_len = SCM_R6RS_BYTEVECTOR_LENGTH (source);
349 c_target_len = SCM_R6RS_BYTEVECTOR_LENGTH (target);
351 if (EXPECT_FALSE (c_source_start + c_len > c_source_len))
352 scm_out_of_range (FUNC_NAME, source_start);
353 if (EXPECT_FALSE (c_target_start + c_len > c_target_len))
354 scm_out_of_range (FUNC_NAME, target_start);
356 memcpy (c_target + c_target_start,
357 c_source + c_source_start,
358 c_len);
360 return SCM_UNSPECIFIED;
362 #undef FUNC_NAME
364 SCM_DEFINE (scm_r6rs_bytevector_copy, "bytevector-copy", 1, 0, 0,
365 (SCM bv),
366 "Return a newly allocated copy of @var{bv}.")
367 #define FUNC_NAME s_scm_r6rs_bytevector_copy
369 unsigned c_len;
370 signed char *c_bv, *c_copy;
372 SCM_VALIDATE_R6RS_BYTEVECTOR (1, bv);
374 c_len = SCM_R6RS_BYTEVECTOR_LENGTH (bv);
375 c_bv = SCM_R6RS_BYTEVECTOR_CONTENTS (bv);
377 c_copy = scm_gc_malloc (c_len, SCM_GC_BYTEVECTOR);
378 memcpy (c_copy, c_bv, c_len);
380 return (make_bytevector (c_len, c_copy));
382 #undef FUNC_NAME
385 /* Operations on bytes and octets. */
387 SCM_DEFINE (scm_r6rs_bytevector_u8_ref, "bytevector-u8-ref", 2, 0, 0,
388 (SCM bv, SCM index),
389 "Return the octet located at @var{index} in @var{bv}.")
390 #define FUNC_NAME s_scm_r6rs_bytevector_u8_ref
392 INTEGER_NATIVE_REF (8, unsigned);
394 #undef FUNC_NAME
396 SCM_DEFINE (scm_r6rs_bytevector_s8_ref, "bytevector-s8-ref", 2, 0, 0,
397 (SCM bv, SCM index),
398 "Return the byte located at @var{index} in @var{bv}.")
399 #define FUNC_NAME s_scm_r6rs_bytevector_u8_ref
401 INTEGER_NATIVE_REF (8, signed);
403 #undef FUNC_NAME
405 SCM_DEFINE (scm_r6rs_bytevector_u8_set_x, "bytevector-u8-set!", 3, 0, 0,
406 (SCM bv, SCM index, SCM value),
407 "Return the octet located at @var{index} in @var{bv}.")
408 #define FUNC_NAME s_scm_r6rs_bytevector_u8_set_x
410 INTEGER_NATIVE_SET (8, unsigned);
412 #undef FUNC_NAME
414 SCM_DEFINE (scm_r6rs_bytevector_s8_set_x, "bytevector-s8-set!", 3, 0, 0,
415 (SCM bv, SCM index, SCM value),
416 "Return the octet located at @var{index} in @var{bv}.")
417 #define FUNC_NAME s_scm_r6rs_bytevector_u8_set_x
419 INTEGER_NATIVE_SET (8, signed);
421 #undef FUNC_NAME
423 #undef OCTET_ACCESSOR_PROLOGUE
426 SCM_DEFINE (scm_r6rs_bytevector_to_u8_list, "bytevector->u8-list", 1, 0, 0,
427 (SCM bv),
428 "Return a newly allocated list of octets containing the "
429 "contents of @var{bv}.")
430 #define FUNC_NAME s_scm_r6rs_bytevector_to_u8_list
432 SCM lst, pair;
433 unsigned c_len, i;
434 unsigned char *c_bv;
436 SCM_VALIDATE_R6RS_BYTEVECTOR (1, bv);
438 c_len = SCM_R6RS_BYTEVECTOR_LENGTH (bv);
439 c_bv = (unsigned char *) SCM_R6RS_BYTEVECTOR_CONTENTS (bv);
441 lst = scm_make_list (scm_from_uint (c_len), SCM_UNSPECIFIED);
442 for (i = 0, pair = lst;
443 i < c_len;
444 i++, pair = SCM_CDR (pair))
446 SCM_SETCAR (pair, SCM_I_MAKINUM (c_bv[i]));
449 return lst;
451 #undef FUNC_NAME
453 SCM_DEFINE (scm_r6rs_u8_list_to_bytevector, "u8-list->bytevector", 1, 0, 0,
454 (SCM lst),
455 "Turn @var{lst}, a list of octets, into a bytevector.")
456 #define FUNC_NAME s_scm_r6rs_u8_list_to_bytevector
458 SCM item;
459 unsigned c_len, i;
460 unsigned char *c_bv;
462 SCM_VALIDATE_LIST_COPYLEN (1, lst, c_len);
464 c_bv = scm_gc_malloc (c_len, SCM_GC_BYTEVECTOR);
465 for (i = 0; i < c_len; lst = SCM_CDR (lst), i++)
467 item = SCM_CAR (lst);
469 if (EXPECT_TRUE (SCM_I_INUMP (item)))
471 long c_item;
473 c_item = SCM_I_INUM (item);
474 if (EXPECT_TRUE ((c_item >= 0) && (c_item < 256)))
475 c_bv[i] = (unsigned char) c_item;
476 else
477 goto type_error;
479 else
480 goto type_error;
483 return (make_bytevector (c_len, (signed char *) c_bv));
485 type_error:
486 scm_gc_free (c_bv, c_len, SCM_GC_BYTEVECTOR);
487 scm_wrong_type_arg (FUNC_NAME, 1, item);
489 return SCM_BOOL_F;
491 #undef FUNC_NAME
493 /* Compute the two's complement of VALUE (a positive integer) on SIZE octets
494 using (2^(SIZE * 8) - VALUE). */
495 static inline void
496 twos_complement (mpz_t value, size_t size)
498 unsigned long bit_count;
500 /* We expect BIT_COUNT to fit in a unsigned long thanks to the range
501 checking on SIZE performed earlier. */
502 bit_count = (unsigned long) size << 3UL;
504 if (EXPECT_TRUE (bit_count < sizeof (unsigned long)))
505 mpz_ui_sub (value, 1UL << bit_count, value);
506 else
508 mpz_t max;
510 mpz_init (max);
511 mpz_ui_pow_ui (max, 2, bit_count);
512 mpz_sub (value, max, value);
513 mpz_clear (max);
517 static inline SCM
518 bytevector_large_ref (const char *c_bv, size_t c_size, int signed_p,
519 SCM endianness)
521 SCM result;
522 mpz_t c_mpz;
523 int c_endianness, negative_p = 0;
525 if (signed_p)
527 if (scm_is_eq (endianness, scm_sym_big))
528 negative_p = c_bv[0] & 0x80;
529 else
530 negative_p = c_bv[c_size - 1] & 0x80;
533 c_endianness = scm_is_eq (endianness, scm_sym_big) ? 1 : -1;
535 mpz_init (c_mpz);
536 mpz_import (c_mpz, 1 /* 1 word */, 1 /* word order doesn't matter */,
537 c_size /* word is C_SIZE-byte long */,
538 c_endianness,
539 0 /* nails */, c_bv);
541 if (signed_p && negative_p)
543 twos_complement (c_mpz, c_size);
544 mpz_neg (c_mpz, c_mpz);
547 result = scm_from_mpz (c_mpz);
548 mpz_clear (c_mpz); /* FIXME: Needed? */
550 return result;
553 static inline int
554 bytevector_large_set (char *c_bv, size_t c_size, int signed_p,
555 SCM value, SCM endianness)
557 size_t word_count, value_size;
558 mpz_t c_mpz;
559 int c_endianness, err = 0;
561 c_endianness = scm_is_eq (endianness, scm_sym_big) ? 1 : -1;
563 mpz_init (c_mpz);
564 scm_to_mpz (value, c_mpz);
565 if (mpz_sgn (c_mpz) < 0)
567 if (EXPECT_TRUE (signed_p))
569 mpz_neg (c_mpz, c_mpz);
570 twos_complement (c_mpz, c_size);
572 else
574 err = -1;
575 goto finish;
579 value_size = (mpz_sizeinbase (c_mpz, 2) + (8 * c_size)) / (8 * c_size);
580 if (EXPECT_FALSE (value_size > c_size))
582 err = -2;
583 goto finish;
586 mpz_export (c_bv, &word_count, 1 /* word order doesn't matter */,
587 c_size, c_endianness,
588 0 /* nails */, c_mpz);
589 if (EXPECT_FALSE (word_count != 1))
590 abort ();
592 finish:
593 mpz_clear (c_mpz);
595 return err;
598 #define GENERIC_INTEGER_ACCESSOR_PROLOGUE(_sign) \
599 unsigned c_len, c_index, c_size; \
600 char *c_bv; \
602 SCM_VALIDATE_R6RS_BYTEVECTOR (1, bv); \
603 c_index = scm_to_uint (index); \
604 c_size = scm_to_uint (size); \
606 c_len = SCM_R6RS_BYTEVECTOR_LENGTH (bv); \
607 c_bv = (char *) SCM_R6RS_BYTEVECTOR_CONTENTS (bv); \
609 /* C_SIZE must have its 3 higher bits set to zero so that \
610 multiplying it by 8 yields a number that fits in an \
611 unsigned long. */ \
612 if (EXPECT_FALSE ((c_size == 0) || (c_size >= (ULONG_MAX >> 3L)))) \
613 scm_out_of_range (FUNC_NAME, size); \
614 if (EXPECT_FALSE (c_index + c_size > c_len)) \
615 scm_out_of_range (FUNC_NAME, index);
618 #define BV_SIGNED_signed 1
619 #define BV_SIGNED_unsigned 0
621 /* Template of an integer reference function. */
622 #define GENERIC_INTEGER_REF(_sign) \
623 SCM result; \
625 if (c_size < 3) \
627 int swap; \
628 _sign int value; \
630 swap = !scm_is_eq (endianness, native_endianness); \
631 switch (c_size) \
633 case 1: \
634 value = *(_sign char *) c_bv; \
635 break; \
636 case 2: \
637 value = * (INT_TYPE (16, _sign) *) c_bv; \
638 if (swap) \
639 value = (INT_TYPE (16, _sign)) bswap_16 (value); \
640 break; \
641 default: \
642 abort (); \
645 result = SCM_I_MAKINUM ((_sign int) value); \
647 else \
648 result = bytevector_large_ref ((char *) c_bv, \
649 c_size, \
650 BV_SIGNED_ ## _sign, endianness); \
652 return result;
654 static inline SCM
655 bytevector_signed_ref (const char *c_bv, size_t c_size, SCM endianness)
657 GENERIC_INTEGER_REF (signed);
660 static inline SCM
661 bytevector_unsigned_ref (const char *c_bv, size_t c_size, SCM endianness)
663 GENERIC_INTEGER_REF (unsigned);
667 /* Template of an integer assignment function. */
668 #define GENERIC_INTEGER_SET(_sign) \
669 if (c_size < 3) \
671 _sign int c_value; \
673 if (EXPECT_FALSE (!SCM_I_INUMP (value))) \
674 goto range_error; \
676 c_value = SCM_I_INUM (value); \
677 switch (c_size) \
679 case 1: \
680 if (EXPECT_TRUE (INT_VALID_P (8, _sign) (c_value))) \
681 * (_sign char *) c_bv = (_sign char) c_value; \
682 else \
683 goto range_error; \
684 break; \
686 case 2: \
687 if (EXPECT_TRUE (INT_VALID_P (16, _sign) (c_value))) \
689 int swap; \
690 INT_TYPE (16, _sign) c_value16; \
692 swap = !scm_is_eq (endianness, native_endianness); \
694 c_value16 = \
695 swap ? bswap_16 (c_value) : c_value; \
696 * (INT_TYPE (16, _sign) *) c_bv = c_value16; \
698 else \
699 goto range_error; \
700 break; \
702 default: \
703 abort (); \
706 else \
708 int err; \
710 err = bytevector_large_set (c_bv, c_size, \
711 BV_SIGNED_ ## _sign, \
712 value, endianness); \
713 if (err) \
714 goto range_error; \
717 return; \
719 range_error: \
720 scm_out_of_range (FUNC_NAME, value); \
721 return;
723 static inline void
724 bytevector_signed_set (char *c_bv, size_t c_size,
725 SCM value, SCM endianness,
726 const char *func_name)
727 #define FUNC_NAME func_name
729 GENERIC_INTEGER_SET (signed);
731 #undef FUNC_NAME
733 static inline void
734 bytevector_unsigned_set (char *c_bv, size_t c_size,
735 SCM value, SCM endianness,
736 const char *func_name)
737 #define FUNC_NAME func_name
739 GENERIC_INTEGER_SET (unsigned);
741 #undef FUNC_NAME
743 #undef GENERIC_INTEGER_SET
744 #undef GENERIC_INTEGER_REF
745 #undef BV_SIGNED_unsigned
746 #undef BV_SIGNED_signed
749 SCM_DEFINE (scm_r6rs_bytevector_uint_ref, "bytevector-uint-ref", 4, 0, 0,
750 (SCM bv, SCM index, SCM endianness, SCM size),
751 "Return the @var{size}-octet long unsigned integer at index "
752 "@var{index} in @var{bv}.")
753 #define FUNC_NAME s_scm_r6rs_bytevector_uint_ref
755 GENERIC_INTEGER_ACCESSOR_PROLOGUE (unsigned);
757 return (bytevector_unsigned_ref (&c_bv[c_index], c_size, endianness));
759 #undef FUNC_NAME
761 SCM_DEFINE (scm_r6rs_bytevector_sint_ref, "bytevector-sint-ref", 4, 0, 0,
762 (SCM bv, SCM index, SCM endianness, SCM size),
763 "Return the @var{size}-octet long unsigned integer at index "
764 "@var{index} in @var{bv}.")
765 #define FUNC_NAME s_scm_r6rs_bytevector_sint_ref
767 GENERIC_INTEGER_ACCESSOR_PROLOGUE (signed);
769 return (bytevector_signed_ref (&c_bv[c_index], c_size, endianness));
771 #undef FUNC_NAME
773 SCM_DEFINE (scm_r6rs_bytevector_uint_set_x, "bytevector-uint-set!", 5, 0, 0,
774 (SCM bv, SCM index, SCM value, SCM endianness, SCM size),
775 "Set the @var{size}-octet long unsigned integer at @var{index} "
776 "to @var{value}.")
777 #define FUNC_NAME s_scm_r6rs_bytevector_uint_set_x
779 GENERIC_INTEGER_ACCESSOR_PROLOGUE (unsigned);
781 bytevector_unsigned_set (&c_bv[c_index], c_size, value, endianness,
782 FUNC_NAME);
784 return SCM_UNSPECIFIED;
786 #undef FUNC_NAME
788 SCM_DEFINE (scm_r6rs_bytevector_sint_set_x, "bytevector-sint-set!", 5, 0, 0,
789 (SCM bv, SCM index, SCM value, SCM endianness, SCM size),
790 "Set the @var{size}-octet long signed integer at @var{index} "
791 "to @var{value}.")
792 #define FUNC_NAME s_scm_r6rs_bytevector_sint_set_x
794 GENERIC_INTEGER_ACCESSOR_PROLOGUE (signed);
796 bytevector_signed_set (&c_bv[c_index], c_size, value, endianness,
797 FUNC_NAME);
799 return SCM_UNSPECIFIED;
801 #undef FUNC_NAME
805 /* Operations on integers of arbitrary size. */
807 #define INTEGERS_TO_LIST(_sign) \
808 SCM lst, pair; \
809 const char *c_bv; \
810 size_t i, c_len, c_size; \
812 SCM_VALIDATE_R6RS_BYTEVECTOR (1, bv); \
813 SCM_VALIDATE_SYMBOL (2, endianness); \
814 c_size = scm_to_uint (size); \
816 c_bv = (char *) SCM_R6RS_BYTEVECTOR_CONTENTS (bv); \
817 c_len = SCM_R6RS_BYTEVECTOR_LENGTH (bv); \
819 lst = scm_make_list (scm_from_uint (c_len / c_size), SCM_UNSPECIFIED); \
820 for (i = 0, pair = lst; \
821 i <= c_len - c_size; \
822 i += c_size, c_bv += c_size, pair = SCM_CDR (pair)) \
824 SCM_SETCAR (pair, \
825 bytevector_ ## _sign ## _ref (c_bv, c_size, \
826 endianness)); \
829 return lst;
831 SCM_DEFINE (scm_r6rs_bytevector_to_sint_list, "bytevector->sint-list",
832 3, 0, 0,
833 (SCM bv, SCM endianness, SCM size),
834 "Return a list of signed integers of @var{size} octets "
835 "representing the contents of @var{bv}.")
836 #define FUNC_NAME s_scm_r6rs_bytevector_to_sint_list
838 INTEGERS_TO_LIST (signed);
840 #undef FUNC_NAME
842 SCM_DEFINE (scm_r6rs_bytevector_to_uint_list, "bytevector->uint-list",
843 3, 0, 0,
844 (SCM bv, SCM endianness, SCM size),
845 "Return a list of unsigned integers of @var{size} octets "
846 "representing the contents of @var{bv}.")
847 #define FUNC_NAME s_scm_r6rs_bytevector_to_sint_list
849 INTEGERS_TO_LIST (unsigned);
851 #undef FUNC_NAME
853 #undef INTEGER_TO_LIST
856 #define INTEGER_LIST_TO_BYTEVECTOR(_sign) \
857 SCM result; \
858 size_t c_len, c_size; \
859 char *c_bv, *c_bv_ptr; \
861 SCM_VALIDATE_LIST_COPYLEN (1, lst, c_len); \
862 SCM_VALIDATE_SYMBOL (2, endianness); \
863 c_size = scm_to_uint (size); \
865 if (EXPECT_FALSE ((c_size == 0) || (c_size >= (ULONG_MAX >> 3L)))) \
866 scm_out_of_range (FUNC_NAME, size); \
868 c_bv = scm_gc_malloc (c_len * c_size, SCM_GC_BYTEVECTOR); \
870 /* FIXME: We leak C_BV here if one of the elements in LST is incorrect \
871 but `scm_dynwind_free ()' isn't appropriate. */ \
873 for (c_bv_ptr = c_bv; \
874 !scm_is_null (lst); \
875 lst = SCM_CDR (lst), c_bv_ptr += c_size) \
877 bytevector_ ## _sign ## _set (c_bv_ptr, c_size, \
878 SCM_CAR (lst), endianness, \
879 FUNC_NAME); \
882 result = make_bytevector (c_len * c_size, (signed char *) c_bv); \
884 return result;
887 SCM_DEFINE (scm_r6rs_uint_list_to_bytevector, "uint-list->bytevector",
888 3, 0, 0,
889 (SCM lst, SCM endianness, SCM size),
890 "Return a bytevector containing the unsigned integers "
891 "listed in @var{lst} and encoded on @var{size} octets "
892 "according to @var{endianness}.")
893 #define FUNC_NAME s_scm_r6rs_uint_list_to_bytevector
895 INTEGER_LIST_TO_BYTEVECTOR (unsigned);
897 #undef FUNC_NAME
899 SCM_DEFINE (scm_r6rs_sint_list_to_bytevector, "sint-list->bytevector",
900 3, 0, 0,
901 (SCM lst, SCM endianness, SCM size),
902 "Return a bytevector containing the signed integers "
903 "listed in @var{lst} and encoded on @var{size} octets "
904 "according to @var{endianness}.")
905 #define FUNC_NAME s_scm_r6rs_sint_list_to_bytevector
907 INTEGER_LIST_TO_BYTEVECTOR (signed);
909 #undef FUNC_NAME
911 #undef INTEGER_LIST_TO_BYTEVECTOR
915 /* Operations on 16-bit integers. */
917 SCM_DEFINE (scm_r6rs_bytevector_u16_ref, "bytevector-u16-ref",
918 3, 0, 0,
919 (SCM bv, SCM index, SCM endianness),
920 "Return the unsigned 16-bit integer from @var{bv} at "
921 "@var{index}.")
922 #define FUNC_NAME s_scm_r6rs_bytevector_u16_ref
924 INTEGER_REF (16, unsigned);
926 #undef FUNC_NAME
928 SCM_DEFINE (scm_r6rs_bytevector_s16_ref, "bytevector-s16-ref",
929 3, 0, 0,
930 (SCM bv, SCM index, SCM endianness),
931 "Return the signed 16-bit integer from @var{bv} at "
932 "@var{index}.")
933 #define FUNC_NAME s_scm_r6rs_bytevector_s16_ref
935 INTEGER_REF (16, signed);
937 #undef FUNC_NAME
939 SCM_DEFINE (scm_r6rs_bytevector_u16_native_ref, "bytevector-u16-native-ref",
940 2, 0, 0,
941 (SCM bv, SCM index),
942 "Return the unsigned 16-bit integer from @var{bv} at "
943 "@var{index} using the native endianness.")
944 #define FUNC_NAME s_scm_r6rs_bytevector_u16_native_ref
946 INTEGER_NATIVE_REF (16, unsigned);
948 #undef FUNC_NAME
950 SCM_DEFINE (scm_r6rs_bytevector_s16_native_ref, "bytevector-s16-native-ref",
951 2, 0, 0,
952 (SCM bv, SCM index),
953 "Return the unsigned 16-bit integer from @var{bv} at "
954 "@var{index} using the native endianness.")
955 #define FUNC_NAME s_scm_r6rs_bytevector_u16_native_ref
957 INTEGER_NATIVE_REF (16, signed);
959 #undef FUNC_NAME
961 SCM_DEFINE (scm_r6rs_bytevector_u16_set_x, "bytevector-u16-set!",
962 4, 0, 0,
963 (SCM bv, SCM index, SCM value, SCM endianness),
964 "Store @var{value} in @var{bv} at @var{index} according to "
965 "@var{endianness}.")
966 #define FUNC_NAME s_scm_r6rs_bytevector_u16_set_x
968 INTEGER_SET (16, unsigned);
970 #undef FUNC_NAME
972 SCM_DEFINE (scm_r6rs_bytevector_s16_set_x, "bytevector-s16-set!",
973 4, 0, 0,
974 (SCM bv, SCM index, SCM value, SCM endianness),
975 "Store @var{value} in @var{bv} at @var{index} according to "
976 "@var{endianness}.")
977 #define FUNC_NAME s_scm_r6rs_bytevector_s16_set_x
979 INTEGER_SET (16, signed);
981 #undef FUNC_NAME
983 SCM_DEFINE (scm_r6rs_bytevector_u16_native_set_x, "bytevector-u16-native-set!",
984 3, 0, 0,
985 (SCM bv, SCM index, SCM value),
986 "Store the unsigned integer @var{value} at index @var{index} "
987 "of @var{bv} using the native endianness.")
988 #define FUNC_NAME s_scm_r6rs_bytevector_u16_native_set_x
990 INTEGER_NATIVE_SET (16, unsigned);
992 #undef FUNC_NAME
994 SCM_DEFINE (scm_r6rs_bytevector_s16_native_set_x, "bytevector-s16-native-set!",
995 3, 0, 0,
996 (SCM bv, SCM index, SCM value),
997 "Store the signed integer @var{value} at index @var{index} "
998 "of @var{bv} using the native endianness.")
999 #define FUNC_NAME s_scm_r6rs_bytevector_u16_native_set_x
1001 INTEGER_NATIVE_SET (16, signed);
1003 #undef FUNC_NAME
1005 /* FIXME: Unfinished! */
1008 /* Initialization. */
1010 void
1011 scm_init_r6rs_bytevector (void)
1013 #include "bytevector.c.x"
1015 #ifdef WORDS_BIGENDIAN
1016 native_endianness = scm_sym_big;
1017 #else
1018 native_endianness = scm_sym_little;
1019 #endif
1022 /* arch-tag: e5a30664-90fc-40d7-8036-ee767aea5fb6