Bytevectors

Many applications must deal with blocks of binary data by accessing them in various ways—extracting signed or unsigned numbers of various sizes. Therefore, the (r6rs bytevector)library provides a single type for blocks of binary data with multiple ways to access that data. It deals only with integers in various sizes with specified endianness, because these are the most frequent applications.

Bytevectorsare objects of a disjoint type. Conceptually, a bytevector represents a sequence of 8-bit bytes. The description of bytevectors uses the term byte for an exact integer in the interval { - 128, ..., 127} and the term octet for an exact integer in the interval {0, ..., 255}. A byte corresponds to its two’s complement representation as an octet.

The length of a bytevector is the number of bytes it contains. This number is fixed. A valid index into a bytevector is an exact, non-negative integer. The first byte of a bytevector has index 0; the last byte has an index one less than the length of the bytevector.

Generally, the access procedures come in different flavors according to the size of the represented integer and the endianness of the representation. The procedures also distinguish signed and unsigned representations. The signed representations all use two’s complement.

Like list and vector literals, literals representing bytevectors must be quoted:

’#vu8(12 23 123)         ===⇒ #vu8(12 23 123)

2.1  General operations

syntax:  (endianness <endianness symbol>) 

<Endianness symbol> must be a symbol describing an endianness. An implementation must support at least the symbols big and little, but may support other endianness symbols. (endianness <endianness symbol>) evaluates to the symbol named <endianness symbol>. Whenever one of the procedures operating on bytevectors accepts an endianness as an argument, that argument must be one of these symbols. It is a syntax violation for <endianness symbol> to be anything other than an endianness symbol supported by the implementation.

Note:   Implementors are encouraged to use widely accepted designations for endianness symbols other than big and little.

procedure:  (native-endianness) 

Returns the endianness symbol associated implementation’s preferred endianness (usually that of the underlying machine architecture). This may be any <endianness symbol>, specifically a symbol other than big and little.

procedure:  (bytevector? obj) 

Returns #t if obj is a bytevector, otherwise returns #f.

procedure:  (make-bytevector k) 
procedure:  (make-bytevector k fill) 

Returns a newly allocated bytevector of k bytes.

If the fill argument is missing, the initial contents of the returned bytevector are unspecified.

If the fill argument is present, it must be an exact integer in the interval { - 128, ... 255} that specifies the initial value for the bytes of the bytevector: If fill is positive, it is interpreted as an octet; if it is negative, it is interpreted as a byte.

procedure:  (bytevector-length bytevector) 

Returns, as an exact integer, the number of bytes in bytevector.

procedure:  (bytevector=? bytevector1 bytevector2) 

Returns #t if bytevector1 and bytevector2 are equal—that is, if they have the same length and equal bytes at all valid indices. It returns #f otherwise.

:  (bytevector-fill! bytevector fill) 
The fill argument is as in the description of the make-bytevector procedure. Stores fill in every element of vector and returns the unspecified value. Analogous to vector-fill!.

procedure:  (bytevector-copy! source source-start 

target target-start k)

Source-start, target-start, and k must be non-negative exact integers that satisfy

[r6rs-lib-Z-G-1.gif]

where lsource is the length of source and ltarget is the length of target.

The bytevector-copy! procedure copies the bytes from source at indices

[r6rs-lib-Z-G-2.gif]

to consecutive indices in target starting at target-index.

This must work even if the memory regions for the source and the target overlap, i.e., the bytes at the target location after the copy must be equal to the bytes at the source location before the copy.

This returns the unspecified value.

(let ((b (u8-list->bytevector ’(1 2 3 4 5 6 7 8))))
  (bytevector-copy! b 0 b 3 4)
  (bytevector->u8-list b))         ===⇒ (1 2 3 1 2 3 4 8)

procedure:  (bytevector-copy bytevector) 

Returns a newly allocated copy of bytevector.

2.2  Operations on bytes and octets

procedure:  (bytevector-u8-ref bytevector k) 
procedure:  (bytevector-s8-ref bytevector k) 

K must be a valid index of bytevector.

The bytevector-u8-ref procedure returns the byte at index k of bytevector, as an octet.

The bytevector-s8-ref procedure returns the byte at index k of bytevector, as a (signed) byte.

(let ((b1 (make-bytevector 16 -127))
      (b2 (make-bytevector 16 255)))
  (list
    (bytevector-s8-ref b1 0)
    (bytevector-u8-ref b1 0)
    (bytevector-s8-ref b2 0)
    (bytevector-u8-ref b2 0)))         ===⇒ (-127 129 -1 255)

procedure:  (bytevector-u8-set! bytevector k octet) 
procedure:  (bytevector-s8-set! bytevector k byte) 

K must be a valid index of bytevector.

The bytevector-u8-set! procedure stores octet in element k of bytevector.

The bytevector-s8-set! procedure stores the two’s complement representation of byte in element k of bytevector.

Both procedures return the unspecified value.

(let ((b (make-bytevector 16 -127)))

  (bytevector-s8-set! b 0 -126)
  (bytevector-u8-set! b 1 246)

  (list
    (bytevector-s8-ref b 0)
    (bytevector-u8-ref b 0)
    (bytevector-s8-ref b 1)
    (bytevector-u8-ref b 1)))         ===⇒ (-126 130 -10 246)

procedure:  (bytevector->u8-list bytevector) 
procedure:  (u8-list->bytevector list) 

List must be a list of octets.

The bytevector->u8-list procedure returns a newly allocated list of the bytes of bytevector in the same order.

The u8-list->bytevector procedure returns a newly allocated bytevector whose elements are the elements of list list, in the same order. It is analogous to list->vector.

procedure:  (bytevector-uint-ref bytevector k endianness size) 
procedure:  (bytevector-sint-ref bytevector k endianness size) 
procedure:  (bytevector-uint-set! bytevector k n endianness size) 
procedure:  (bytevector-sint-set! bytevector k n endianness size) 

Size must be a positive exact integer. {k, ..., k + size - 1} must be valid indices of bytevector.

bytevector-uint-ref retrieves the exact integer corresponding to the unsigned representation of size size and specified by endianness at indices {k, ..., k + size - 1}.

bytevector-sint-ref retrieves the exact integer corresponding to the two’s complement representation of size size and specified by endianness at indices {k, ..., k + size - 1}.

For bytevector-uint-set!, n must be an exact integer in the set {0, ..., 256size - 1}.

bytevector-uint-set! stores the unsigned representation of size size and specified by endianness into bytevector at indices {k, ..., k + size - 1}.

For bytevector-sint-set!, n must be an exact integer in the interval { - 256size/2, ..., 256size/2 - 1}. bytevector-sint-set! stores the two’s complement representation of size size and specified by endianness into bytevector at indices {k, ..., k + size - 1}.

The ...-set! procedures return the unspecified value.

(define b (make-bytevector 16 -127))

(bytevector-uint-set! b 0 (- (expt 2 128) 3)
                     (endianness little) 16)

(bytevector-uint-ref b 0 (endianness little) 16)
                ===⇒
    #xfffffffffffffffffffffffffffffffd

(bytevector-sint-ref b 0 (endianness little) 16)
                ===⇒ -3

(bytevector->u8-list b)
                ===⇒ (253 255 255 255 255 255 255 255
               255 255 255 255 255 255 255 255)

(bytevector-uint-set! b 0 (- (expt 2 128) 3)
                 (endianness big) 16)

(bytevector-uint-ref b 0 (endianness big) 16) 
                ===⇒
    #xfffffffffffffffffffffffffffffffd

(bytevector-sint-ref b 0 (endianness big) 16) 
                ===⇒ -3

(bytevector->u8-list b) 
                ===⇒ (255 255 255 255 255 255 255 255
               255 255 255 255 255 255 255 253))

2.3  Operations on integers of arbitrary size

procedure:  (bytevector->uint-list bytevector endianness size) 
procedure:  (bytevector->sint-list bytevector endianness size) 
procedure:  (uint-list->bytevector list endianness size) 
procedure:  (sint-list->bytevector list endianness size) 

Size must be a positive exact integer.

These procedures convert between lists of integers and their consecutive representations according to size and endianness in the bytevector objects in the same way as bytevector->u8-list and u8-list->bytevector do for one-byte representations.

(let ((b (u8-list->bytevector ’(1 2 3 255 1 2 1 2))))
  (bytevector->sint-list b (endianness little) 2)) 
                ===⇒ (513 -253 513 513)

(let ((b (u8-list->bytevector ’(1 2 3 255 1 2 1 2))))
  (bytevector->uint-list b (endianness little) 2)) 
                ===⇒ (513 65283 513 513)

2.4  Operations on 16-bit integers

procedure:  (bytevector-u16-ref bytevector k endianness) 
procedure:  (bytevector-s16-ref bytevector k endianness) 
procedure:  (bytevector-u16-native-ref bytevector k) 
procedure:  (bytevector-s16-native-ref bytevector k) 
procedure:  (bytevector-u16-set! bytevector k n endianness) 
procedure:  (bytevector-s16-set! bytevector k n endianness) 
procedure:  (bytevector-u16-native-set! bytevector k n) 
procedure:  (bytevector-s16-native-set! bytevector k n) 

K must be a valid index of bytevector; so must k + 1.

These retrieve and set two-byte representations of numbers at indices k and k + 1, according to the endianness specified by endianness. The procedures with u16 in their names deal with the unsigned representation; those with s16 in their names deal with the two’s complement representation.

The procedures with native in their names employ the native endianness, and work only at aligned indices: k must be a multiple of 2.

The ...-set! procedures return the unspecified value.

(define b
  (u8-list->bytevector
    ’(255 255 255 255 255 255 255 255
      255 255 255 255 255 255 255 253)))

(bytevector-u16-ref b 14 (endianness little)) 
                ===⇒ 65023
(bytevector-s16-ref b 14 (endianness little)) 
                ===⇒ -513
(bytevector-u16-ref b 14 (endianness big)) 
                ===⇒ 65533
(bytevector-s16-ref b 14 (endianness big)) 
                ===⇒ -3

(bytevector-u16-set! b 0 12345 (endianness little))
(bytevector-u16-ref b 0 (endianness little)) 
                ===⇒ 12345

(bytevector-u16-native-set! b 0 12345)
(bytevector-u16-native-ref b 0)         ===⇒ 12345

(bytevector-u16-ref b 0 (endianness little)) 
                ===⇒ unspecified

2.5  Operations on 32-bit integers

procedure:  (bytevector-u32-ref bytevector k endianness) 
procedure:  (bytevector-s32-ref bytevector k endianness) 
procedure:  (bytevector-u32-native-ref bytevector k) 
procedure:  (bytevector-s32-native-ref bytevector k) 
procedure:  (bytevector-u32-set! bytevector k n endianness) 
procedure:  (bytevector-s32-set! bytevector k n endianness) 
procedure:  (bytevector-u32-native-set! bytevector k n) 
procedure:  (bytevector-s32-native-set! bytevector k n) 

{k, ..., k + 3} must be valid indices of bytevector..

These retrieve and set four-byte representations of numbers at indices {k, ..., k + 3}, according to the endianness specified by endianness. The procedures with u32 in their names deal with the unsigned representation, those with s32 with the two’s complement representation.

The procedures with native in their names employ the native endianness, and work only at aligned indices: k must be a multiple of 4.

The ...-set! procedures return the unspecified value.

(define b
  (u8-list->bytevector
    ’(255 255 255 255 255 255 255 255
      255 255 255 255 255 255 255 253)))

(bytevector-u32-ref b 12 (endianness little)) 
                ===⇒ 4261412863
(bytevector-s32-ref b 12 (endianness little)) 
                ===⇒ -33554433
(bytevector-u32-ref b 12 (endianness big)) 
                ===⇒ 4294967293
(bytevector-s32-ref b 12 (endianness big)) 
                ===⇒ -3

procedure:  (bytevector-u64-ref bytevector k endianness) 
procedure:  (bytevector-s64-ref bytevector k endianness) 
procedure:  (bytevector-u64-native-ref bytevector k) 
procedure:  (bytevector-s64-native-ref bytevector k) 
procedure:  (bytevector-u64-set! bytevector k n endianness) 
procedure:  (bytevector-s64-set! bytevector k n endianness) 
procedure:  (bytevector-u64-native-set! bytevector k n) 
procedure:  (bytevector-s64-native-set! bytevector k n) 

{k, ..., k + 7} must be valid indices of bytevector.

These retrieve and set eight-byte representations of numbers at indices {k, ..., k + 7}, according to the endianness specified by endianness. The procedures with u64 in their names deal with the unsigned representation, those with s64 with the two’s complement representation.

The procedures with native in their names employ the native endianness, and work only at aligned indices: k must be a multiple of 8.

The ...-set! procedures return the unspecified value.

(define b
  (u8-list->bytevector
    ’(255 255 255 255 255 255 255 255
      255 255 255 255 255 255 255 253)))

(bytevector-u64-ref b 8 (endianness little)) 
                ===⇒ 18302628885633695743
(bytevector-s64-ref b 8 (endianness little)) 
                ===⇒ -144115188075855873
(bytevector-u64-ref b 8 (endianness big)) 
                ===⇒ 18446744073709551613
(bytevector-s64-ref b 8 (endianness big)) 
                ===⇒ -3

procedure:  (bytevector-ieee-single-native-ref bytevector k) 
procedure:  (bytevector-ieee-single-ref bytevector k endianness) 

{k, ..., k + 3} must be valid indices of bytevector. For bytevector-ieee-single-native-ref, k must be a multiple of 4.

These procedures return the inexact real that best represents the IEEE-754 single precision number represented by the four bytes beginning at index k.

2.6  Operations on IEEE-754 numbers

procedure:  (bytevector-ieee-double-native-ref bytevector k) 
procedure:  (bytevector-ieee-double-ref bytevector k endianness) 

{k, ..., k + 7} must be valid indices of bytevector. For bytevector-ieee-double-native-ref, k must be a multiple of 8.

These procedures return the inexact real that best represents the IEEE-754 single precision number represented by the eight bytes beginning at index k.

procedure:  (bytevector-ieee-single-native-set! bytevector k x) 
procedure:  (bytevector-ieee-single-set! bytevector 

k x endianness)

{k, ..., k + 3} must be valid indices of bytevector. For bytevector-ieee-single-native-set!, k must be a multiple of 4. X must be a real number.

These procedures store an IEEE-754 single precision representation of x into elements k through k + 3 of bytevector, and returns the unspecified value.

procedure:  (bytevector-ieee-double-native-set! bytevector k x) 
procedure:  (bytevector-ieee-double-set! bytevector 

k x endianness)

{k, ..., k + 7} must be valid indices of bytevector. For bytevector-ieee-double-native-set!, k must be a multiple of 8.

These procedures store an IEEE-754 double precision representation of x into elements k through k + 7 of bytevector, and returns the unspecified value.